Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import random | |
| def scan_assets(): | |
| """Discover textures, bump maps, glTF models, and OBJ(+MTL) pairs.""" | |
| files = [f for f in os.listdir() if os.path.isfile(f)] | |
| img_exts = (".jpg", ".jpeg", ".png", ".gif") | |
| # Textures (exclude bump/normal) | |
| textures = [ | |
| f for f in files | |
| if f.lower().endswith(img_exts) | |
| and not any(tag in f.lower() for tag in ("bump", "normal")) | |
| ] | |
| # Bump/NORMAL map (just take the first one, if any) | |
| bump_maps = [ | |
| f for f in files | |
| if f.lower().endswith(img_exts) | |
| and any(tag in f.lower() for tag in ("bump", "normal")) | |
| ] | |
| # glTF models | |
| gltf_models = [f for f in files if f.lower().endswith((".glb", ".gltf"))] | |
| # OBJ models + their MTL partners | |
| obj_models = [f for f in files if f.lower().endswith(".obj")] | |
| mtl_files = {os.path.splitext(f)[0]: f for f in files if f.lower().endswith(".mtl")} | |
| models = [] | |
| idx = 0 | |
| # Register glTF entries | |
| for gltf in gltf_models: | |
| models.append({ | |
| "type": "gltf", | |
| "asset_id": f"model{idx}", | |
| "src": gltf | |
| }) | |
| idx += 1 | |
| # Register OBJ entries (with optional MTL) | |
| for obj in obj_models: | |
| base = os.path.splitext(obj)[0] | |
| mtl = mtl_files.get(base) | |
| entry = { | |
| "type": "obj", | |
| "obj_id": f"model{idx}-obj", | |
| "obj": obj, | |
| "mtl_id": f"model{idx}-mtl" if mtl else None, | |
| "mtl": mtl | |
| } | |
| models.append(entry) | |
| idx += 1 | |
| return textures, bump_maps, models | |
| def main(): | |
| st.title("🔳 A-Frame Tilemap with Random 3D Models") | |
| grid_size = st.sidebar.slider("Grid Size", 1, 20, 10) | |
| textures, bump_maps, models = scan_assets() | |
| if not textures or not models: | |
| st.warning("⚠️ Drop at least one image (jpg/png) **and** one glb/obj model (with optional mtl) into this folder.") | |
| return | |
| # Build <a-assets> | |
| asset_tags = [] | |
| for i, tex in enumerate(textures): | |
| asset_tags.append(f'<img id="tex{i}" src="{tex}">') | |
| if bump_maps: | |
| asset_tags.append(f'<img id="bump0" src="{bump_maps[0]}">') | |
| for m in models: | |
| if m["type"] == "gltf": | |
| asset_tags.append(f'<a-asset-item id="{m["asset_id"]}" src="{m["src"]}"></a-asset-item>') | |
| else: | |
| asset_tags.append(f'<a-asset-item id="{m["obj_id"]}" src="{m["obj"]}"></a-asset-item>') | |
| if m["mtl_id"]: | |
| asset_tags.append(f'<a-asset-item id="{m["mtl_id"]}" src="{m["mtl"]}"></a-asset-item>') | |
| assets_html = "\n ".join(asset_tags) | |
| # JS arrays | |
| tex_js = ", ".join(f'"#tex{i}"' for i in range(len(textures))) | |
| has_bump = "true" if bump_maps else "false" | |
| models_js = [] | |
| for m in models: | |
| if m["type"] == "gltf": | |
| models_js.append(f'{{type:"gltf", id:"#%s"}}' % m["asset_id"]) | |
| else: | |
| mtl_part = f', mtl:"#%s"' % m["mtl_id"] if m["mtl_id"] else "" | |
| models_js.append(f'{{type:"obj", obj:"#%s"{mtl}}}' % (m["obj_id"], mtl=mtl_part)) | |
| models_js = ", ".join(models_js) | |
| # Ground material | |
| if bump_maps: | |
| ground_mat = "ground.setAttribute('material','color:#228B22; bumpMap:#bump0; bumpScale:0.2');" | |
| else: | |
| ground_mat = "ground.setAttribute('material','color:#228B22');" | |
| # Final HTML | |
| html = f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Tilemap Scene</title> | |
| <!-- Core A-Frame --> | |
| <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script> | |
| <!-- Loaders for OBJ & glTF --> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-extras.loaders.min.js"></script> | |
| </head> | |
| <body> | |
| <a-scene> | |
| <a-assets> | |
| {assets_html} | |
| </a-assets> | |
| <!-- Lights --> | |
| <a-entity light="type: ambient; color: #BBB"></a-entity> | |
| <a-entity light="type: directional; color: #FFF; intensity: 0.6" position="1 1 0"></a-entity> | |
| <a-entity light="type: point; intensity: 0.6" position="0 5 0"></a-entity> | |
| <!-- Camera --> | |
| <a-entity camera look-controls position="0 {grid_size} {grid_size}"></a-entity> | |
| <!-- Container --> | |
| <a-entity id="tilemap"></a-entity> | |
| </a-scene> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() {{ | |
| var scene = document.querySelector('a-scene'); | |
| var tilemap = document.querySelector('#tilemap'); | |
| var textures = [{tex_js}]; | |
| var models = [{models_js}]; | |
| var grid = {grid_size}; | |
| for (var i = 0; i < grid; i++) {{ | |
| for (var j = 0; j < grid; j++) {{ | |
| var x = i - grid/2; | |
| var z = j - grid/2; | |
| // Base tile | |
| var tile = document.createElement('a-box'); | |
| tile.setAttribute('width', 1); | |
| tile.setAttribute('height', 0.1); | |
| tile.setAttribute('depth', 1); | |
| var tidx = Math.floor(Math.random() * textures.length); | |
| tile.setAttribute('material', 'src:' + textures[tidx] + '; repeat:1 1'); | |
| tile.setAttribute('position', x + ' 0 ' + z); | |
| tilemap.appendChild(tile); | |
| // Random model | |
| var m = models[Math.floor(Math.random() * models.length)]; | |
| var ent = document.createElement('a-entity'); | |
| if (m.type === 'gltf') {{ | |
| ent.setAttribute('gltf-model', m.id); | |
| }} else {{ | |
| var cmd = 'obj: ' + m.obj; | |
| if (m.mtl) cmd += '; mtl: ' + m.mtl; | |
| ent.setAttribute('obj-model', cmd); | |
| }} | |
| ent.setAttribute('scale', '0.5 0.5 0.5'); | |
| ent.setAttribute('position', x + ' 0.5 ' + z); | |
| tilemap.appendChild(ent); | |
| }} | |
| }} | |
| // Ground plane | |
| var ground = document.createElement('a-plane'); | |
| ground.setAttribute('width', grid * 2); | |
| ground.setAttribute('height', grid * 2); | |
| ground.setAttribute('rotation', '-90 0 0'); | |
| {ground_mat} | |
| ground.setAttribute('position', '0 -0.05 0'); | |
| scene.insertBefore(ground, scene.firstChild); | |
| }}); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| st.components.v1.html(html, height=600, scrolling=False) | |
| if __name__ == "__main__": | |
| main() | |