fix(pipeline): stand the character up before scaling; reach bones by role

Fixes the two root causes behind four of the seven reported breakages, and
rejects the source that cannot be fixed.

STAND UP FIRST. flatten_and_scale() now derives the up axis from the
skeleton and rotates the model upright before measuring anything. Aria and
Momo are correct: 1.75 m tall, 1.43 x 0.41 and 1.52 x 0.39 across, verified
by render.

The up vector is measured from the FEET to the HIPS, not from the hips to
the head. The head is not a reliable landmark — the spine walk ends on the
last non-cosmetic bone in the chain, which on a rig with a facial skeleton
can sit BELOW the hips. Momo's did, so the first cut of this fix stood her
neatly on her head: right size, right proportions, upside down. Feet cannot
be mistaken.

REACH BONES BY ROLE. SkinnedPlayerModel gained _role_bone(), and set_weapon
uses it. Four characters could not hold a gun because one hardcoded lookup
knew three spellings and their hands are called "Right wrist" and
"J_Bip_R_Hand" — both resolved perfectly in the sidecar the whole time.

HIKARI IS REJECTED. She now fails the gate: her feet and spine disagree
about which way is up, so the stand-up correction cannot resolve her
either, on top of zero-length cosmetic bones and a second armature that was
smuggling its own clips into the export. That is not a tuning problem, it
is a file that has been through two toolchains. De-registered and removed
rather than shipped broken — which is what the gate is for.

Six GLB skins remain, all passing. Smoke 0 failures, 11/11 movement tests.

