Spaces:
Paused
Paused
Download scripts/verify_animation.py from mantrakp/component-studio: direct link, hf CLI and curl.
- Browser
- Download file 2.61 kB
-
https://huggingface.co/spaces/mantrakp/component-studio/resolve/main/scripts/verify_animation.py
- Command line
-
hf download hf://spaces/mantrakp/component-studio/scripts/verify_animation.py
-
curl -L -o verify_animation.py https://huggingface.co/spaces/mantrakp/component-studio/resolve/main/scripts/verify_animation.py
2.61 kB
| """Reimport the delivery GLB and verify skinning plus actual animated deformation.""" | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| import sys | |
| import bpy | |
| import numpy as np | |
| p = argparse.ArgumentParser() | |
| p.add_argument("--input", required=True) | |
| p.add_argument("--output", required=True) | |
| a = p.parse_args(sys.argv[sys.argv.index("--") + 1 :]) | |
| bpy.ops.object.select_all(action="SELECT") | |
| bpy.ops.object.delete(use_global=False) | |
| bpy.context.scene.render.fps = 30 | |
| bpy.ops.import_scene.gltf(filepath=str(Path(a.input).resolve())) | |
| meshes = [ | |
| obj | |
| for obj in bpy.context.scene.objects | |
| if obj.type == "MESH" and any(m.type == "ARMATURE" for m in obj.modifiers) | |
| ] | |
| assert len(meshes) == 8, "Expected all eight skinned components" | |
| rigs = [obj for obj in bpy.context.scene.objects if obj.type == "ARMATURE"] | |
| assert len(rigs) == 1, "Expected one rig" | |
| assert rigs[0].animation_data and rigs[0].animation_data.action, "Missing embedded animation" | |
| assert len(rigs[0].data.bones) == 21 | |
| for obj in meshes: | |
| assert all(v.groups for v in obj.data.vertices), f"Unweighted vertices in {obj.name}" | |
| assert all(abs(sum(g.weight for g in v.groups) - 1) < 0.002 for v in obj.data.vertices) | |
| assert any(m.type == "ARMATURE" for m in obj.modifiers) | |
| assert obj.data.uv_layers, f"Missing UVs in {obj.name}" | |
| assert obj.data.materials, f"Missing materials in {obj.name}" | |
| positions = [] | |
| frames = list(range(1, 181)) | |
| for frame in frames: | |
| bpy.context.scene.frame_set(frame) | |
| depsgraph = bpy.context.evaluated_depsgraph_get() | |
| coords = [] | |
| for obj in meshes: | |
| evaluated = obj.evaluated_get(depsgraph) | |
| mesh = evaluated.to_mesh() | |
| coords.extend(tuple(evaluated.matrix_world @ v.co) for v in mesh.vertices) | |
| evaluated.to_mesh_clear() | |
| coords = np.array(coords) | |
| assert np.isfinite(coords).all() | |
| assert np.max(np.ptp(coords, axis=0)) < 1.6, "Implausible skin stretching" | |
| positions.append(coords) | |
| movement = np.max(np.linalg.norm(np.stack(positions) - positions[0], axis=-1), axis=0) | |
| assert movement.max() > 0.1, "The skin does not move with the animation" | |
| report = { | |
| "verified": True, | |
| "frames_checked": frames, | |
| "mesh_objects": len(meshes), | |
| "bones": 21, | |
| "vertices": len(movement), | |
| "moving_vertices": int((movement > 0.005).sum()), | |
| "max_vertex_displacement": float(movement.max()), | |
| "weights_normalized": True, | |
| "uvs_and_materials_present": True, | |
| "finite_deformed_positions": True, | |
| "animation_name": rigs[0].animation_data.action.name, | |
| } | |
| Path(a.output).write_text(json.dumps(report, indent=2)) | |
| print(json.dumps(report)) | |