"""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))