Still open, recorded in the skill: kiyoko faces backwards, miku's grown
hair stretches under animation, the mannequin's rifle hold does not
convince, and taila's front skirt clipping.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-26 16:06:51 -04:00
co-authored by Claude Opus 5
parent 2b3e29dd30
commit 27c4117c25
16 changed files with 2025 additions and 2673 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -1,9 +0,0 @@
{
"name": "school anime girl (rigged)",
"uid": "6eaf44ff6ef7461583a5eccc301b88fc",
"author": "hikari",
"author_url": "https://sketchfab.com/hikari0135",
"license": "CC Attribution",
"license_slug": "by",
"source_url": "https://sketchfab.com/3d-models/school-anime-girl-rigged-6eaf44ff6ef7461583a5eccc301b88fc"
}
-731
View File
@@ -1,731 +0,0 @@
{
"roles": {
"hips": "Hips",
"head": "Bone.003_end",
"neck": "Neck",
"spine": [
"Spine",
"Chest",
"Upper Chest",
"Neck",
"Head",
"Bone.003",
"Bone.003_end"
],
"shoulder.R": "Right shoulder",
"shoulder.L": "Left shoulder",
"foot.R": "Right ankle",
"foot.L": "Left ankle",
"hand.R": "Right wrist",
"hand.L": "Left wrist",
"forearm.R": "Right elbow",
"forearm.L": "Left elbow",
"shin.R": "Right knee",
"shin.L": "Left knee",
"toe.R": "Right toe",
"toe.L": "Left toe_end",
"upper_arm.R": "Right arm",
"upper_arm.L": "Left arm",
"thigh.L": "Left leg",
"thigh.R": "Right leg"
},
"chains": [
{
"class": "breast",
"root_parent": "Upper Chest",
"bones": [
"Breast_L",
"J_Sec_L_Bust2",
"J_Sec_L_Bust2_end"
],
"tips": [
[
-0.0,
0.0,
0.0
],
[
2e-05,
5e-05,
-0.0
],
[
2e-05,
5e-05,
-0.0
]
],
"hulls": [
[],
[],
[]
],
"neighbours": [
{},
{},
{}
]
},
{
"class": "breast",
"root_parent": "Upper Chest",
"bones": [
"Breast_R",
"J_Sec_R_Bust2",
"J_Sec_R_Bust2_end"
],
"tips": [
[
0.0,
0.0,
-0.0
],
[
-3e-05,
5e-05,
-1e-05
],
[
-3e-05,
5e-05,
-1e-05
]
],
"hulls": [
[],
[],
[]
],
"neighbours": [
{},
{},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_01",
"J_Sec_Hair2_01",
"J_Sec_Hair2_01_end"
],
"tips": [
[
-0.0,
-0.0,
-0.0
],
[
3e-05,
3e-05,
-4e-05
],
[
3e-05,
3e-05,
-4e-05
]
],
"hulls": [
[
[
-0.0,
-0.0,
-0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_01": 14.335576471341188
},
{
"J_Sec_Hair1_01": 14.335576471341188
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_02",
"J_Sec_Hair2_02",
"J_Sec_Hair2_02_end"
],
"tips": [
[
0.0,
0.0,
-0.0
],
[
0.0,
2e-05,
-5e-05
],
[
0.0,
2e-05,
-5e-05
]
],
"hulls": [
[
[
-0.0,
0.0,
-0.0
]
],
[
[
-0.0,
0.0,
-0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_02": 13.644314260335037
},
{
"J_Sec_Hair1_02": 13.644314260335037
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_03",
"J_Sec_Hair2_03",
"J_Sec_Hair2_03_end"
],
"tips": [
[
0.0,
0.0,
-0.0
],
[
1e-05,
-0.0,
-6e-05
],
[
1e-05,
-0.0,
-6e-05
]
],
"hulls": [
[
[
0.0,
0.0,
0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_03": 29.697010893706615
},
{
"J_Sec_Hair1_03": 29.697010893706615
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_04",
"J_Sec_Hair2_04",
"J_Sec_Hair2_04_end"
],
"tips": [
[
0.0,
0.0,
0.0
],
[
2e-05,
1e-05,
-6e-05
],
[
2e-05,
1e-05,
-6e-05
]
],
"hulls": [
[
[
-0.0,
-0.0,
0.0
]
],
[
[
0.0,
0.0,
-0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_04": 11.592366459380674
},
{
"J_Sec_Hair1_04": 11.592366459380674
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_05",
"J_Sec_Hair2_05",
"J_Sec_Hair2_05_end"
],
"tips": [
[
0.0,
0.0,
-0.0
],
[
1e-05,
2e-05,
-6e-05
],
[
1e-05,
2e-05,
-6e-05
]
],
"hulls": [
[
[
0.0,
0.0,
-0.0
]
],
[
[
-0.0,
0.0,
-0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_05": 11.876289952662745
},
{
"J_Sec_Hair1_05": 11.876289952662745
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_06",
"J_Sec_Hair2_06",
"J_Sec_Hair3_06",
"J_Sec_Hair4_06",
"J_Sec_Hair5_06",
"J_Sec_Hair5_06_end"
],
"tips": [
[
-0.0,
0.0,
-0.0
],
[
-0.0,
0.0,
0.0
],
[
-0.0,
0.0,
0.0
],
[
-0.0,
0.0,
0.0
],
[
0.0,
3e-05,
-5e-05
],
[
0.0,
3e-05,
-5e-05
]
],
"hulls": [
[
[
0.0,
0.0,
-0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[
[
-0.0,
0.0,
-0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_06": 35.917357232526754
},
{
"J_Sec_Hair3_06": 35.52913589468082,
"J_Sec_Hair1_06": 35.917357232526754
},
{
"J_Sec_Hair4_06": 40.302343821289796,
"J_Sec_Hair2_06": 35.52913589468082
},
{
"J_Sec_Hair3_06": 40.302343821289796,
"J_Sec_Hair5_06": 40.950213036078026
},
{
"J_Sec_Hair4_06": 40.950213036078026
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_07",
"J_Sec_Hair2_07",
"J_Sec_Hair3_07",
"J_Sec_Hair4_07",
"J_Sec_Hair5_07",
"J_Sec_Hair5_07_end"
],
"tips": [
[
0.0,
0.0,
0.0
],
[
-0.0,
0.0,
-0.0
],
[
-0.0,
0.0,
0.0
],
[
-0.0,
0.0,
0.0
],
[
3e-05,
3e-05,
-3e-05
],
[
3e-05,
3e-05,
-3e-05
]
],
"hulls": [
[
[
0.0,
0.0,
-0.0
]
],
[
[
-0.0,
0.0,
-0.0
]
],
[
[
0.0,
0.0,
0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[
[
0.0,
0.0,
-0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_07": 35.51566362438205
},
{
"J_Sec_Hair3_07": 35.31215403867316,
"J_Sec_Hair1_07": 35.51566362438205
},
{
"J_Sec_Hair4_07": 44.27519591590416,
"J_Sec_Hair2_07": 35.31215403867316
},
{
"J_Sec_Hair3_07": 44.27519591590416,
"J_Sec_Hair5_07": 40.6256217681383
},
{
"J_Sec_Hair4_07": 40.6256217681383
},
{}
]
},
{
"class": "hair",
"root_parent": "Head",
"bones": [
"J_Sec_Hair1_08",
"J_Sec_Hair2_08",
"J_Sec_Hair3_08",
"J_Sec_Hair3_08_end"
],
"tips": [
[
0.0,
0.0,
0.0
],
[
-0.0,
0.0,
0.0
],
[
4e-05,
-4e-05,
-1e-05
],
[
4e-05,
-4e-05,
-1e-05
]
],
"hulls": [
[
[
-0.0,
-0.0,
0.0
]
],
[
[
-0.0,
0.0,
0.0
]
],
[
[
-0.0,
-0.0,
0.0
]
],
[]
],
"neighbours": [
{
"J_Sec_Hair2_08": 2.5078807040660998
},
{
"J_Sec_Hair1_08": 2.5078807040660998,
"J_Sec_Hair3_08": 2.222614413427433
},
{
"J_Sec_Hair2_08": 2.222614413427433
},
{}
]
}
],
"twist": [
{
"bone": "Bone.004",
"parent": "Head",
"child": "Bone.004_end"
},
{
"bone": "Bone.005",
"parent": "Head",
"child": "Bone.005_end"
},
{
"bone": "Bone.006",
"parent": "Head",
"child": "Bone.006_end"
},
{
"bone": "Bone.007",
"parent": "Head",
"child": "Bone.007_end"
},
{
"bone": "Bone.008",
"parent": "Head",
"child": "Bone.008_end"
},
{
"bone": "Bone.009",
"parent": "Head",
"child": "Bone.009_end"
},
{
"bone": "Bone.010",
"parent": "Head",
"child": "Bone.010_end"
},
{
"bone": "Bone.011",
"parent": "Head",
"child": "Bone.011_end"
},
{
"bone": "Bone.012",
"parent": "Head",
"child": "Bone.012_end"
},
{
"bone": "Bone.013",
"parent": "Head",
"child": "Bone.013_end"
},
{
"bone": "Bone.001",
"parent": "Upper Chest",
"child": "Bone.001_end"
},
{
"bone": "Bone.002",
"parent": "Upper Chest",
"child": "Bone.002_end"
}
],
"colliders": [],
"weights_authored": true,
"driven_bones": [
"Bone.003",
"Bone.003_end",
"Chest",
"Head",
"Hips",
"Left ankle",
"Left arm",
"Left elbow",
"Left knee",
"Left leg",
"Left shoulder",
"Left toe_end",
"Left wrist",
"Neck",
"Right ankle",
"Right arm",
"Right elbow",
"Right knee",
"Right leg",
"Right shoulder",
"Right toe",
"Right wrist",
"Spine",
"Upper Chest"
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.
File diff suppressed because it is too large Load Diff
-7
View File
@@ -41,13 +41,6 @@
"description": "Momo Ayase, DANDADAN (CC-BY, HiGuys920)",
"model": "res://assets/characters/skins/momo.glb",
"unlocked": true
},
{
"id": "hikari",
"name": "Hikari",
"description": "School anime girl \u2014 VRoid (CC-BY, hikari)",
"model": "res://assets/characters/skins/hikari.glb",
"unlocked": true
}
]
}