Xernive's picture
Fix Hunyuan3D error handling + enhanced logging
0e805d4
"""
Blender optimization script for game-ready assets.
Run with: blender --background --python blender_optimize.py -- input.glb output.glb
"""
import bpy
import sys
from pathlib import Path
from mathutils import Vector
def optimize_asset(input_path: str, output_path: str):
"""Optimize GLB for game engine use."""
print(f"[Blender] Optimizing: {input_path}")
# Clear scene
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Import GLB
bpy.ops.import_scene.gltf(filepath=input_path)
# Get imported object
obj = bpy.context.selected_objects[0]
bpy.context.view_layer.objects.active = obj
# 1. Normalize scale to 2m height
bbox = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
height = max(v.z for v in bbox) - min(v.z for v in bbox)
if height > 0:
scale_factor = 2.0 / height
obj.scale = (scale_factor, scale_factor, scale_factor)
bpy.ops.object.transform_apply(scale=True)
# 2. Clean mesh topology
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.mesh.delete_loose()
bpy.ops.mesh.dissolve_degenerate(threshold=0.0001)
bpy.ops.object.mode_set(mode='OBJECT')
# 3. Quad remesh for better topology
mod = obj.modifiers.new(name="Remesh", type='REMESH')
mod.mode = 'SHARP'
mod.octree_depth = 7 # ~7,500 polygons
mod.sharpness = 1.0
mod.use_smooth_shade = True
bpy.ops.object.modifier_apply(modifier="Remesh")
# 4. Smart UV unwrap
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project(
angle_limit=66.0,
island_margin=0.02,
area_weight=1.0,
correct_aspect=True,
scale_to_bounds=True
)
bpy.ops.object.mode_set(mode='OBJECT')
# 5. Apply smooth shading
bpy.ops.object.shade_smooth()
obj.data.use_auto_smooth = True
obj.data.auto_smooth_angle = 0.523599 # 30 degrees
# 6. Generate LOD levels
lod_levels = [
("LOD0", 1.0), # 100% - original
("LOD1", 0.5), # 50% - medium distance
("LOD2", 0.25), # 25% - far distance
]
for lod_name, ratio in lod_levels:
lod_obj = obj.copy()
lod_obj.data = obj.data.copy()
lod_obj.name = f"{obj.name}_{lod_name}"
bpy.context.collection.objects.link(lod_obj)
if ratio < 1.0:
# Apply decimate modifier
bpy.context.view_layer.objects.active = lod_obj
mod = lod_obj.modifiers.new(name="Decimate", type='DECIMATE')
mod.ratio = ratio
bpy.ops.object.modifier_apply(modifier="Decimate")
# 7. Generate collision mesh
collision_obj = obj.copy()
collision_obj.data = obj.data.copy()
collision_obj.name = f"{obj.name}_collision"
bpy.context.collection.objects.link(collision_obj)
bpy.context.view_layer.objects.active = collision_obj
# Simplify heavily for collision
mod = collision_obj.modifiers.new(name="Decimate", type='DECIMATE')
mod.ratio = 0.1 # 10% of original
bpy.ops.object.modifier_apply(modifier="Decimate")
# Convex hull for physics
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.convex_hull()
bpy.ops.object.mode_set(mode='OBJECT')
# 8. Export with Draco compression
bpy.ops.export_scene.gltf(
filepath=output_path,
export_format='GLB',
export_draco_mesh_compression_enable=True,
export_draco_mesh_compression_level=6,
export_draco_position_quantization=14,
export_draco_normal_quantization=10,
export_draco_texcoord_quantization=12,
export_materials='EXPORT',
export_colors=True,
export_cameras=False,
export_lights=False,
export_apply=True,
export_yup=True
)
print(f"[Blender] Optimization complete: {output_path}")
if __name__ == "__main__":
# Parse command line arguments
argv = sys.argv
argv = argv[argv.index("--") + 1:] # Get args after --
if len(argv) < 2:
print("Usage: blender --background --python blender_optimize.py -- input.glb output.glb")
sys.exit(1)
input_path = argv[0]
output_path = argv[1]
optimize_asset(input_path, output_path)