Segmentation: - Face-based exclusive assignment (no overlap, no caps needed) - Hips split from torso (pelvis as own segment) - Torso_upper independent (spine_03 + clavicle, not a variant) - Clavicle moved from arm_upper to torso_upper Hair pipeline: - Solidify modifier (20mm) for scalp hair volume - Normal recalculation before solidify for consistent direction - Eyebrows/facial hair NOT solidified - Fixed mask generation (was outputting black, now white) Clothing pipeline: - Solidify modifier (25mm) for clothing thickness - Peasant shoes added from Fantasy pack Tooling: - convert_outfit.py with solidify support - inspect_glb.py, check_hair_symmetry.py for debugging - Segment reference distribution locked - Q-063 footsteps VFX filed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
32 lines
1.1 KiB
Python
32 lines
1.1 KiB
Python
"""Check if a hair mesh is symmetric across the X axis."""
|
|
import sys
|
|
import bpy
|
|
|
|
if __name__ == "__main__":
|
|
argv = sys.argv
|
|
if "--" not in argv:
|
|
sys.exit(1)
|
|
path = argv[argv.index("--") + 1]
|
|
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
bpy.ops.import_scene.gltf(filepath=path)
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type != 'MESH' or 'Hair' not in obj.name:
|
|
continue
|
|
verts = obj.data.vertices
|
|
xs = [v.co.x for v in verts]
|
|
zs = [v.co.z for v in verts]
|
|
print(f"{obj.name}: {len(verts)} verts")
|
|
print(f" X range: {min(xs):.4f} to {max(xs):.4f}")
|
|
print(f" Z range: {min(zs):.4f} to {max(zs):.4f}")
|
|
left = sum(1 for x in xs if x > 0.01)
|
|
right = sum(1 for x in xs if x < -0.01)
|
|
center = sum(1 for x in xs if abs(x) <= 0.01)
|
|
print(f" left(+X): {left}, right(-X): {right}, center: {center}")
|
|
|
|
# Check normals for consistency
|
|
normals = [v.normal for v in verts]
|
|
outward = sum(1 for n in normals if n.length() > 0.5)
|
|
print(f" verts with normals: {outward}/{len(verts)}")
|