Files
Portfolio/assets/blender/chromatic/export-chromatic.py
LucaP 5d5935a1f8 feat: immerse the portfolio in a chromatic synthwave night
Carry rose, violet, coral, electric blue and cyan through the interface, road, terrain, signs and atmosphere. Add a Blender-authored paint edition and matching runtime fallback captures. Interpolate atmospheric color on a small sky lattice and enrich existing speed effects without another rendering pass.

Preserve approved copy, project media, metadata, navigation contracts and adaptive audio. Verified with typecheck, client/SSR builds, validated prerendering, artifact and transfer checks, responsive Chrome interaction, fallback checks and Intel-backed performance sampling.
2026-09-05 13:17:53 +03:00

52 lines
3.5 KiB
Python

"""Export isolated authored source into material batches; leave lookdev editable."""
import bpy, json, hashlib
from mathutils import Matrix, Vector
from pathlib import Path
root_path=Path(__file__).resolve().parents[3]
scene=bpy.data.scenes['Chromatic Nightdrive']
bpy.context.window.scene=scene
export_scene=bpy.data.scenes.new('Chromatic Export')
root=bpy.data.objects.new('evolution-chase-car',None); export_scene.collection.objects.link(root)
names={'M_Body_Gunmetal':'car-body','M_Body_Haunch':'body-haunches','M_Glass_Smoke':'rear-glass','M_Rear_Light':'rear-light-bar','M_Cyan_Accent':'car-underglow-source','M_Lower_Graphite':'body-trim','M_Wheel':'wheel-machining','M_Tire':'tires'}
depsgraph=bpy.context.evaluated_depsgraph_get(); groups={}
for obj in scene.objects:
if obj.type not in ['MESH','CURVE'] or obj.name.startswith('LOOKDEV'): continue
evaluated=obj.evaluated_get(depsgraph)
mesh=bpy.data.meshes.new_from_object(evaluated,depsgraph=depsgraph); mesh.transform(obj.matrix_world)
clone=bpy.data.objects.new('export-'+obj.name,mesh); export_scene.collection.objects.link(clone)
key=obj.data.materials[0].name.split('.')[0]
wheel=None
for axle in ['front','rear']:
for side in ['left','right']:
if any(token in obj.name for token in [f'{axle}-wheel-{side}', f'{axle}-rim-{side}', f'{axle}-rim-ring-{side}', f'wheel-{axle}-{side}']):
wheel=f'wheel-{axle}-{side}'
groups.setdefault((key,wheel),[]).append(clone)
bpy.context.window.scene=export_scene
wheel_roots={}
for (key,wheel),objects in groups.items():
bpy.ops.object.select_all(action='DESELECT')
for obj in objects: obj.select_set(True)
bpy.context.view_layer.objects.active=objects[0]
if len(objects)>1: bpy.ops.object.join()
joined=bpy.context.object; joined.name='evolution-'+names[key]
if wheel:
if wheel not in wheel_roots:
axle='front' if 'front' in wheel else 'rear'; side=-1 if 'left' in wheel else 1
wheel_root=bpy.data.objects.new(wheel,None); export_scene.collection.objects.link(wheel_root)
wheel_root.parent=root; wheel_root.location=(side*1.01,-1.62 if axle=='front' else 1.62,.42 if axle=='front' else .46)
wheel_roots[wheel]=wheel_root
wheel_root=wheel_roots[wheel]
joined.data.transform(Matrix.Translation(-wheel_root.location))
joined.parent=wheel_root
else: joined.parent=root
bpy.ops.object.select_all(action='SELECT')
output=root_path/'src/assets/nightdrive-grand-tourer-chromatic.glb'
bpy.ops.export_scene.gltf(filepath=str(output),export_format='GLB',use_selection=True,use_active_scene=True,export_apply=True,export_animations=False,export_cameras=False,export_lights=False)
triangles=0
for obj in export_scene.objects:
if obj.type=='MESH': obj.data.calc_loop_triangles(); triangles+=len(obj.data.loop_triangles)
provenance={'asset':'Nightdrive chromatic grand tourer','source':'nightdrive-chromatic.blend','base':'../expressway/nightdrive-expressway.blend at 43e5103; original evolution at c9ac36e','authorship':'Original procedural modelling in Blender for this portfolio; no external assets or generated project evidence.','editable_scene':'Chromatic Nightdrive','material_groups':len(groups),'independent_wheels':4,'triangles':triangles,'export':'../../../src/assets/nightdrive-grand-tourer-chromatic.glb','sha256':hashlib.sha256(output.read_bytes()).hexdigest()}
(root_path/'assets/blender/chromatic/nightdrive-chromatic.asset.json').write_text(json.dumps(provenance,indent=2)+'\n')
bpy.context.window.scene=scene
print(json.dumps(provenance))