Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,106 +4,140 @@ import random
|
|
| 4 |
|
| 5 |
@st.cache_data
|
| 6 |
def scan_assets():
|
| 7 |
-
"""
|
| 8 |
-
img_exts = (".jpg", ".jpeg", ".png", ".gif")
|
| 9 |
files = [f for f in os.listdir() if os.path.isfile(f)]
|
|
|
|
|
|
|
|
|
|
| 10 |
textures = [
|
| 11 |
f for f in files
|
| 12 |
if f.lower().endswith(img_exts)
|
| 13 |
and not any(tag in f.lower() for tag in ("bump", "normal"))
|
| 14 |
]
|
|
|
|
|
|
|
| 15 |
bump_maps = [
|
| 16 |
f for f in files
|
| 17 |
if f.lower().endswith(img_exts)
|
| 18 |
and any(tag in f.lower() for tag in ("bump", "normal"))
|
| 19 |
]
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
def main():
|
| 27 |
-
st.title("🔳 A-Frame Tilemap with
|
| 28 |
-
grid_size = st.sidebar.slider("Grid Size",
|
| 29 |
-
textures, bump_maps, model_files = scan_assets()
|
| 30 |
|
| 31 |
-
|
| 32 |
-
|
|
|
|
| 33 |
return
|
| 34 |
|
| 35 |
-
#
|
| 36 |
asset_tags = []
|
| 37 |
for i, tex in enumerate(textures):
|
| 38 |
asset_tags.append(f'<img id="tex{i}" src="{tex}">')
|
|
|
|
| 39 |
if bump_maps:
|
| 40 |
-
asset_tags.append(f'<img id="
|
| 41 |
-
|
| 42 |
-
for
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
| 47 |
|
| 48 |
assets_html = "\n ".join(asset_tags)
|
| 49 |
-
texture_list = ", ".join(f'"#tex{i}"' for i in range(len(textures)))
|
| 50 |
-
# JS array of model objects
|
| 51 |
-
model_js_array = ", ".join(
|
| 52 |
-
f'{{id:"{m["id"]}", ext:"{m["ext"]}"}}' for m in models_info
|
| 53 |
-
)
|
| 54 |
|
| 55 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
if bump_maps:
|
| 57 |
-
ground_mat = (
|
| 58 |
-
"ground.setAttribute('material',"
|
| 59 |
-
"'color: #228B22; bumpMap: #grassBump; bumpScale: 0.2');"
|
| 60 |
-
)
|
| 61 |
else:
|
| 62 |
-
ground_mat = "ground.setAttribute('material','color
|
| 63 |
|
|
|
|
| 64 |
html = f"""
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
<
|
| 74 |
-
|
| 75 |
-
|
|
|
|
|
|
|
| 76 |
{assets_html}
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
camera
|
| 93 |
-
look-controls
|
| 94 |
-
position="0 {grid_size} {grid_size}">
|
| 95 |
-
</a-entity>
|
| 96 |
-
|
| 97 |
-
<!-- Container for tiles & models -->
|
| 98 |
-
<a-entity id="tilemap"></a-entity>
|
| 99 |
-
</a-scene>
|
| 100 |
-
|
| 101 |
-
<script>
|
| 102 |
document.addEventListener('DOMContentLoaded', function() {{
|
| 103 |
-
var scene
|
| 104 |
-
var tilemap
|
| 105 |
-
var textures = [{
|
| 106 |
-
var models = [{
|
| 107 |
var grid = {grid_size};
|
| 108 |
|
| 109 |
for (var i = 0; i < grid; i++) {{
|
|
@@ -116,18 +150,20 @@ def main():
|
|
| 116 |
tile.setAttribute('width', 1);
|
| 117 |
tile.setAttribute('height', 0.1);
|
| 118 |
tile.setAttribute('depth', 1);
|
| 119 |
-
var
|
| 120 |
-
tile.setAttribute('material', 'src:
|
| 121 |
tile.setAttribute('position', x + ' 0 ' + z);
|
| 122 |
tilemap.appendChild(tile);
|
| 123 |
|
| 124 |
// Random model
|
| 125 |
var m = models[Math.floor(Math.random() * models.length)];
|
| 126 |
var ent = document.createElement('a-entity');
|
| 127 |
-
if (m.
|
| 128 |
-
ent.setAttribute('obj-model', 'obj: ' + m.id);
|
| 129 |
-
}} else {{
|
| 130 |
ent.setAttribute('gltf-model', m.id);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
}}
|
| 132 |
ent.setAttribute('scale', '0.5 0.5 0.5');
|
| 133 |
ent.setAttribute('position', x + ' 0.5 ' + z);
|
|
@@ -144,9 +180,9 @@ def main():
|
|
| 144 |
ground.setAttribute('position', '0 -0.05 0');
|
| 145 |
scene.insertBefore(ground, scene.firstChild);
|
| 146 |
}});
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
"""
|
| 151 |
|
| 152 |
st.components.v1.html(html, height=600, scrolling=False)
|
|
|
|
| 4 |
|
| 5 |
@st.cache_data
|
| 6 |
def scan_assets():
|
| 7 |
+
"""Discover textures, bump maps, glTF models, and OBJ(+MTL) pairs."""
|
|
|
|
| 8 |
files = [f for f in os.listdir() if os.path.isfile(f)]
|
| 9 |
+
img_exts = (".jpg", ".jpeg", ".png", ".gif")
|
| 10 |
+
|
| 11 |
+
# Textures (exclude bump/normal)
|
| 12 |
textures = [
|
| 13 |
f for f in files
|
| 14 |
if f.lower().endswith(img_exts)
|
| 15 |
and not any(tag in f.lower() for tag in ("bump", "normal"))
|
| 16 |
]
|
| 17 |
+
|
| 18 |
+
# Bump/NORMAL map (just take the first one, if any)
|
| 19 |
bump_maps = [
|
| 20 |
f for f in files
|
| 21 |
if f.lower().endswith(img_exts)
|
| 22 |
and any(tag in f.lower() for tag in ("bump", "normal"))
|
| 23 |
]
|
| 24 |
+
|
| 25 |
+
# glTF models
|
| 26 |
+
gltf_models = [f for f in files if f.lower().endswith((".glb", ".gltf"))]
|
| 27 |
+
|
| 28 |
+
# OBJ models + their MTL partners
|
| 29 |
+
obj_models = [f for f in files if f.lower().endswith(".obj")]
|
| 30 |
+
mtl_files = {os.path.splitext(f)[0]: f for f in files if f.lower().endswith(".mtl")}
|
| 31 |
+
|
| 32 |
+
models = []
|
| 33 |
+
idx = 0
|
| 34 |
+
|
| 35 |
+
# Register glTF entries
|
| 36 |
+
for gltf in gltf_models:
|
| 37 |
+
models.append({
|
| 38 |
+
"type": "gltf",
|
| 39 |
+
"asset_id": f"model{idx}",
|
| 40 |
+
"src": gltf
|
| 41 |
+
})
|
| 42 |
+
idx += 1
|
| 43 |
+
|
| 44 |
+
# Register OBJ entries (with optional MTL)
|
| 45 |
+
for obj in obj_models:
|
| 46 |
+
base = os.path.splitext(obj)[0]
|
| 47 |
+
mtl = mtl_files.get(base)
|
| 48 |
+
entry = {
|
| 49 |
+
"type": "obj",
|
| 50 |
+
"obj_id": f"model{idx}-obj",
|
| 51 |
+
"obj": obj,
|
| 52 |
+
"mtl_id": f"model{idx}-mtl" if mtl else None,
|
| 53 |
+
"mtl": mtl
|
| 54 |
+
}
|
| 55 |
+
models.append(entry)
|
| 56 |
+
idx += 1
|
| 57 |
+
|
| 58 |
+
return textures, bump_maps, models
|
| 59 |
|
| 60 |
def main():
|
| 61 |
+
st.title("🔳 A-Frame Tilemap with Random 3D Models")
|
| 62 |
+
grid_size = st.sidebar.slider("Grid Size", 1, 20, 10)
|
|
|
|
| 63 |
|
| 64 |
+
textures, bump_maps, models = scan_assets()
|
| 65 |
+
if not textures or not models:
|
| 66 |
+
st.warning("⚠️ Drop at least one image (jpg/png) **and** one glb/obj model (with optional mtl) into this folder.")
|
| 67 |
return
|
| 68 |
|
| 69 |
+
# Build <a-assets>
|
| 70 |
asset_tags = []
|
| 71 |
for i, tex in enumerate(textures):
|
| 72 |
asset_tags.append(f'<img id="tex{i}" src="{tex}">')
|
| 73 |
+
|
| 74 |
if bump_maps:
|
| 75 |
+
asset_tags.append(f'<img id="bump0" src="{bump_maps[0]}">')
|
| 76 |
+
|
| 77 |
+
for m in models:
|
| 78 |
+
if m["type"] == "gltf":
|
| 79 |
+
asset_tags.append(f'<a-asset-item id="{m["asset_id"]}" src="{m["src"]}"></a-asset-item>')
|
| 80 |
+
else:
|
| 81 |
+
asset_tags.append(f'<a-asset-item id="{m["obj_id"]}" src="{m["obj"]}"></a-asset-item>')
|
| 82 |
+
if m["mtl_id"]:
|
| 83 |
+
asset_tags.append(f'<a-asset-item id="{m["mtl_id"]}" src="{m["mtl"]}"></a-asset-item>')
|
| 84 |
|
| 85 |
assets_html = "\n ".join(asset_tags)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
# JS arrays
|
| 88 |
+
tex_js = ", ".join(f'"#tex{i}"' for i in range(len(textures)))
|
| 89 |
+
has_bump = "true" if bump_maps else "false"
|
| 90 |
+
models_js = []
|
| 91 |
+
for m in models:
|
| 92 |
+
if m["type"] == "gltf":
|
| 93 |
+
models_js.append(f'{{type:"gltf", id:"#%s"}}' % m["asset_id"])
|
| 94 |
+
else:
|
| 95 |
+
mtl_part = f', mtl:"#%s"' % m["mtl_id"] if m["mtl_id"] else ""
|
| 96 |
+
models_js.append(f'{{type:"obj", obj:"#%s"{mtl}}}' % (m["obj_id"], mtl=mtl_part))
|
| 97 |
+
models_js = ", ".join(models_js)
|
| 98 |
+
|
| 99 |
+
# Ground material
|
| 100 |
if bump_maps:
|
| 101 |
+
ground_mat = "ground.setAttribute('material','color:#228B22; bumpMap:#bump0; bumpScale:0.2');"
|
|
|
|
|
|
|
|
|
|
| 102 |
else:
|
| 103 |
+
ground_mat = "ground.setAttribute('material','color:#228B22');"
|
| 104 |
|
| 105 |
+
# Final HTML
|
| 106 |
html = f"""
|
| 107 |
+
<!DOCTYPE html>
|
| 108 |
+
<html>
|
| 109 |
+
<head>
|
| 110 |
+
<meta charset="utf-8">
|
| 111 |
+
<title>Tilemap Scene</title>
|
| 112 |
+
<!-- Core A-Frame -->
|
| 113 |
+
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
|
| 114 |
+
<!-- Loaders for OBJ & glTF -->
|
| 115 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-extras.loaders.min.js"></script>
|
| 116 |
+
</head>
|
| 117 |
+
<body>
|
| 118 |
+
<a-scene>
|
| 119 |
+
<a-assets>
|
| 120 |
{assets_html}
|
| 121 |
+
</a-assets>
|
| 122 |
+
|
| 123 |
+
<!-- Lights -->
|
| 124 |
+
<a-entity light="type: ambient; color: #BBB"></a-entity>
|
| 125 |
+
<a-entity light="type: directional; color: #FFF; intensity: 0.6" position="1 1 0"></a-entity>
|
| 126 |
+
<a-entity light="type: point; intensity: 0.6" position="0 5 0"></a-entity>
|
| 127 |
+
|
| 128 |
+
<!-- Camera -->
|
| 129 |
+
<a-entity camera look-controls position="0 {grid_size} {grid_size}"></a-entity>
|
| 130 |
+
|
| 131 |
+
<!-- Container -->
|
| 132 |
+
<a-entity id="tilemap"></a-entity>
|
| 133 |
+
</a-scene>
|
| 134 |
+
|
| 135 |
+
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
document.addEventListener('DOMContentLoaded', function() {{
|
| 137 |
+
var scene = document.querySelector('a-scene');
|
| 138 |
+
var tilemap = document.querySelector('#tilemap');
|
| 139 |
+
var textures = [{tex_js}];
|
| 140 |
+
var models = [{models_js}];
|
| 141 |
var grid = {grid_size};
|
| 142 |
|
| 143 |
for (var i = 0; i < grid; i++) {{
|
|
|
|
| 150 |
tile.setAttribute('width', 1);
|
| 151 |
tile.setAttribute('height', 0.1);
|
| 152 |
tile.setAttribute('depth', 1);
|
| 153 |
+
var tidx = Math.floor(Math.random() * textures.length);
|
| 154 |
+
tile.setAttribute('material', 'src:' + textures[tidx] + '; repeat:1 1');
|
| 155 |
tile.setAttribute('position', x + ' 0 ' + z);
|
| 156 |
tilemap.appendChild(tile);
|
| 157 |
|
| 158 |
// Random model
|
| 159 |
var m = models[Math.floor(Math.random() * models.length)];
|
| 160 |
var ent = document.createElement('a-entity');
|
| 161 |
+
if (m.type === 'gltf') {{
|
|
|
|
|
|
|
| 162 |
ent.setAttribute('gltf-model', m.id);
|
| 163 |
+
}} else {{
|
| 164 |
+
var cmd = 'obj: ' + m.obj;
|
| 165 |
+
if (m.mtl) cmd += '; mtl: ' + m.mtl;
|
| 166 |
+
ent.setAttribute('obj-model', cmd);
|
| 167 |
}}
|
| 168 |
ent.setAttribute('scale', '0.5 0.5 0.5');
|
| 169 |
ent.setAttribute('position', x + ' 0.5 ' + z);
|
|
|
|
| 180 |
ground.setAttribute('position', '0 -0.05 0');
|
| 181 |
scene.insertBefore(ground, scene.firstChild);
|
| 182 |
}});
|
| 183 |
+
</script>
|
| 184 |
+
</body>
|
| 185 |
+
</html>
|
| 186 |
"""
|
| 187 |
|
| 188 |
st.components.v1.html(html, height=600, scrolling=False)
|