74 lines
3.3 KiB
Python
74 lines
3.3 KiB
Python
"""Export the district kit to the runtime GLB (positions + indices only).
|
|
|
|
blender -b assets/blender/districts/nightdrive-districts.blend --python assets/blender/districts/export-districts.py [-- --preview out.png]
|
|
|
|
Normals are derived per fragment at runtime (flat shading from screen-space
|
|
derivatives), so the GLB carries no normals, UVs or materials and shared box
|
|
corners stay welded. Writes provenance next to this script.
|
|
"""
|
|
import bpy, json, hashlib, sys, math
|
|
from pathlib import Path
|
|
|
|
here = Path(__file__).resolve().parent
|
|
root = here.parents[2]
|
|
scene = bpy.data.scenes['Roadside Districts']
|
|
output = root / 'src/assets/nightdrive-districts.glb'
|
|
# Retired in round 6: the overhead gantry and the sign truss now own the road,
|
|
# and the billboards' light strips read as garbled text. They stay authored in
|
|
# the .blend but are no longer exported.
|
|
RETIRED = {'gantry', 'billboard'}
|
|
exported = [o for o in scene.objects if o.name.rsplit('_', 1)[0] not in RETIRED]
|
|
|
|
for obj in scene.objects:
|
|
obj.select_set(obj in exported)
|
|
bpy.ops.export_scene.gltf(
|
|
filepath=str(output), export_format='GLB', use_selection=True, use_active_scene=True,
|
|
export_apply=True, export_normals=False, export_texcoords=False, export_materials='NONE',
|
|
export_animations=False, export_cameras=False, export_lights=False, export_yup=True,
|
|
)
|
|
triangles = 0
|
|
for obj in exported:
|
|
obj.data.calc_loop_triangles()
|
|
triangles += len(obj.data.loop_triangles)
|
|
provenance = {
|
|
'asset': 'Nightdrive roadside districts',
|
|
'source': 'nightdrive-districts.blend',
|
|
'authorship': 'Original procedural modelling in Blender for this portfolio (author-districts.py); no external assets.',
|
|
'editable_scene': 'Roadside Districts',
|
|
'pieces': sorted({o.name.rsplit('_', 1)[0] for o in exported}),
|
|
'retired': sorted(RETIRED),
|
|
'triangles': triangles,
|
|
'attributes': 'POSITION + indices; flat normals derived at runtime',
|
|
'export': '../../../src/assets/nightdrive-districts.glb',
|
|
'sha256': hashlib.sha256(output.read_bytes()).hexdigest(),
|
|
}
|
|
(here / 'nightdrive-districts.asset.json').write_text(json.dumps(provenance, indent=2) + '\n')
|
|
print(json.dumps(provenance))
|
|
|
|
if '--preview' in sys.argv:
|
|
target = sys.argv[sys.argv.index('--preview') + 1]
|
|
names = provenance['pieces']
|
|
for i, name in enumerate(names):
|
|
for suffix in ('body', 'light'):
|
|
obj = scene.objects[f'{name}_{suffix}']
|
|
obj.location = ((i % 4) * 34 - 50, (i // 4) * 40, 0)
|
|
mat = bpy.data.materials.new(f'preview-{suffix}')
|
|
mat.diffuse_color = (0.05, 0.05, 0.08, 1) if suffix == 'body' else (0.1, 0.9, 1.0, 1)
|
|
obj.data.materials.append(mat)
|
|
cam = bpy.data.objects.new('preview-cam', bpy.data.cameras.new('preview-cam'))
|
|
scene.collection.objects.link(cam)
|
|
cam.location = (95, -95, 70)
|
|
cam.rotation_euler = (math.radians(58), 0, math.radians(45))
|
|
cam.data.lens = 30
|
|
scene.camera = cam
|
|
try:
|
|
scene.render.engine = 'BLENDER_WORKBENCH'
|
|
except TypeError as e:
|
|
print(e)
|
|
scene.display.shading.color_type = 'MATERIAL'
|
|
scene.display.shading.light = 'STUDIO'
|
|
scene.display.shading.show_cavity = True
|
|
scene.render.resolution_x, scene.render.resolution_y = 1600, 1000
|
|
scene.render.filepath = target
|
|
bpy.ops.render.render(write_still=True)
|