Ship the Quaternius npc_casual pilot base: rig-agnostic pose sync, five tint slots, unwrapped UVs and wrist-strap camera
This commit is contained in:
1 parent
68f49253fe
commit
cd285b3a39
12 files changed
+242
-74
No files matched your search
@@ -78,8 +78,18 @@ def joint_positions(gltf):
|
||||
for child in node.get("children", []):
|
||||
walk(child, world, child_q)
|
||||
|
||||
# The Blender library exports the armature root as "Armature"; the Quaternius
|
||||
# sources call it "CharacterArmature". Resolve it from the skin instead of the
|
||||
# name so a rebuilt rig cannot silently return an empty joint map.
|
||||
skin_joints = set(gltf["skins"][0]["joints"]) if gltf.get("skins") else set()
|
||||
|
||||
def holds_joints(index):
|
||||
if index in skin_joints:
|
||||
return True
|
||||
return any(holds_joints(child) for child in nodes[index].get("children", []))
|
||||
|
||||
for root in roots:
|
||||
if nodes[root].get("name") == "Armature":
|
||||
if root in skin_joints or holds_joints(root):
|
||||
walk(root)
|
||||
return out
|
||||
|
||||
@@ -108,13 +118,39 @@ def mesh_vertices(gltf, binary):
|
||||
return out
|
||||
|
||||
|
||||
## ADR-0018: a Quaternius-derived character keeps its 62-joint armature, so the
|
||||
## 13-core joint names are aliased onto the upstream ones before measuring.
|
||||
QUATERNIUS_JOINTS = {
|
||||
"ShoulderL": "UpperArm.L", "ShoulderR": "UpperArm.R",
|
||||
"ElbowL": "LowerArm.L", "ElbowR": "LowerArm.R",
|
||||
"HandL": "Wrist.L", "HandR": "Wrist.R",
|
||||
"HipL": "UpperLeg.L", "HipR": "UpperLeg.R",
|
||||
"KneeL": "LowerLeg.L", "KneeR": "LowerLeg.R",
|
||||
"Spine": "Hips",
|
||||
}
|
||||
|
||||
|
||||
def _alias_joints(joints):
|
||||
"""Return a joint map that answers both rig namings."""
|
||||
out = dict(joints)
|
||||
for core, upstream in QUATERNIUS_JOINTS.items():
|
||||
if core not in out and upstream in joints:
|
||||
out[core] = joints[upstream]
|
||||
return out
|
||||
|
||||
|
||||
def _span(values):
|
||||
return max(values) - min(values) if values else 0.0
|
||||
|
||||
|
||||
def measure(name):
|
||||
_magic, _version, _length, _size, gltf, binary = _read_glb(asset_path("npc", name))
|
||||
joints = joint_positions(gltf)
|
||||
raw_joints = joint_positions(gltf)
|
||||
# ADR-0018: the Quaternius base ships its upstream (semi-realistic)
|
||||
# proportions, so the stylized guide band does not apply to it. The row still
|
||||
# reports every measurement and ratio.
|
||||
rig = "quaternius" if "UpperArm.L" in raw_joints else "core"
|
||||
joints = _alias_joints(raw_joints)
|
||||
verts = mesh_vertices(gltf, binary)
|
||||
ys = [p[1] for p, _bone, _material in verts]
|
||||
height = _span(ys)
|
||||
@@ -123,9 +159,10 @@ def measure(name):
|
||||
shoulder = [p for p, _bone, _material in verts
|
||||
if shoulder_y - 0.08 <= p[1] <= shoulder_y + 0.06]
|
||||
hip = [p for p, bone, material in verts
|
||||
if bone in ("HipL", "HipR") and material == "pants"]
|
||||
if bone in ("HipL", "HipR", "UpperLeg.L", "UpperLeg.R")
|
||||
and material == "pants"]
|
||||
feet = [p for p, bone, _material in verts
|
||||
if bone in ("KneeL", "KneeR") and p[1] < 0.2]
|
||||
if bone in ("KneeL", "KneeR", "LowerLeg.L", "LowerLeg.R") and p[1] < 0.2]
|
||||
head_h = _span([p[1] for p in head])
|
||||
head_w = _span([p[0] for p in head])
|
||||
shoulder_w = _span([p[0] for p in shoulder])
|
||||
@@ -165,6 +202,7 @@ def measure(name):
|
||||
}
|
||||
return {
|
||||
"character": name,
|
||||
"rig": rig,
|
||||
"measurements": {key: round(value, 3) for key, value in values.items()},
|
||||
"ratios": {key: round(value, 3) for key, value in ratios.items()},
|
||||
"checks": checks,
|
||||
|
||||
Reference in new issue
Block a user