44 lines
2.1 KiB
Python
44 lines
2.1 KiB
Python
"""CampGuide-only hand influence isolation experiment, preserving the fitted rig.
|
|
|
|
This intentionally does not fix joint placement, facing, feet, or neck weights.
|
|
Usage: blender -b --python <script> -- <skinned.glb> <out.glb>
|
|
"""
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import bpy
|
|
|
|
|
|
src, dst = map(Path, sys.argv[sys.argv.index('--')+1:])
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
bpy.ops.import_scene.gltf(filepath=str(src))
|
|
mesh = max((o for o in bpy.context.scene.objects if o.type == 'MESH'), key=lambda o: len(o.data.vertices))
|
|
arm = next(o for o in bpy.context.scene.objects if o.type == 'ARMATURE')
|
|
height = max((mesh.matrix_world @ v.co).z for v in mesh.data.vertices)
|
|
changed = []
|
|
for v in mesh.data.vertices:
|
|
p = mesh.matrix_world @ v.co
|
|
# Restrict to the hands in this particular T-pose mesh.
|
|
if not (abs(p.x) > height*.255 and height*.55 < p.z < height*.67):
|
|
continue
|
|
side = 'Left' if p.x * (arm.matrix_world @ arm.data.bones['LeftHand'].head_local).x > 0 else 'Right'
|
|
allowed = {side+'Arm', side+'ForeArm', side+'Hand'}
|
|
old = {mesh.vertex_groups[g.group].name: g.weight for g in v.groups}
|
|
new = {name: value for name, value in old.items() if name in allowed}
|
|
total = sum(new.values())
|
|
if total < 1e-8:
|
|
new, total = {side+'Hand': 1.0}, 1.0
|
|
rejected = sum(value for name, value in old.items() if name not in allowed)
|
|
for g in list(v.groups):
|
|
mesh.vertex_groups[g.group].remove([v.index])
|
|
for name, value in new.items():
|
|
mesh.vertex_groups[name].add([v.index], value/total, 'REPLACE')
|
|
if rejected > .001:
|
|
changed.append({'vertex': v.index, 'rejected': rejected, 'old': old})
|
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
bpy.ops.export_scene.gltf(filepath=str(dst), export_format='GLB', export_yup=True, export_skins=True, export_animations=False)
|
|
dst.with_suffix('.json').write_text(json.dumps({'purpose': 'hand-only nonlocal influence isolation; not production-ready',
|
|
'source': str(src), 'changed_over_0_001': len(changed), 'changes': changed}, indent=2), encoding='utf-8')
|
|
print('HAND_CLEANUP', len(changed))
|