"""Slim TRELLIS GLBs to prop grade, in place. /Applications/Blender.app/Contents/MacOS/Blender -b -P tools/slim_glb.py -- [max_tris] [tex_px] TRELLIS ships ~200k-face meshes with PNG textures -- 20MB+ per prop, which is lovely and also insane for background clutter. Per GLB: decimate every mesh down to max_tris (default 18k), scale every image to tex_px (default 1024), re-export with JPEG textures. Typical result is a 10x size drop with no visible cost at prop distance. Prints "SLIM name before->after KB" per file. """ import os import sys import bpy argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] DIR = argv[0] if argv else "assets/tools" MAX_TRIS = int(argv[1]) if len(argv) > 1 else 18000 TEX = int(argv[2]) if len(argv) > 2 else 1024 def reset(): bpy.ops.wm.read_factory_settings(use_empty=True) def tri_count(obj): m = obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh() n = len(m.loop_triangles) obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh_clear() return n def slim(path): reset() before = os.path.getsize(path) // 1024 bpy.ops.import_scene.gltf(filepath=path) meshes = [o for o in bpy.data.objects if o.type == "MESH"] total = 0 for o in meshes: o.data.calc_loop_triangles() total += len(o.data.loop_triangles) if total > MAX_TRIS: ratio = MAX_TRIS / float(total) for o in meshes: bpy.context.view_layer.objects.active = o mod = o.modifiers.new("slim", "DECIMATE") mod.ratio = ratio bpy.ops.object.modifier_apply(modifier=mod.name) for img in bpy.data.images: if img.size[0] > TEX or img.size[1] > TEX: img.scale(min(img.size[0], TEX), min(img.size[1], TEX)) kwargs = { "filepath": path, "export_format": "GLB", "export_image_format": "JPEG", "export_yup": True, } # quality kwarg name moved between Blender versions; feed whichever exists props = bpy.ops.export_scene.gltf.get_rna_type().properties.keys() for k in ("export_jpeg_quality", "export_image_quality"): if k in props: kwargs[k] = 80 bpy.ops.export_scene.gltf(**kwargs) print("SLIM %s %d->%dKB (%d tris)" % ( os.path.basename(path), before, os.path.getsize(path) // 1024, min(total, MAX_TRIS)), flush=True) for f in sorted(os.listdir(DIR)): if f.endswith(".glb"): try: slim(os.path.join(DIR, f)) except Exception as e: # keep the batch alive; one dud mustn't kill it print("SLIM_FAIL %s %s" % (f, str(e)[:160]), flush=True) print("SLIM_COMPLETE", flush=True)