"""Builds the phone LOD of the grand tourer. Headless only: blender -b --python scripts/blender/make-lite-car.py -- \ src/assets/nightdrive-grand-tourer-chromatic.glb \ src/assets/nightdrive-grand-tourer-lite.glb On a phone the car is about 200 px wide and seen from behind, so the wheel machining, the tyre treads and the dense trim carry detail nobody can see. They are collapsed; the silhouette parts (body, haunches, glass) are kept as authored so the car reads the same. Node names and the hierarchy are preserved: car.ts finds the wheels, the light bar and the underglow by name. """ import sys import bpy source, target = sys.argv[sys.argv.index("--") + 1 :][:2] # Fraction of faces kept, by node name prefix (after the "evolution-" prefix). RATIOS = { "wheel-machining": 0.22, "tires": 0.25, "body-trim": 0.35, "rear-light-bar": 0.4, "car-underglow-source": 0.35, } bpy.ops.wm.read_factory_settings(use_empty=True) bpy.ops.import_scene.gltf(filepath=source) before = after = 0 for obj in bpy.data.objects: if obj.type != "MESH": continue name = obj.name.removeprefix("evolution-") faces = len(obj.data.polygons) before += faces ratio = next((r for prefix, r in RATIOS.items() if name.startswith(prefix)), None) if ratio is not None and faces > 200: # Meshes can be shared between wheels; give each object its own data. obj.data = obj.data.copy() modifier = obj.modifiers.new("lod", "DECIMATE") modifier.decimate_type = "COLLAPSE" modifier.ratio = ratio modifier.use_collapse_triangulate = True bpy.context.view_layer.objects.active = obj for other in bpy.context.view_layer.objects: other.select_set(other is obj) bpy.ops.object.modifier_apply(modifier=modifier.name) after += len(obj.data.polygons) bpy.ops.export_scene.gltf( filepath=target, export_format="GLB", export_apply=True, export_yup=True, export_texcoords=False, export_normals=True, export_materials="EXPORT", export_animations=False, ) print(f"LITE faces {before} -> {after}")