Spaces:
Running
Running
import streamlit as st | |
import os | |
import random | |
def scan_assets(): | |
"""Finds all textures, bump maps, and 3D models in the current folder.""" | |
img_exts = (".jpg", ".jpeg", ".png", ".gif") | |
files = [f for f in os.listdir() if os.path.isfile(f)] | |
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_maps = [ | |
f for f in files | |
if f.lower().endswith(img_exts) | |
and any(tag in f.lower() for tag in ("bump", "normal")) | |
] | |
models = [ | |
f for f in files | |
if f.lower().endswith((".glb", ".gltf", ".obj")) | |
] | |
return textures, bump_maps, models | |
def main(): | |
st.title("🔳 A-Frame Tilemap with Random Models") | |
grid_size = st.sidebar.slider("Grid Size", min_value=1, max_value=20, value=10) | |
textures, bump_maps, models = scan_assets() | |
if not textures or not models: | |
st.warning("⚠️ Please add at least one texture image and one .glb/.obj file to this folder.") | |
return | |
# --- Prepare <a-assets> tags --- | |
asset_tags = [] | |
for i, tex in enumerate(textures): | |
asset_tags.append(f'<img id="tex{i}" src="{tex}">') | |
if bump_maps: | |
# just pick the first bump map | |
asset_tags.append(f'<img id="grassBump" src="{bump_maps[0]}">') | |
for i, mdl in enumerate(models): | |
asset_tags.append(f'<a-asset-item id="model{i}" src="{mdl}"></a-asset-item>') | |
assets_html = "\n ".join(asset_tags) | |
# JavaScript arrays of IDs | |
texture_list = ", ".join(f'"#tex{i}"' for i in range(len(textures))) | |
model_list = ", ".join(f'"model{i}"' for i in range(len(models))) | |
has_bump_js = "true" if bump_maps else "false" | |
# Material setup for ground | |
if bump_maps: | |
ground_mat = ( | |
"ground.setAttribute('material'," | |
"'color: #228B22; bumpMap: #grassBump; bumpScale: 0.2');" | |
) | |
else: | |
ground_mat = "ground.setAttribute('material','color: #228B22');" | |
# --- The A-Frame HTML template --- | |
html_template = """ | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Tilemap Scene</title> | |
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script> | |
<script src="https://unpkg.com/[email protected]/dist/aframe-extras.loaders.min.js"></script> | |
</head> | |
<body> | |
<a-scene> | |
<a-assets> | |
{assets_html} | |
</a-assets> | |
<!-- Lighting --> | |
<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 for dynamic tiles & models --> | |
<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 = [{texture_list}]; | |
var models = [{model_list}]; | |
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 tex = textures[Math.floor(Math.random() * textures.length)]; | |
tile.setAttribute('material', 'src: ' + tex + '; repeat: 1 1'); | |
tile.setAttribute('position', x + ' 0 ' + z); | |
tilemap.appendChild(tile); | |
// --- Random model --- | |
var mi = Math.floor(Math.random() * models.length); | |
var mid = models[mi]; | |
var modelEl = document.createElement('a-entity'); | |
// load both .glb/.gltf and .obj via gltf-model (requires extras loader) | |
modelEl.setAttribute('gltf-model', '#' + mid); | |
modelEl.setAttribute('scale', '0.5 0.5 0.5'); | |
modelEl.setAttribute('position', x + ' 0.5 ' + z); | |
tilemap.appendChild(modelEl); | |
}} | |
}} | |
// --- 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> | |
""" | |
html = html_template.format( | |
assets_html=assets_html, | |
texture_list=texture_list, | |
model_list=model_list, | |
grid_size=grid_size, | |
ground_mat=ground_mat | |
) | |
st.components.v1.html(html, height=600, scrolling=False) | |
if __name__ == "__main__": | |
main() | |