Spaces:
Running
Running
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)
|