BlueGit

golden-age paperdoll-renderer

Load models as-is; yaw 0 follows glTF +Z front, spec-correct test bodies

timfa Grok Bot · pushed by timfa

Authored 2026-11-07 · 6 days ago

Hash
f4936e58e0ab4c682b9c051293e235b4e3daab83
Parent
2136718d
17 files changed +184 −156
README.md +1 −1
@@ -23,7 +23,7 @@ De eerste projectweergave toont de bodies. **Kies man / Kies vrouw** selecteert
23 23
24 24 - Sleep om te orbiten. **Vast renderkader tonen** toont het gele orthografische exportkader; stel per man/vrouw **Vaste renderhoogte**, **Centrum Y** (beeldcentrum) en **Pivot Y** (orbitpunt) in en sla op. Zonder handmatige pivot gebruikt de editor de rustpose-nekhoogte (of een fallback met diagnose); **Auto nek** wist een handmatige override. **Omhullende animaties** berekent eenmalig een renderhoogte en Centrum Y waarbinnen alle clips, alle frames (op project-fps) en alle 60 hoeken vallen, met 5% marge aan elke kant; de pivot blijft staan. Daarna is het kader weer vast: er is geen automatische zoom per frame, clip of laag. Equipment dat verder uitsteekt dan het lichaam vraagt handmatig extra hoogte. Body en equipment delen exact dezelfde camera. Uitvinken geeft vrije perspectief-preview met scrollzoom; die zoom verandert de export niet. Een echte 24-bits depthbuffer houdt voorste oppervlakken vooraan.
25 25 - Kies een animatieclip en gebruik **Pauze / Speel**.
26 - **Yaw − / Yaw +** volgen twaalf hoeken van 30°; **pitch -60 / -30 / 0 / 30 / 60** kiezen de hoogte. Yaw 0 kijkt naar de voorkant van de testlichamen (−Z). Vaste oriëntatie (+Y omhoog, het lichaam kijkt naar −Z, zijn rechterzijde is +X): yaw 0 = voorkant (zijn rechterhand links in beeld), 90 = linkerzijde, 180 = achterkant, 270 = rechterzijde; positieve pitch kijkt van boven. Een test met gelabelde markers bewaakt deze afspraak.
26 - **Yaw − / Yaw +** volgen twaalf hoeken van 30°; **pitch -60 / -30 / 0 / 30 / 60** kiezen de hoogte. Yaw 0 kijkt naar de voorkant van elk model. Vaste oriëntatie volgens de glTF-standaard (+Y omhoog, de voorkant kijkt naar +Z, de linkerzijde is +X); modellen en animaties worden as-is geladen, zonder automatische correctie: yaw 0 = voorkant (de rechterhand links in beeld), 90 = linkerzijde, 180 = achterkant, 270 = rechterzijde; positieve pitch kijkt van boven. Een test met gelabelde markers bewaakt deze afspraak.
27 27 - **Render alle hoeken** rendert alle animaties × alle frames op project-fps × 60 hoeken (5 pitches × 12 yaws). Na de losse maps schrijft hij per maptype een atlas plus een `.ppda`-bestand (zie *Atlassen en .ppda*). De preview wordt direct vervangen door iedere voltooide pass: highres → detail → target/diffuse → depth → normal → specular. Alle passes behouden hun aspect met gecentreerde letterboxing en nearest-neighbor weergave. Voortgang telt echte passes; **Annuleer render** behoudt geldige gedeeltelijke uitvoer. **Live 3D** keert terug naar het model; een volgende voltooide renderpass kan de preview weer vervangen.
28 28 - Stel breedte, hoogte en fps in de Bodies-weergave in. Nieuwe projecten beginnen op 4 fps; een bestaand `.ppd` houdt zijn opgeslagen fps.
29 29 - **Zoek onderbroken runs** (ook in de equipment-editor) toont geannuleerde, mislukte of afgebroken runs van dezelfde body/equipmentset met het aantal vastgelegde PNG's. **Hervat** gaat alleen verder als bronbestanden en instellingen exact gelijk zijn aan die run; anders staat erbij wat er verschilt. Na annuleren verschijnt de run direct in deze lijst. Zie *Hervatten* hieronder.
@@ -23,7 +23,7 @@ De eerste projectweergave toont de bodies. **Kies man / Kies vrouw** selecteert
23
23
24- Sleep om te orbiten. **Vast renderkader tonen** toont het gele orthografische exportkader; stel per man/vrouw **Vaste renderhoogte**, **Centrum Y** (beeldcentrum) en **Pivot Y** (orbitpunt) in en sla op. Zonder handmatige pivot gebruikt de editor de rustpose-nekhoogte (of een fallback met diagnose); **Auto nek** wist een handmatige override. **Omhullende animaties** berekent eenmalig een renderhoogte en Centrum Y waarbinnen alle clips, alle frames (op project-fps) en alle 60 hoeken vallen, met 5% marge aan elke kant; de pivot blijft staan. Daarna is het kader weer vast: er is geen automatische zoom per frame, clip of laag. Equipment dat verder uitsteekt dan het lichaam vraagt handmatig extra hoogte. Body en equipment delen exact dezelfde camera. Uitvinken geeft vrije perspectief-preview met scrollzoom; die zoom verandert de export niet. Een echte 24-bits depthbuffer houdt voorste oppervlakken vooraan.
24- Sleep om te orbiten. **Vast renderkader tonen** toont het gele orthografische exportkader; stel per man/vrouw **Vaste renderhoogte**, **Centrum Y** (beeldcentrum) en **Pivot Y** (orbitpunt) in en sla op. Zonder handmatige pivot gebruikt de editor de rustpose-nekhoogte (of een fallback met diagnose); **Auto nek** wist een handmatige override. **Omhullende animaties** berekent eenmalig een renderhoogte en Centrum Y waarbinnen alle clips, alle frames (op project-fps) en alle 60 hoeken vallen, met 5% marge aan elke kant; de pivot blijft staan. Daarna is het kader weer vast: er is geen automatische zoom per frame, clip of laag. Equipment dat verder uitsteekt dan het lichaam vraagt handmatig extra hoogte. Body en equipment delen exact dezelfde camera. Uitvinken geeft vrije perspectief-preview met scrollzoom; die zoom verandert de export niet. Een echte 24-bits depthbuffer houdt voorste oppervlakken vooraan.
25- Kies een animatieclip en gebruik **Pauze / Speel**.
25- Kies een animatieclip en gebruik **Pauze / Speel**.
26- **Yaw − / Yaw +** volgen twaalf hoeken van 30°; **pitch -60 / -30 / 0 / 30 / 60** kiezen de hoogte. Yaw 0 kijkt naar de voorkant van de testlichamen (−Z). Vaste oriëntatie (+Y omhoog, het lichaam kijkt naar −Z, zijn rechterzijde is +X): yaw 0 = voorkant (zijn rechterhand links in beeld), 90 = linkerzijde, 180 = achterkant, 270 = rechterzijde; positieve pitch kijkt van boven. Een test met gelabelde markers bewaakt deze afspraak.
26- **Yaw − / Yaw +** volgen twaalf hoeken van 30°; **pitch -60 / -30 / 0 / 30 / 60** kiezen de hoogte. Yaw 0 kijkt naar de voorkant van elk model. Vaste oriëntatie volgens de glTF-standaard (+Y omhoog, de voorkant kijkt naar +Z, de linkerzijde is +X); modellen en animaties worden as-is geladen, zonder automatische correctie: yaw 0 = voorkant (de rechterhand links in beeld), 90 = linkerzijde, 180 = achterkant, 270 = rechterzijde; positieve pitch kijkt van boven. Een test met gelabelde markers bewaakt deze afspraak.
27- **Render alle hoeken** rendert alle animaties × alle frames op project-fps × 60 hoeken (5 pitches × 12 yaws). Na de losse maps schrijft hij per maptype een atlas plus een `.ppda`-bestand (zie *Atlassen en .ppda*). De preview wordt direct vervangen door iedere voltooide pass: highres → detail → target/diffuse → depth → normal → specular. Alle passes behouden hun aspect met gecentreerde letterboxing en nearest-neighbor weergave. Voortgang telt echte passes; **Annuleer render** behoudt geldige gedeeltelijke uitvoer. **Live 3D** keert terug naar het model; een volgende voltooide renderpass kan de preview weer vervangen.
27- **Render alle hoeken** rendert alle animaties × alle frames op project-fps × 60 hoeken (5 pitches × 12 yaws). Na de losse maps schrijft hij per maptype een atlas plus een `.ppda`-bestand (zie *Atlassen en .ppda*). De preview wordt direct vervangen door iedere voltooide pass: highres → detail → target/diffuse → depth → normal → specular. Alle passes behouden hun aspect met gecentreerde letterboxing en nearest-neighbor weergave. Voortgang telt echte passes; **Annuleer render** behoudt geldige gedeeltelijke uitvoer. **Live 3D** keert terug naar het model; een volgende voltooide renderpass kan de preview weer vervangen.
28- Stel breedte, hoogte en fps in de Bodies-weergave in. Nieuwe projecten beginnen op 4 fps; een bestaand `.ppd` houdt zijn opgeslagen fps.
28- Stel breedte, hoogte en fps in de Bodies-weergave in. Nieuwe projecten beginnen op 4 fps; een bestaand `.ppd` houdt zijn opgeslagen fps.
29- **Zoek onderbroken runs** (ook in de equipment-editor) toont geannuleerde, mislukte of afgebroken runs van dezelfde body/equipmentset met het aantal vastgelegde PNG's. **Hervat** gaat alleen verder als bronbestanden en instellingen exact gelijk zijn aan die run; anders staat erbij wat er verschilt. Na annuleren verschijnt de run direct in deze lijst. Zie *Hervatten* hieronder.
29- **Zoek onderbroken runs** (ook in de equipment-editor) toont geannuleerde, mislukte of afgebroken runs van dezelfde body/equipmentset met het aantal vastgelegde PNG's. **Hervat** gaat alleen verder als bronbestanden en instellingen exact gelijk zijn aan die run; anders staat erbij wat er verschilt. Na annuleren verschijnt de run direct in deze lijst. Zie *Hervatten* hieronder.
scripts/generate_test_bodies.py +16 −15
@@ -3,7 +3,8 @@
3 3
4 4 The meshes, skeleton and clips are procedural. Nothing is downloaded.
5 5 The figures are labelled test assets, not finished characters. glTF axes:
6 Y is up, the character faces -Z, bind rotations are identity.
6 Y is up, the character faces +Z (the glTF front), its left side is +X,
7 bind rotations are identity.
7 8
8 9 Run from the repository root:
9 10
@@ -82,20 +83,20 @@ def skeleton(sex: str) -> list[tuple[str, str | None, tuple[float, float, float]
82 83 ("Chest", "Spine", (0.0, 0.20, 0.0), (p["chest_w"], 0.22, 0.18)),
83 84 ("Neck", "Chest", (0.0, 0.16, 0.0), (0.10, 0.08, 0.10)),
84 85 ("Head", "Neck", (0.0, 0.14, 0.0), (0.18, 0.22, 0.20)),
85 ("LeftShoulder", "Chest", (-shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
86 ("LeftUpperArm", "LeftShoulder", (-0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
86 ("LeftShoulder", "Chest", (shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
87 ("LeftUpperArm", "LeftShoulder", (0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
87 88 ("LeftLowerArm", "LeftUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
88 89 ("LeftHand", "LeftLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
89 ("RightShoulder", "Chest", (shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
90 ("RightUpperArm", "RightShoulder", (0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
90 ("RightShoulder", "Chest", (-shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
91 ("RightUpperArm", "RightShoulder", (-0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
91 92 ("RightLowerArm", "RightUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
92 93 ("RightHand", "RightLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
93 ("LeftUpperLeg", "Hips", (-hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
94 ("LeftUpperLeg", "Hips", (hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
94 95 ("LeftLowerLeg", "LeftUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
95 ("LeftFoot", "LeftLowerLeg", (0.0, -p["lower_leg"], -0.06), (0.10, 0.06, 0.22)),
96 ("RightUpperLeg", "Hips", (hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
96 ("LeftFoot", "LeftLowerLeg", (0.0, -p["lower_leg"], 0.06), (0.10, 0.06, 0.22)),
97 ("RightUpperLeg", "Hips", (-hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
97 98 ("RightLowerLeg", "RightUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
98 ("RightFoot", "RightLowerLeg", (0.0, -p["lower_leg"], -0.06), (0.10, 0.06, 0.22)),
99 ("RightFoot", "RightLowerLeg", (0.0, -p["lower_leg"], 0.06), (0.10, 0.06, 0.22)),
99 100 ]
100 101 names = [item[0] for item in spec]
101 102 if names != JOINT_ORDER:
@@ -124,17 +125,17 @@ def walk_clips() -> dict[str, list[tuple[float, float, float, float]]]:
124 125 "RightUpperArm": [-22.0, 0.0, 22.0, 0.0, -22.0],
125 126 "Chest": [4.0, 0.0, -4.0, 0.0, 4.0],
126 127 }
127 return {name: [quat_x(angle) for angle in angles] for name, angles in curves.items()} | {
128 return {name: [quat_x(-angle) for angle in angles] for name, angles in curves.items()} | {
128 129 "_times": [(t, 0.0, 0.0, 0.0) for t in times]
129 130 }
130 131
131 132
132 133 def attack_clips() -> dict[str, list[tuple[float, float, float, float]]]:
133 # Right arm swings toward -Z (negative X rotation). Chest twists about Y.
134 arm = [quat_x(a) for a in (0.0, -20.0, -100.0, -40.0, 0.0)]
135 forearm = [quat_x(a) for a in (0.0, -10.0, -35.0, -15.0, 0.0)]
134 # Right arm swings forward toward +Z (positive X rotation). Chest twists about Y.
135 arm = [quat_x(-a) for a in (0.0, -20.0, -100.0, -40.0, 0.0)]
136 forearm = [quat_x(-a) for a in (0.0, -10.0, -35.0, -15.0, 0.0)]
136 137 chest = [quat_y(a) for a in (0.0, 6.0, 18.0, 8.0, 0.0)]
137 spine = [quat_x(a) for a in (0.0, 4.0, 12.0, 4.0, 0.0)]
138 spine = [quat_x(-a) for a in (0.0, 4.0, 12.0, 4.0, 0.0)]
138 139 return {
139 140 "RightUpperArm": arm,
140 141 "RightLowerArm": forearm,
@@ -376,7 +377,7 @@ def build_glb(sex: str) -> bytes:
376 377 "license": "CC0-1.0",
377 378 "generator": "scripts/generate_test_bodies.py",
378 379 "up": "+Y",
379 "forward": "-Z",
380 "forward": "+Z",
380 381 "note": "Procedureel testlichaam. Geen motion capture en geen definitief karakter.",
381 382 },
382 383 },
@@ -3,7 +3,8 @@
3
3
4The meshes, skeleton and clips are procedural. Nothing is downloaded.
4The meshes, skeleton and clips are procedural. Nothing is downloaded.
5The figures are labelled test assets, not finished characters. glTF axes:
5The figures are labelled test assets, not finished characters. glTF axes:
6Y is up, the character faces -Z, bind rotations are identity.
6Y is up, the character faces +Z (the glTF front), its left side is +X,
7bind rotations are identity.
7
8
8Run from the repository root:
9Run from the repository root:
9
10
@@ -82,20 +83,20 @@ def skeleton(sex: str) -> list[tuple[str, str | None, tuple[float, float, float]
82 ("Chest", "Spine", (0.0, 0.20, 0.0), (p["chest_w"], 0.22, 0.18)),
83 ("Chest", "Spine", (0.0, 0.20, 0.0), (p["chest_w"], 0.22, 0.18)),
83 ("Neck", "Chest", (0.0, 0.16, 0.0), (0.10, 0.08, 0.10)),
84 ("Neck", "Chest", (0.0, 0.16, 0.0), (0.10, 0.08, 0.10)),
84 ("Head", "Neck", (0.0, 0.14, 0.0), (0.18, 0.22, 0.20)),
85 ("Head", "Neck", (0.0, 0.14, 0.0), (0.18, 0.22, 0.20)),
85 ("LeftShoulder", "Chest", (-shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
86 ("LeftShoulder", "Chest", (shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
86 ("LeftUpperArm", "LeftShoulder", (-0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
87 ("LeftUpperArm", "LeftShoulder", (0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
87 ("LeftLowerArm", "LeftUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
88 ("LeftLowerArm", "LeftUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
88 ("LeftHand", "LeftLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
89 ("LeftHand", "LeftLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
89 ("RightShoulder", "Chest", (shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
90 ("RightShoulder", "Chest", (-shoulder, 0.08, 0.0), (0.10, 0.08, 0.08)),
90 ("RightUpperArm", "RightShoulder", (0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
91 ("RightUpperArm", "RightShoulder", (-0.08, 0.0, 0.0), (0.08, p["upper_arm"], 0.08)),
91 ("RightLowerArm", "RightUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
92 ("RightLowerArm", "RightUpperArm", (0.0, -p["upper_arm"], 0.0), (0.07, p["lower_arm"], 0.07)),
92 ("RightHand", "RightLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
93 ("RightHand", "RightLowerArm", (0.0, -p["lower_arm"], 0.0), (0.08, 0.10, 0.04)),
93 ("LeftUpperLeg", "Hips", (-hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
94 ("LeftUpperLeg", "Hips", (hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
94 ("LeftLowerLeg", "LeftUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
95 ("LeftLowerLeg", "LeftUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
95 ("LeftFoot", "LeftLowerLeg", (0.0, -p["lower_leg"], -0.06), (0.10, 0.06, 0.22)),
96 ("LeftFoot", "LeftLowerLeg", (0.0, -p["lower_leg"], 0.06), (0.10, 0.06, 0.22)),
96 ("RightUpperLeg", "Hips", (hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
97 ("RightUpperLeg", "Hips", (-hip, -0.06, 0.0), (0.12, p["upper_leg"], 0.12)),
97 ("RightLowerLeg", "RightUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
98 ("RightLowerLeg", "RightUpperLeg", (0.0, -p["upper_leg"], 0.0), (0.09, p["lower_leg"], 0.09)),
98 ("RightFoot", "RightLowerLeg", (0.0, -p["lower_leg"], -0.06), (0.10, 0.06, 0.22)),
99 ("RightFoot", "RightLowerLeg", (0.0, -p["lower_leg"], 0.06), (0.10, 0.06, 0.22)),
99 ]
100 ]
100 names = [item[0] for item in spec]
101 names = [item[0] for item in spec]
101 if names != JOINT_ORDER:
102 if names != JOINT_ORDER:
@@ -124,17 +125,17 @@ def walk_clips() -> dict[str, list[tuple[float, float, float, float]]]:
124 "RightUpperArm": [-22.0, 0.0, 22.0, 0.0, -22.0],
125 "RightUpperArm": [-22.0, 0.0, 22.0, 0.0, -22.0],
125 "Chest": [4.0, 0.0, -4.0, 0.0, 4.0],
126 "Chest": [4.0, 0.0, -4.0, 0.0, 4.0],
126 }
127 }
127 return {name: [quat_x(angle) for angle in angles] for name, angles in curves.items()} | {
128 return {name: [quat_x(-angle) for angle in angles] for name, angles in curves.items()} | {
128 "_times": [(t, 0.0, 0.0, 0.0) for t in times]
129 "_times": [(t, 0.0, 0.0, 0.0) for t in times]
129 }
130 }
130
131
131
132
132def attack_clips() -> dict[str, list[tuple[float, float, float, float]]]:
133def attack_clips() -> dict[str, list[tuple[float, float, float, float]]]:
133 # Right arm swings toward -Z (negative X rotation). Chest twists about Y.
134 # Right arm swings forward toward +Z (positive X rotation). Chest twists about Y.
134 arm = [quat_x(a) for a in (0.0, -20.0, -100.0, -40.0, 0.0)]
135 arm = [quat_x(-a) for a in (0.0, -20.0, -100.0, -40.0, 0.0)]
135 forearm = [quat_x(a) for a in (0.0, -10.0, -35.0, -15.0, 0.0)]
136 forearm = [quat_x(-a) for a in (0.0, -10.0, -35.0, -15.0, 0.0)]
136 chest = [quat_y(a) for a in (0.0, 6.0, 18.0, 8.0, 0.0)]
137 chest = [quat_y(a) for a in (0.0, 6.0, 18.0, 8.0, 0.0)]
137 spine = [quat_x(a) for a in (0.0, 4.0, 12.0, 4.0, 0.0)]
138 spine = [quat_x(-a) for a in (0.0, 4.0, 12.0, 4.0, 0.0)]
138 return {
139 return {
139 "RightUpperArm": arm,
140 "RightUpperArm": arm,
140 "RightLowerArm": forearm,
141 "RightLowerArm": forearm,
@@ -376,7 +377,7 @@ def build_glb(sex: str) -> bytes:
376 "license": "CC0-1.0",
377 "license": "CC0-1.0",
377 "generator": "scripts/generate_test_bodies.py",
378 "generator": "scripts/generate_test_bodies.py",
378 "up": "+Y",
379 "up": "+Y",
379 "forward": "-Z",
380 "forward": "+Z",
380 "note": "Procedureel testlichaam. Geen motion capture en geen definitief karakter.",
381 "note": "Procedureel testlichaam. Geen motion capture en geen definitief karakter.",
381 },
382 },
382 },
383 },
src/camera.rs +10 −10
@@ -171,8 +171,8 @@ pub fn matrices(framing: CameraFraming, yaw: f32, pitch: f32, aspect: f32) -> (M
171 171 }
172 172
173 173 /// Labelled orientation convention (§6/§13), verified by
174 /// `orientation_convention_is_labelled`: the character faces −Z, +Y is up,
175 /// its right hand side is +X. Camera eye = `eye_offset(yaw, pitch)`.
174 /// `orientation_convention_is_labelled`: the glTF convention — the character
175 /// faces +Z, +Y is up, its left hand side is +X (−X is right). Camera eye = `eye_offset(yaw, pitch)`.
176 176 pub const ORIENTATION_VIEWS: [(f32, &str); 4] = [
177 177 (0., "voorkant"),
178 178 (90., "linkerzijde"),
@@ -564,12 +564,12 @@ pub fn arm_measure(body: &PreviewCharacter) -> Option<(f32, f32)> {
564 564 mod tests {
565 565 #[test]
566 566 fn orientation_convention_is_labelled() {
567 // Labelled markers on a body facing −Z with its right side on +X.
567 // Labelled markers on a body facing +Z (glTF front), its left side on +X.
568 568 let markers = [
569 ("voorkant", [0., 1., -0.3]),
570 ("achterkant", [0., 1., 0.3]),
571 ("linkerzijde", [-0.3, 1., 0.]),
572 ("rechterzijde", [0.3, 1., 0.]),
569 ("voorkant", [0., 1., 0.3]),
570 ("achterkant", [0., 1., -0.3]),
571 ("linkerzijde", [0.3, 1., 0.]),
572 ("rechterzijde", [-0.3, 1., 0.]),
573 573 ];
574 574 let framing = crate::project::CameraFraming::default();
575 575 for (yaw, label) in ORIENTATION_VIEWS {
@@ -583,14 +583,14 @@ mod tests {
583 583 .0;
584 584 assert_eq!(nearest, label, "yaw {yaw}");
585 585 }
586 // Yaw 0 (front): the character's right (+X) is on the image's left.
586 // Yaw 0 (front): the character's right (−X) is on the image's left.
587 587 let (view, _) = matrices(framing, 0., 0., 1.);
588 588 let x = |p: [f32; 3]| view[0] * p[0] + view[4] * p[1] + view[8] * p[2] + view[12];
589 assert!(x([0.3, 1., 0.]) < 0. && x([-0.3, 1., 0.]) > 0.);
589 assert!(x([-0.3, 1., 0.]) < 0. && x([0.3, 1., 0.]) > 0.);
590 590 // Positive pitch looks down from above: the front marker sits lower.
591 591 let (view, _) = matrices(framing, 0., 60., 1.);
592 592 let y = |p: [f32; 3]| view[1] * p[0] + view[5] * p[1] + view[9] * p[2] + view[13];
593 assert!(y([0., 1., -0.3]) < y([0., 1., 0.3]));
593 assert!(y([0., 1., 0.3]) < y([0., 1., -0.3]));
594 594 }
595 595
596 596 #[test]
@@ -171,8 +171,8 @@ pub fn matrices(framing: CameraFraming, yaw: f32, pitch: f32, aspect: f32) -> (M
171}
171}
172
172
173/// Labelled orientation convention (§6/§13), verified by
173/// Labelled orientation convention (§6/§13), verified by
174/// `orientation_convention_is_labelled`: the character faces −Z, +Y is up,
174/// `orientation_convention_is_labelled`: the glTF convention — the character
175/// its right hand side is +X. Camera eye = `eye_offset(yaw, pitch)`.
175/// faces +Z, +Y is up, its left hand side is +X (−X is right). Camera eye = `eye_offset(yaw, pitch)`.
176pub const ORIENTATION_VIEWS: [(f32, &str); 4] = [
176pub const ORIENTATION_VIEWS: [(f32, &str); 4] = [
177 (0., "voorkant"),
177 (0., "voorkant"),
178 (90., "linkerzijde"),
178 (90., "linkerzijde"),
@@ -564,12 +564,12 @@ pub fn arm_measure(body: &PreviewCharacter) -> Option<(f32, f32)> {
564mod tests {
564mod tests {
565 #[test]
565 #[test]
566 fn orientation_convention_is_labelled() {
566 fn orientation_convention_is_labelled() {
567 // Labelled markers on a body facing −Z with its right side on +X.
567 // Labelled markers on a body facing +Z (glTF front), its left side on +X.
568 let markers = [
568 let markers = [
569 ("voorkant", [0., 1., -0.3]),
569 ("voorkant", [0., 1., 0.3]),
570 ("achterkant", [0., 1., 0.3]),
570 ("achterkant", [0., 1., -0.3]),
571 ("linkerzijde", [-0.3, 1., 0.]),
571 ("linkerzijde", [0.3, 1., 0.]),
572 ("rechterzijde", [0.3, 1., 0.]),
572 ("rechterzijde", [-0.3, 1., 0.]),
573 ];
573 ];
574 let framing = crate::project::CameraFraming::default();
574 let framing = crate::project::CameraFraming::default();
575 for (yaw, label) in ORIENTATION_VIEWS {
575 for (yaw, label) in ORIENTATION_VIEWS {
@@ -583,14 +583,14 @@ mod tests {
583 .0;
583 .0;
584 assert_eq!(nearest, label, "yaw {yaw}");
584 assert_eq!(nearest, label, "yaw {yaw}");
585 }
585 }
586 // Yaw 0 (front): the character's right (+X) is on the image's left.
586 // Yaw 0 (front): the character's right (−X) is on the image's left.
587 let (view, _) = matrices(framing, 0., 0., 1.);
587 let (view, _) = matrices(framing, 0., 0., 1.);
588 let x = |p: [f32; 3]| view[0] * p[0] + view[4] * p[1] + view[8] * p[2] + view[12];
588 let x = |p: [f32; 3]| view[0] * p[0] + view[4] * p[1] + view[8] * p[2] + view[12];
589 assert!(x([0.3, 1., 0.]) < 0. && x([-0.3, 1., 0.]) > 0.);
589 assert!(x([-0.3, 1., 0.]) < 0. && x([0.3, 1., 0.]) > 0.);
590 // Positive pitch looks down from above: the front marker sits lower.
590 // Positive pitch looks down from above: the front marker sits lower.
591 let (view, _) = matrices(framing, 0., 60., 1.);
591 let (view, _) = matrices(framing, 0., 60., 1.);
592 let y = |p: [f32; 3]| view[1] * p[0] + view[5] * p[1] + view[9] * p[2] + view[13];
592 let y = |p: [f32; 3]| view[1] * p[0] + view[5] * p[1] + view[9] * p[2] + view[13];
593 assert!(y([0., 1., -0.3]) < y([0., 1., 0.3]));
593 assert!(y([0., 1., 0.3]) < y([0., 1., -0.3]));
594 }
594 }
595
595
596 #[test]
596 #[test]
src/export_fixture.rs +1 −1
@@ -122,7 +122,7 @@ impl SyntheticRun {
122 122 "depth_bits": self.depth_format.bits,
123 123 "normal_space": "view (+Y up), PNG rows top-down",
124 124 "camera_distance": 10,
125 "yaw_zero": "camera on -Z",
125 "yaw_zero": "camera on +Z",
126 126 "pitches": PRODUCTION_PITCHES,
127 127 "yaws": production::production_yaws(),
128 128 "yaw_step": 30,
@@ -122,7 +122,7 @@ impl SyntheticRun {
122 "depth_bits": self.depth_format.bits,
122 "depth_bits": self.depth_format.bits,
123 "normal_space": "view (+Y up), PNG rows top-down",
123 "normal_space": "view (+Y up), PNG rows top-down",
124 "camera_distance": 10,
124 "camera_distance": 10,
125 "yaw_zero": "camera on -Z",
125 "yaw_zero": "camera on +Z",
126 "pitches": PRODUCTION_PITCHES,
126 "pitches": PRODUCTION_PITCHES,
127 "yaws": production::production_yaws(),
127 "yaws": production::production_yaws(),
128 "yaw_step": 30,
128 "yaw_step": 30,
src/export_set.rs +1 −1
@@ -1033,7 +1033,7 @@ mod tests {
1033 1033 "depth_encoding": "gray",
1034 1034 "depth_bits": 16,
1035 1035 "camera_distance": 10.0,
1036 "yaw_zero": "camera on -Z",
1036 "yaw_zero": "camera on +Z",
1037 1037 "normal_space": "view (+Y up), PNG rows top-down",
1038 1038 "pitches": crate::preview::PRODUCTION_PITCHES,
1039 1039 "yaws": crate::production::production_yaws(),
@@ -1033,7 +1033,7 @@ mod tests {
1033 "depth_encoding": "gray",
1033 "depth_encoding": "gray",
1034 "depth_bits": 16,
1034 "depth_bits": 16,
1035 "camera_distance": 10.0,
1035 "camera_distance": 10.0,
1036 "yaw_zero": "camera on -Z",
1036 "yaw_zero": "camera on +Z",
1037 "normal_space": "view (+Y up), PNG rows top-down",
1037 "normal_space": "view (+Y up), PNG rows top-down",
1038 "pitches": crate::preview::PRODUCTION_PITCHES,
1038 "pitches": crate::preview::PRODUCTION_PITCHES,
1039 "yaws": crate::production::production_yaws(),
1039 "yaws": crate::production::production_yaws(),
src/preview.rs +7 −4
@@ -3,7 +3,7 @@
3 3 //! This is not the paperdoll export. The export camera is orthographic and
4 4 //! still out of scope. The interactive camera can snap to the same five
5 5 //! pitches and twelve yaws. The character forward axis of the test bodies is -Z,
6 //! so yaw 0 places the camera on -Z, looking at the body.
6 //! so yaw 0 places the camera on +Z (the glTF front), looking at the body.
7 7
8 8 use std::path::Path;
9 9
@@ -1188,9 +1188,9 @@ pub fn eye_offset(yaw_deg: f32, pitch_deg: f32, distance: f32) -> [f32; 3] {
1188 1188 let yaw = yaw_deg.to_radians();
1189 1189 let pitch = pitch_deg.to_radians();
1190 1190 [
1191 -distance * pitch.cos() * yaw.sin(),
1191 distance * pitch.cos() * yaw.sin(),
1192 1192 distance * pitch.sin(),
1193 -distance * pitch.cos() * yaw.cos(),
1193 distance * pitch.cos() * yaw.cos(),
1194 1194 ]
1195 1195 }
1196 1196
@@ -1927,7 +1927,10 @@ mod tests {
1927 1927 #[test]
1928 1928 fn yaw_zero_is_in_front_and_the_twelve_yaws_differ() {
1929 1929 let front = eye_offset(0.0, 0.0, 4.0);
1930 assert!(front[2] < -3.5, "yaw 0 camera belongs on -Z, got {front:?}");
1930 assert!(
1931 front[2] > 3.5,
1932 "yaw 0 camera belongs on +Z (glTF front), got {front:?}"
1933 );
1931 1934 assert!(front[0].abs() < 0.05);
1932 1935 let mut seen = Vec::new();
1933 1936 for yaw in paperdoll_yaws_deg() {
@@ -3,7 +3,7 @@
3//! This is not the paperdoll export. The export camera is orthographic and
3//! This is not the paperdoll export. The export camera is orthographic and
4//! still out of scope. The interactive camera can snap to the same five
4//! still out of scope. The interactive camera can snap to the same five
5//! pitches and twelve yaws. The character forward axis of the test bodies is -Z,
5//! pitches and twelve yaws. The character forward axis of the test bodies is -Z,
6//! so yaw 0 places the camera on -Z, looking at the body.
6//! so yaw 0 places the camera on +Z (the glTF front), looking at the body.
7
7
8use std::path::Path;
8use std::path::Path;
9
9
@@ -1188,9 +1188,9 @@ pub fn eye_offset(yaw_deg: f32, pitch_deg: f32, distance: f32) -> [f32; 3] {
1188 let yaw = yaw_deg.to_radians();
1188 let yaw = yaw_deg.to_radians();
1189 let pitch = pitch_deg.to_radians();
1189 let pitch = pitch_deg.to_radians();
1190 [
1190 [
1191 -distance * pitch.cos() * yaw.sin(),
1191 distance * pitch.cos() * yaw.sin(),
1192 distance * pitch.sin(),
1192 distance * pitch.sin(),
1193 -distance * pitch.cos() * yaw.cos(),
1193 distance * pitch.cos() * yaw.cos(),
1194 ]
1194 ]
1195}
1195}
1196
1196
@@ -1927,7 +1927,10 @@ mod tests {
1927 #[test]
1927 #[test]
1928 fn yaw_zero_is_in_front_and_the_twelve_yaws_differ() {
1928 fn yaw_zero_is_in_front_and_the_twelve_yaws_differ() {
1929 let front = eye_offset(0.0, 0.0, 4.0);
1929 let front = eye_offset(0.0, 0.0, 4.0);
1930 assert!(front[2] < -3.5, "yaw 0 camera belongs on -Z, got {front:?}");
1930 assert!(
1931 front[2] > 3.5,
1932 "yaw 0 camera belongs on +Z (glTF front), got {front:?}"
1933 );
1931 assert!(front[0].abs() < 0.05);
1934 assert!(front[0].abs() < 0.05);
1932 let mut seen = Vec::new();
1935 let mut seen = Vec::new();
1933 for yaw in paperdoll_yaws_deg() {
1936 for yaw in paperdoll_yaws_deg() {
src/preview_gl.rs +1 −1
@@ -137,7 +137,7 @@ impl Viewport {
137 137 ));
138 138 });
139 139 ui.label(
140 "Sleep om te draaien, scroll om te zoomen. Yaw 0 kijkt op de voorkant (−Z). De knoppen zetten de vijf pitches en de twaalf yaws.",
140 "Sleep om te draaien, scroll om te zoomen. Yaw 0 kijkt op de voorkant (+Z, glTF-standaard). De knoppen zetten de vijf pitches en de twaalf yaws.",
141 141 );
142 142 let available = ui.available_rect_before_wrap();
143 143 let rect = egui::Rect::from_min_size(
@@ -137,7 +137,7 @@ impl Viewport {
137 ));
137 ));
138 });
138 });
139 ui.label(
139 ui.label(
140 "Sleep om te draaien, scroll om te zoomen. Yaw 0 kijkt op de voorkant (−Z). De knoppen zetten de vijf pitches en de twaalf yaws.",
140 "Sleep om te draaien, scroll om te zoomen. Yaw 0 kijkt op de voorkant (+Z, glTF-standaard). De knoppen zetten de vijf pitches en de twaalf yaws.",
141 );
141 );
142 let available = ui.available_rect_before_wrap();
142 let available = ui.available_rect_before_wrap();
143 let rect = egui::Rect::from_min_size(
143 let rect = egui::Rect::from_min_size(
src/production.rs +1 −1
@@ -1474,7 +1474,7 @@ pub fn contract(mut job: RenderJob) -> Result<Contract, String> {
1474 1474 "depth_bits": job.depth_format.bits,
1475 1475 "normal_space": "view (+Y up), PNG rows top-down",
1476 1476 "camera_distance": crate::camera::CAMERA_DISTANCE,
1477 "yaw_zero": "camera on -Z",
1477 "yaw_zero": "camera on +Z",
1478 1478 "pitches": preview::PRODUCTION_PITCHES,
1479 1479 "yaws": production_yaws(),
1480 1480 "yaw_step": 30,
@@ -1474,7 +1474,7 @@ pub fn contract(mut job: RenderJob) -> Result<Contract, String> {
1474 "depth_bits": job.depth_format.bits,
1474 "depth_bits": job.depth_format.bits,
1475 "normal_space": "view (+Y up), PNG rows top-down",
1475 "normal_space": "view (+Y up), PNG rows top-down",
1476 "camera_distance": crate::camera::CAMERA_DISTANCE,
1476 "camera_distance": crate::camera::CAMERA_DISTANCE,
1477 "yaw_zero": "camera on -Z",
1477 "yaw_zero": "camera on +Z",
1478 "pitches": preview::PRODUCTION_PITCHES,
1478 "pitches": preview::PRODUCTION_PITCHES,
1479 "yaws": production_yaws(),
1479 "yaws": production_yaws(),
1480 "yaw_step": 30,
1480 "yaw_step": 30,
src/still.rs +4 −4
@@ -185,12 +185,12 @@ mod tests {
185 185 .join("tests/fixtures/bodies/test-body-male.glb");
186 186 let mut character = load_gltf_file(&path).unwrap();
187 187 character.positions = vec![
188 [-0.5, 0.4, -0.25],
189 [0.5, 0.4, -0.25],
190 [0., 1.5, -0.25],
191 188 [-0.5, 0.4, 0.25],
192 189 [0.5, 0.4, 0.25],
193 [0., 1.5, 0.95],
190 [0., 1.5, 0.25],
191 [-0.5, 0.4, -0.25],
192 [0.5, 0.4, -0.25],
193 [0., 1.5, -0.95],
194 194 ];
195 195 character.joint_nodes.clear();
196 196 character.inverse_binds.clear();
@@ -185,12 +185,12 @@ mod tests {
185 .join("tests/fixtures/bodies/test-body-male.glb");
185 .join("tests/fixtures/bodies/test-body-male.glb");
186 let mut character = load_gltf_file(&path).unwrap();
186 let mut character = load_gltf_file(&path).unwrap();
187 character.positions = vec![
187 character.positions = vec![
188 [-0.5, 0.4, -0.25],
189 [0.5, 0.4, -0.25],
190 [0., 1.5, -0.25],
191 [-0.5, 0.4, 0.25],
188 [-0.5, 0.4, 0.25],
192 [0.5, 0.4, 0.25],
189 [0.5, 0.4, 0.25],
193 [0., 1.5, 0.95],
190 [0., 1.5, 0.25],
191 [-0.5, 0.4, -0.25],
192 [0.5, 0.4, -0.25],
193 [0., 1.5, -0.95],
194 ];
194 ];
195 character.joint_nodes.clear();
195 character.joint_nodes.clear();
196 character.inverse_binds.clear();
196 character.inverse_binds.clear();
tests/fixtures/bodies/README.md +1 −1
@@ -12,7 +12,7 @@ Beide hebben een skin met 19 joints en twee clips:
12 12 - `walk` — loopcyclus van 1 seconde, armen tegengesteld aan de benen, eerste en laatste sample gelijk
13 13 - `attack` — zwaai van de rechterarm naar voren, 0,8 seconde, eindigt weer in de bindpose
14 14
15 De bindpose heeft identiteitsrotaties. glTF-assen: +Y is omhoog, het gezicht wijst naar -Z. Elke vertex hoort met gewicht 1 bij één joint. De inverse-bindmatrix is de inverse translatie van de wereldpositie in die bindpose.
15 De bindpose heeft identiteitsrotaties. glTF-assen: +Y is omhoog, het gezicht wijst naar +Z (glTF-standaard), de linkerzijde ligt op +X. Elke vertex hoort met gewicht 1 bij één joint. De inverse-bindmatrix is de inverse translatie van de wereldpositie in die bindpose.
16 16
17 17 Opnieuw genereren, vanaf de repositoryroot:
18 18
@@ -12,7 +12,7 @@ Beide hebben een skin met 19 joints en twee clips:
12- `walk` — loopcyclus van 1 seconde, armen tegengesteld aan de benen, eerste en laatste sample gelijk
12- `walk` — loopcyclus van 1 seconde, armen tegengesteld aan de benen, eerste en laatste sample gelijk
13- `attack` — zwaai van de rechterarm naar voren, 0,8 seconde, eindigt weer in de bindpose
13- `attack` — zwaai van de rechterarm naar voren, 0,8 seconde, eindigt weer in de bindpose
14
14
15De bindpose heeft identiteitsrotaties. glTF-assen: +Y is omhoog, het gezicht wijst naar -Z. Elke vertex hoort met gewicht 1 bij één joint. De inverse-bindmatrix is de inverse translatie van de wereldpositie in die bindpose.
15De bindpose heeft identiteitsrotaties. glTF-assen: +Y is omhoog, het gezicht wijst naar +Z (glTF-standaard), de linkerzijde ligt op +X. Elke vertex hoort met gewicht 1 bij één joint. De inverse-bindmatrix is de inverse translatie van de wereldpositie in die bindpose.
16
16
17Opnieuw genereren, vanaf de repositoryroot:
17Opnieuw genereren, vanaf de repositoryroot:
18
18
tests/fixtures/bodies/static/static-body-male.glb Binary file +0 −0

The contents of this binary file are not shown.

tests/fixtures/bodies/static/static-body-male.obj +112 −112
@@ -103,86 +103,6 @@ vn 0.000000 1.000000 0.000000
103 103 vn 0.000000 1.000000 0.000000
104 104 vn 0.000000 -1.000000 0.000000
105 105 vn 0.000000 -1.000000 0.000000
106 v -0.250000 1.340000 -0.040000
107 v -0.150000 1.340000 -0.040000
108 v -0.150000 1.420000 -0.040000
109 v -0.250000 1.420000 -0.040000
110 v -0.250000 1.340000 0.040000
111 v -0.150000 1.340000 0.040000
112 v -0.150000 1.420000 0.040000
113 v -0.250000 1.420000 0.040000
114 vn 0.000000 0.000000 -1.000000
115 vn 0.000000 0.000000 -1.000000
116 vn 0.000000 0.000000 1.000000
117 vn 0.000000 -0.000000 1.000000
118 vn -1.000000 0.000000 0.000000
119 vn -1.000000 0.000000 0.000000
120 vn 1.000000 0.000000 0.000000
121 vn 1.000000 0.000000 0.000000
122 vn 0.000000 1.000000 0.000000
123 vn 0.000000 1.000000 0.000000
124 vn 0.000000 -1.000000 0.000000
125 vn 0.000000 -1.000000 0.000000
126 v -0.320000 1.240000 -0.040000
127 v -0.240000 1.240000 -0.040000
128 v -0.240000 1.520000 -0.040000
129 v -0.320000 1.520000 -0.040000
130 v -0.320000 1.240000 0.040000
131 v -0.240000 1.240000 0.040000
132 v -0.240000 1.520000 0.040000
133 v -0.320000 1.520000 0.040000
134 vn 0.000000 0.000000 -1.000000
135 vn 0.000000 0.000000 -1.000000
136 vn 0.000000 0.000000 1.000000
137 vn 0.000000 -0.000000 1.000000
138 vn -1.000000 0.000000 0.000000
139 vn -1.000000 0.000000 0.000000
140 vn 1.000000 0.000000 0.000000
141 vn 1.000000 0.000000 0.000000
142 vn 0.000000 1.000000 0.000000
143 vn 0.000000 1.000000 0.000000
144 vn 0.000000 -1.000000 0.000000
145 vn 0.000000 -1.000000 0.000000
146 v -0.315000 0.970000 -0.035000
147 v -0.245000 0.970000 -0.035000
148 v -0.245000 1.230000 -0.035000
149 v -0.315000 1.230000 -0.035000
150 v -0.315000 0.970000 0.035000
151 v -0.245000 0.970000 0.035000
152 v -0.245000 1.230000 0.035000
153 v -0.315000 1.230000 0.035000
154 vn 0.000000 0.000000 -1.000000
155 vn 0.000000 0.000000 -1.000000
156 vn 0.000000 0.000000 1.000000
157 vn 0.000000 -0.000000 1.000000
158 vn -1.000000 0.000000 0.000000
159 vn -1.000000 0.000000 0.000000
160 vn 1.000000 0.000000 0.000000
161 vn 1.000000 0.000000 0.000000
162 vn 0.000000 1.000000 0.000000
163 vn 0.000000 1.000000 0.000000
164 vn 0.000000 -1.000000 0.000000
165 vn 0.000000 -1.000000 0.000000
166 v -0.320000 0.790000 -0.020000
167 v -0.240000 0.790000 -0.020000
168 v -0.240000 0.890000 -0.020000
169 v -0.320000 0.890000 -0.020000
170 v -0.320000 0.790000 0.020000
171 v -0.240000 0.790000 0.020000
172 v -0.240000 0.890000 0.020000
173 v -0.320000 0.890000 0.020000
174 vn 0.000000 0.000000 -1.000000
175 vn 0.000000 0.000000 -1.000000
176 vn 0.000000 0.000000 1.000000
177 vn 0.000000 -0.000000 1.000000
178 vn -1.000000 0.000000 0.000000
179 vn -1.000000 0.000000 0.000000
180 vn 1.000000 0.000000 0.000000
181 vn 1.000000 0.000000 0.000000
182 vn 0.000000 1.000000 0.000000
183 vn 0.000000 1.000000 0.000000
184 vn 0.000000 -1.000000 0.000000
185 vn 0.000000 -1.000000 0.000000
186 106 v 0.150000 1.340000 -0.040000
187 107 v 0.250000 1.340000 -0.040000
188 108 v 0.250000 1.420000 -0.040000
@@ -263,14 +183,14 @@ vn 0.000000 1.000000 0.000000
263 183 vn 0.000000 1.000000 0.000000
264 184 vn 0.000000 -1.000000 0.000000
265 185 vn 0.000000 -1.000000 0.000000
266 v -0.160000 0.690000 -0.060000
267 v -0.040000 0.690000 -0.060000
268 v -0.040000 1.110000 -0.060000
269 v -0.160000 1.110000 -0.060000
270 v -0.160000 0.690000 0.060000
271 v -0.040000 0.690000 0.060000
272 v -0.040000 1.110000 0.060000
273 v -0.160000 1.110000 0.060000
186 v -0.250000 1.340000 -0.040000
187 v -0.150000 1.340000 -0.040000
188 v -0.150000 1.420000 -0.040000
189 v -0.250000 1.420000 -0.040000
190 v -0.250000 1.340000 0.040000
191 v -0.150000 1.340000 0.040000
192 v -0.150000 1.420000 0.040000
193 v -0.250000 1.420000 0.040000
274 194 vn 0.000000 0.000000 -1.000000
275 195 vn 0.000000 0.000000 -1.000000
276 196 vn 0.000000 0.000000 1.000000
@@ -283,14 +203,34 @@ vn 0.000000 1.000000 0.000000
283 203 vn 0.000000 1.000000 0.000000
284 204 vn 0.000000 -1.000000 0.000000
285 205 vn 0.000000 -1.000000 0.000000
286 v -0.145000 0.280000 -0.045000
287 v -0.055000 0.280000 -0.045000
288 v -0.055000 0.680000 -0.045000
289 v -0.145000 0.680000 -0.045000
290 v -0.145000 0.280000 0.045000
291 v -0.055000 0.280000 0.045000
292 v -0.055000 0.680000 0.045000
293 v -0.145000 0.680000 0.045000
206 v -0.320000 1.240000 -0.040000
207 v -0.240000 1.240000 -0.040000
208 v -0.240000 1.520000 -0.040000
209 v -0.320000 1.520000 -0.040000
210 v -0.320000 1.240000 0.040000
211 v -0.240000 1.240000 0.040000
212 v -0.240000 1.520000 0.040000
213 v -0.320000 1.520000 0.040000
214 vn 0.000000 0.000000 -1.000000
215 vn 0.000000 0.000000 -1.000000
216 vn 0.000000 0.000000 1.000000
217 vn 0.000000 -0.000000 1.000000
218 vn -1.000000 0.000000 0.000000
219 vn -1.000000 0.000000 0.000000
220 vn 1.000000 0.000000 0.000000
221 vn 1.000000 0.000000 0.000000
222 vn 0.000000 1.000000 0.000000
223 vn 0.000000 1.000000 0.000000
224 vn 0.000000 -1.000000 0.000000
225 vn 0.000000 -1.000000 0.000000
226 v -0.315000 0.970000 -0.035000
227 v -0.245000 0.970000 -0.035000
228 v -0.245000 1.230000 -0.035000
229 v -0.315000 1.230000 -0.035000
230 v -0.315000 0.970000 0.035000
231 v -0.245000 0.970000 0.035000
232 v -0.245000 1.230000 0.035000
233 v -0.315000 1.230000 0.035000
294 234 vn 0.000000 0.000000 -1.000000
295 235 vn 0.000000 0.000000 -1.000000
296 236 vn 0.000000 0.000000 1.000000
@@ -303,14 +243,14 @@ vn 0.000000 1.000000 0.000000
303 243 vn 0.000000 1.000000 0.000000
304 244 vn 0.000000 -1.000000 0.000000
305 245 vn 0.000000 -1.000000 0.000000
306 v -0.150000 0.050000 -0.170000
307 v -0.050000 0.050000 -0.170000
308 v -0.050000 0.110000 -0.170000
309 v -0.150000 0.110000 -0.170000
310 v -0.150000 0.050000 0.050000
311 v -0.050000 0.050000 0.050000
312 v -0.050000 0.110000 0.050000
313 v -0.150000 0.110000 0.050000
246 v -0.320000 0.790000 -0.020000
247 v -0.240000 0.790000 -0.020000
248 v -0.240000 0.890000 -0.020000
249 v -0.320000 0.890000 -0.020000
250 v -0.320000 0.790000 0.020000
251 v -0.240000 0.790000 0.020000
252 v -0.240000 0.890000 0.020000
253 v -0.320000 0.890000 0.020000
314 254 vn 0.000000 0.000000 -1.000000
315 255 vn 0.000000 0.000000 -1.000000
316 256 vn 0.000000 0.000000 1.000000
@@ -363,14 +303,74 @@ vn 0.000000 1.000000 0.000000
363 303 vn 0.000000 1.000000 0.000000
364 304 vn 0.000000 -1.000000 0.000000
365 305 vn 0.000000 -1.000000 0.000000
366 v 0.050000 0.050000 -0.170000
367 v 0.150000 0.050000 -0.170000
368 v 0.150000 0.110000 -0.170000
369 v 0.050000 0.110000 -0.170000
370 v 0.050000 0.050000 0.050000
371 v 0.150000 0.050000 0.050000
372 v 0.150000 0.110000 0.050000
373 v 0.050000 0.110000 0.050000
306 v 0.050000 0.050000 -0.050000
307 v 0.150000 0.050000 -0.050000
308 v 0.150000 0.110000 -0.050000
309 v 0.050000 0.110000 -0.050000
310 v 0.050000 0.050000 0.170000
311 v 0.150000 0.050000 0.170000
312 v 0.150000 0.110000 0.170000
313 v 0.050000 0.110000 0.170000
314 vn 0.000000 0.000000 -1.000000
315 vn 0.000000 0.000000 -1.000000
316 vn 0.000000 0.000000 1.000000
317 vn 0.000000 -0.000000 1.000000
318 vn -1.000000 0.000000 0.000000
319 vn -1.000000 0.000000 0.000000
320 vn 1.000000 0.000000 0.000000
321 vn 1.000000 0.000000 0.000000
322 vn 0.000000 1.000000 0.000000
323 vn 0.000000 1.000000 0.000000
324 vn 0.000000 -1.000000 0.000000
325 vn 0.000000 -1.000000 0.000000
326 v -0.160000 0.690000 -0.060000
327 v -0.040000 0.690000 -0.060000
328 v -0.040000 1.110000 -0.060000
329 v -0.160000 1.110000 -0.060000
330 v -0.160000 0.690000 0.060000
331 v -0.040000 0.690000 0.060000
332 v -0.040000 1.110000 0.060000
333 v -0.160000 1.110000 0.060000
334 vn 0.000000 0.000000 -1.000000
335 vn 0.000000 0.000000 -1.000000
336 vn 0.000000 0.000000 1.000000
337 vn 0.000000 -0.000000 1.000000
338 vn -1.000000 0.000000 0.000000
339 vn -1.000000 0.000000 0.000000
340 vn 1.000000 0.000000 0.000000
341 vn 1.000000 0.000000 0.000000
342 vn 0.000000 1.000000 0.000000
343 vn 0.000000 1.000000 0.000000
344 vn 0.000000 -1.000000 0.000000
345 vn 0.000000 -1.000000 0.000000
346 v -0.145000 0.280000 -0.045000
347 v -0.055000 0.280000 -0.045000
348 v -0.055000 0.680000 -0.045000
349 v -0.145000 0.680000 -0.045000
350 v -0.145000 0.280000 0.045000
351 v -0.055000 0.280000 0.045000
352 v -0.055000 0.680000 0.045000
353 v -0.145000 0.680000 0.045000
354 vn 0.000000 0.000000 -1.000000
355 vn 0.000000 0.000000 -1.000000
356 vn 0.000000 0.000000 1.000000
357 vn 0.000000 -0.000000 1.000000
358 vn -1.000000 0.000000 0.000000
359 vn -1.000000 0.000000 0.000000
360 vn 1.000000 0.000000 0.000000
361 vn 1.000000 0.000000 0.000000
362 vn 0.000000 1.000000 0.000000
363 vn 0.000000 1.000000 0.000000
364 vn 0.000000 -1.000000 0.000000
365 vn 0.000000 -1.000000 0.000000
366 v -0.150000 0.050000 -0.050000
367 v -0.050000 0.050000 -0.050000
368 v -0.050000 0.110000 -0.050000
369 v -0.150000 0.110000 -0.050000
370 v -0.150000 0.050000 0.170000
371 v -0.050000 0.050000 0.170000
372 v -0.050000 0.110000 0.170000
373 v -0.150000 0.110000 0.170000
374 374 vn 0.000000 0.000000 -1.000000
375 375 vn 0.000000 0.000000 -1.000000
376 376 vn 0.000000 0.000000 1.000000
@@ -103,86 +103,6 @@ vn 0.000000 1.000000 0.000000
103vn 0.000000 1.000000 0.000000
103vn 0.000000 1.000000 0.000000
104vn 0.000000 -1.000000 0.000000
104vn 0.000000 -1.000000 0.000000
105vn 0.000000 -1.000000 0.000000
105vn 0.000000 -1.000000 0.000000
106v -0.250000 1.340000 -0.040000
107v -0.150000 1.340000 -0.040000
108v -0.150000 1.420000 -0.040000
109v -0.250000 1.420000 -0.040000
110v -0.250000 1.340000 0.040000
111v -0.150000 1.340000 0.040000
112v -0.150000 1.420000 0.040000
113v -0.250000 1.420000 0.040000
114vn 0.000000 0.000000 -1.000000
115vn 0.000000 0.000000 -1.000000
116vn 0.000000 0.000000 1.000000
117vn 0.000000 -0.000000 1.000000
118vn -1.000000 0.000000 0.000000
119vn -1.000000 0.000000 0.000000
120vn 1.000000 0.000000 0.000000
121vn 1.000000 0.000000 0.000000
122vn 0.000000 1.000000 0.000000
123vn 0.000000 1.000000 0.000000
124vn 0.000000 -1.000000 0.000000
125vn 0.000000 -1.000000 0.000000
126v -0.320000 1.240000 -0.040000
127v -0.240000 1.240000 -0.040000
128v -0.240000 1.520000 -0.040000
129v -0.320000 1.520000 -0.040000
130v -0.320000 1.240000 0.040000
131v -0.240000 1.240000 0.040000
132v -0.240000 1.520000 0.040000
133v -0.320000 1.520000 0.040000
134vn 0.000000 0.000000 -1.000000
135vn 0.000000 0.000000 -1.000000
136vn 0.000000 0.000000 1.000000
137vn 0.000000 -0.000000 1.000000
138vn -1.000000 0.000000 0.000000
139vn -1.000000 0.000000 0.000000
140vn 1.000000 0.000000 0.000000
141vn 1.000000 0.000000 0.000000
142vn 0.000000 1.000000 0.000000
143vn 0.000000 1.000000 0.000000
144vn 0.000000 -1.000000 0.000000
145vn 0.000000 -1.000000 0.000000
146v -0.315000 0.970000 -0.035000
147v -0.245000 0.970000 -0.035000
148v -0.245000 1.230000 -0.035000
149v -0.315000 1.230000 -0.035000
150v -0.315000 0.970000 0.035000
151v -0.245000 0.970000 0.035000
152v -0.245000 1.230000 0.035000
153v -0.315000 1.230000 0.035000
154vn 0.000000 0.000000 -1.000000
155vn 0.000000 0.000000 -1.000000
156vn 0.000000 0.000000 1.000000
157vn 0.000000 -0.000000 1.000000
158vn -1.000000 0.000000 0.000000
159vn -1.000000 0.000000 0.000000
160vn 1.000000 0.000000 0.000000
161vn 1.000000 0.000000 0.000000
162vn 0.000000 1.000000 0.000000
163vn 0.000000 1.000000 0.000000
164vn 0.000000 -1.000000 0.000000
165vn 0.000000 -1.000000 0.000000
166v -0.320000 0.790000 -0.020000
167v -0.240000 0.790000 -0.020000
168v -0.240000 0.890000 -0.020000
169v -0.320000 0.890000 -0.020000
170v -0.320000 0.790000 0.020000
171v -0.240000 0.790000 0.020000
172v -0.240000 0.890000 0.020000
173v -0.320000 0.890000 0.020000
174vn 0.000000 0.000000 -1.000000
175vn 0.000000 0.000000 -1.000000
176vn 0.000000 0.000000 1.000000
177vn 0.000000 -0.000000 1.000000
178vn -1.000000 0.000000 0.000000
179vn -1.000000 0.000000 0.000000
180vn 1.000000 0.000000 0.000000
181vn 1.000000 0.000000 0.000000
182vn 0.000000 1.000000 0.000000
183vn 0.000000 1.000000 0.000000
184vn 0.000000 -1.000000 0.000000
185vn 0.000000 -1.000000 0.000000
186v 0.150000 1.340000 -0.040000
106v 0.150000 1.340000 -0.040000
187v 0.250000 1.340000 -0.040000
107v 0.250000 1.340000 -0.040000
188v 0.250000 1.420000 -0.040000
108v 0.250000 1.420000 -0.040000
@@ -263,14 +183,14 @@ vn 0.000000 1.000000 0.000000
263vn 0.000000 1.000000 0.000000
183vn 0.000000 1.000000 0.000000
264vn 0.000000 -1.000000 0.000000
184vn 0.000000 -1.000000 0.000000
265vn 0.000000 -1.000000 0.000000
185vn 0.000000 -1.000000 0.000000
266v -0.160000 0.690000 -0.060000
186v -0.250000 1.340000 -0.040000
267v -0.040000 0.690000 -0.060000
187v -0.150000 1.340000 -0.040000
268v -0.040000 1.110000 -0.060000
188v -0.150000 1.420000 -0.040000
269v -0.160000 1.110000 -0.060000
189v -0.250000 1.420000 -0.040000
270v -0.160000 0.690000 0.060000
190v -0.250000 1.340000 0.040000
271v -0.040000 0.690000 0.060000
191v -0.150000 1.340000 0.040000
272v -0.040000 1.110000 0.060000
192v -0.150000 1.420000 0.040000
273v -0.160000 1.110000 0.060000
193v -0.250000 1.420000 0.040000
274vn 0.000000 0.000000 -1.000000
194vn 0.000000 0.000000 -1.000000
275vn 0.000000 0.000000 -1.000000
195vn 0.000000 0.000000 -1.000000
276vn 0.000000 0.000000 1.000000
196vn 0.000000 0.000000 1.000000
@@ -283,14 +203,34 @@ vn 0.000000 1.000000 0.000000
283vn 0.000000 1.000000 0.000000
203vn 0.000000 1.000000 0.000000
284vn 0.000000 -1.000000 0.000000
204vn 0.000000 -1.000000 0.000000
285vn 0.000000 -1.000000 0.000000
205vn 0.000000 -1.000000 0.000000
286v -0.145000 0.280000 -0.045000
206v -0.320000 1.240000 -0.040000
287v -0.055000 0.280000 -0.045000
207v -0.240000 1.240000 -0.040000
288v -0.055000 0.680000 -0.045000
208v -0.240000 1.520000 -0.040000
289v -0.145000 0.680000 -0.045000
209v -0.320000 1.520000 -0.040000
290v -0.145000 0.280000 0.045000
210v -0.320000 1.240000 0.040000
291v -0.055000 0.280000 0.045000
211v -0.240000 1.240000 0.040000
292v -0.055000 0.680000 0.045000
212v -0.240000 1.520000 0.040000
293v -0.145000 0.680000 0.045000
213v -0.320000 1.520000 0.040000
214vn 0.000000 0.000000 -1.000000
215vn 0.000000 0.000000 -1.000000
216vn 0.000000 0.000000 1.000000
217vn 0.000000 -0.000000 1.000000
218vn -1.000000 0.000000 0.000000
219vn -1.000000 0.000000 0.000000
220vn 1.000000 0.000000 0.000000
221vn 1.000000 0.000000 0.000000
222vn 0.000000 1.000000 0.000000
223vn 0.000000 1.000000 0.000000
224vn 0.000000 -1.000000 0.000000
225vn 0.000000 -1.000000 0.000000
226v -0.315000 0.970000 -0.035000
227v -0.245000 0.970000 -0.035000
228v -0.245000 1.230000 -0.035000
229v -0.315000 1.230000 -0.035000
230v -0.315000 0.970000 0.035000
231v -0.245000 0.970000 0.035000
232v -0.245000 1.230000 0.035000
233v -0.315000 1.230000 0.035000
294vn 0.000000 0.000000 -1.000000
234vn 0.000000 0.000000 -1.000000
295vn 0.000000 0.000000 -1.000000
235vn 0.000000 0.000000 -1.000000
296vn 0.000000 0.000000 1.000000
236vn 0.000000 0.000000 1.000000
@@ -303,14 +243,14 @@ vn 0.000000 1.000000 0.000000
303vn 0.000000 1.000000 0.000000
243vn 0.000000 1.000000 0.000000
304vn 0.000000 -1.000000 0.000000
244vn 0.000000 -1.000000 0.000000
305vn 0.000000 -1.000000 0.000000
245vn 0.000000 -1.000000 0.000000
306v -0.150000 0.050000 -0.170000
246v -0.320000 0.790000 -0.020000
307v -0.050000 0.050000 -0.170000
247v -0.240000 0.790000 -0.020000
308v -0.050000 0.110000 -0.170000
248v -0.240000 0.890000 -0.020000
309v -0.150000 0.110000 -0.170000
249v -0.320000 0.890000 -0.020000
310v -0.150000 0.050000 0.050000
250v -0.320000 0.790000 0.020000
311v -0.050000 0.050000 0.050000
251v -0.240000 0.790000 0.020000
312v -0.050000 0.110000 0.050000
252v -0.240000 0.890000 0.020000
313v -0.150000 0.110000 0.050000
253v -0.320000 0.890000 0.020000
314vn 0.000000 0.000000 -1.000000
254vn 0.000000 0.000000 -1.000000
315vn 0.000000 0.000000 -1.000000
255vn 0.000000 0.000000 -1.000000
316vn 0.000000 0.000000 1.000000
256vn 0.000000 0.000000 1.000000
@@ -363,14 +303,74 @@ vn 0.000000 1.000000 0.000000
363vn 0.000000 1.000000 0.000000
303vn 0.000000 1.000000 0.000000
364vn 0.000000 -1.000000 0.000000
304vn 0.000000 -1.000000 0.000000
365vn 0.000000 -1.000000 0.000000
305vn 0.000000 -1.000000 0.000000
366v 0.050000 0.050000 -0.170000
306v 0.050000 0.050000 -0.050000
367v 0.150000 0.050000 -0.170000
307v 0.150000 0.050000 -0.050000
368v 0.150000 0.110000 -0.170000
308v 0.150000 0.110000 -0.050000
369v 0.050000 0.110000 -0.170000
309v 0.050000 0.110000 -0.050000
370v 0.050000 0.050000 0.050000
310v 0.050000 0.050000 0.170000
371v 0.150000 0.050000 0.050000
311v 0.150000 0.050000 0.170000
372v 0.150000 0.110000 0.050000
312v 0.150000 0.110000 0.170000
373v 0.050000 0.110000 0.050000
313v 0.050000 0.110000 0.170000
314vn 0.000000 0.000000 -1.000000
315vn 0.000000 0.000000 -1.000000
316vn 0.000000 0.000000 1.000000
317vn 0.000000 -0.000000 1.000000
318vn -1.000000 0.000000 0.000000
319vn -1.000000 0.000000 0.000000
320vn 1.000000 0.000000 0.000000
321vn 1.000000 0.000000 0.000000
322vn 0.000000 1.000000 0.000000
323vn 0.000000 1.000000 0.000000
324vn 0.000000 -1.000000 0.000000
325vn 0.000000 -1.000000 0.000000
326v -0.160000 0.690000 -0.060000
327v -0.040000 0.690000 -0.060000
328v -0.040000 1.110000 -0.060000
329v -0.160000 1.110000 -0.060000
330v -0.160000 0.690000 0.060000
331v -0.040000 0.690000 0.060000
332v -0.040000 1.110000 0.060000
333v -0.160000 1.110000 0.060000
334vn 0.000000 0.000000 -1.000000
335vn 0.000000 0.000000 -1.000000
336vn 0.000000 0.000000 1.000000
337vn 0.000000 -0.000000 1.000000
338vn -1.000000 0.000000 0.000000
339vn -1.000000 0.000000 0.000000
340vn 1.000000 0.000000 0.000000
341vn 1.000000 0.000000 0.000000
342vn 0.000000 1.000000 0.000000
343vn 0.000000 1.000000 0.000000
344vn 0.000000 -1.000000 0.000000
345vn 0.000000 -1.000000 0.000000
346v -0.145000 0.280000 -0.045000
347v -0.055000 0.280000 -0.045000
348v -0.055000 0.680000 -0.045000
349v -0.145000 0.680000 -0.045000
350v -0.145000 0.280000 0.045000
351v -0.055000 0.280000 0.045000
352v -0.055000 0.680000 0.045000
353v -0.145000 0.680000 0.045000
354vn 0.000000 0.000000 -1.000000
355vn 0.000000 0.000000 -1.000000
356vn 0.000000 0.000000 1.000000
357vn 0.000000 -0.000000 1.000000
358vn -1.000000 0.000000 0.000000
359vn -1.000000 0.000000 0.000000
360vn 1.000000 0.000000 0.000000
361vn 1.000000 0.000000 0.000000
362vn 0.000000 1.000000 0.000000
363vn 0.000000 1.000000 0.000000
364vn 0.000000 -1.000000 0.000000
365vn 0.000000 -1.000000 0.000000
366v -0.150000 0.050000 -0.050000
367v -0.050000 0.050000 -0.050000
368v -0.050000 0.110000 -0.050000
369v -0.150000 0.110000 -0.050000
370v -0.150000 0.050000 0.170000
371v -0.050000 0.050000 0.170000
372v -0.050000 0.110000 0.170000
373v -0.150000 0.110000 0.170000
374vn 0.000000 0.000000 -1.000000
374vn 0.000000 0.000000 -1.000000
375vn 0.000000 0.000000 -1.000000
375vn 0.000000 0.000000 -1.000000
376vn 0.000000 0.000000 1.000000
376vn 0.000000 0.000000 1.000000
tests/fixtures/bodies/test-body-female.glb Binary file +0 −0

The contents of this binary file are not shown.

tests/fixtures/bodies/test-body-male.fbx Binary file +0 −0

The contents of this binary file are not shown.

tests/fixtures/bodies/test-body-male.glb Binary file +0 −0

The contents of this binary file are not shown.

tests/fixtures/equipment/sword.ppe Binary file +0 −0

The contents of this binary file are not shown.

tests/test_body_assets.rs +29 −5
@@ -203,7 +203,29 @@ fn assert_body(json: &Value, bin: &[u8], sex: &str) {
203 203 assert_eq!(extras["sex"], sex);
204 204 assert_eq!(extras["license"], "CC0-1.0");
205 205 assert_eq!(extras["up"], "+Y");
206 assert_eq!(extras["forward"], "-Z");
206 assert_eq!(extras["forward"], "+Z");
207 // glTF convention: front faces +Z, the character's left side is +X.
208 let node = |name: &str| {
209 json["nodes"]
210 .as_array()
211 .unwrap()
212 .iter()
213 .find(|n| n["name"] == name)
214 .unwrap_or_else(|| panic!("{name} missing"))
215 .clone()
216 };
217 assert!(
218 node("LeftFoot")["translation"][2].as_f64().unwrap() > 0.0,
219 "toes point to +Z"
220 );
221 assert!(
222 node("LeftUpperLeg")["translation"][0].as_f64().unwrap() > 0.0,
223 "left leg on +X"
224 );
225 assert!(
226 node("RightUpperLeg")["translation"][0].as_f64().unwrap() < 0.0,
227 "right leg on -X"
228 );
207 229 let label = extras["label"].as_str().unwrap();
208 230 assert!(
209 231 label.contains("geen definitief karakter"),
@@ -339,14 +361,16 @@ fn male_and_female_test_bodies_have_a_skeleton_and_both_clips() {
339 361 assert_body(&male, &male_bin, "male");
340 362 assert_body(&female, &female_bin, "female");
341 363
342 let male_shoulder = translation(&male, "LeftShoulder")[0];
343 let female_shoulder = translation(&female, "LeftShoulder")[0];
364 let male_shoulder = translation(&male, "LeftShoulder")[0].abs();
365 let female_shoulder = translation(&female, "LeftShoulder")[0].abs();
344 366 assert!(
345 male_shoulder < female_shoulder,
367 male_shoulder > female_shoulder,
346 368 "male shoulders must be wider ({male_shoulder} vs {female_shoulder})"
347 369 );
348 370 assert!(translation(&male, "Hips")[1] > translation(&female, "Hips")[1]);
349 assert!(translation(&female, "LeftUpperLeg")[0] < translation(&male, "LeftUpperLeg")[0]);
371 assert!(
372 translation(&female, "LeftUpperLeg")[0].abs() > translation(&male, "LeftUpperLeg")[0].abs()
373 );
350 374
351 375 let readme =
352 376 std::fs::read_to_string(repo_root().join("tests/fixtures/bodies/README.md")).unwrap();
@@ -203,7 +203,29 @@ fn assert_body(json: &Value, bin: &[u8], sex: &str) {
203 assert_eq!(extras["sex"], sex);
203 assert_eq!(extras["sex"], sex);
204 assert_eq!(extras["license"], "CC0-1.0");
204 assert_eq!(extras["license"], "CC0-1.0");
205 assert_eq!(extras["up"], "+Y");
205 assert_eq!(extras["up"], "+Y");
206 assert_eq!(extras["forward"], "-Z");
206 assert_eq!(extras["forward"], "+Z");
207 // glTF convention: front faces +Z, the character's left side is +X.
208 let node = |name: &str| {
209 json["nodes"]
210 .as_array()
211 .unwrap()
212 .iter()
213 .find(|n| n["name"] == name)
214 .unwrap_or_else(|| panic!("{name} missing"))
215 .clone()
216 };
217 assert!(
218 node("LeftFoot")["translation"][2].as_f64().unwrap() > 0.0,
219 "toes point to +Z"
220 );
221 assert!(
222 node("LeftUpperLeg")["translation"][0].as_f64().unwrap() > 0.0,
223 "left leg on +X"
224 );
225 assert!(
226 node("RightUpperLeg")["translation"][0].as_f64().unwrap() < 0.0,
227 "right leg on -X"
228 );
207 let label = extras["label"].as_str().unwrap();
229 let label = extras["label"].as_str().unwrap();
208 assert!(
230 assert!(
209 label.contains("geen definitief karakter"),
231 label.contains("geen definitief karakter"),
@@ -339,14 +361,16 @@ fn male_and_female_test_bodies_have_a_skeleton_and_both_clips() {
339 assert_body(&male, &male_bin, "male");
361 assert_body(&male, &male_bin, "male");
340 assert_body(&female, &female_bin, "female");
362 assert_body(&female, &female_bin, "female");
341
363
342 let male_shoulder = translation(&male, "LeftShoulder")[0];
364 let male_shoulder = translation(&male, "LeftShoulder")[0].abs();
343 let female_shoulder = translation(&female, "LeftShoulder")[0];
365 let female_shoulder = translation(&female, "LeftShoulder")[0].abs();
344 assert!(
366 assert!(
345 male_shoulder < female_shoulder,
367 male_shoulder > female_shoulder,
346 "male shoulders must be wider ({male_shoulder} vs {female_shoulder})"
368 "male shoulders must be wider ({male_shoulder} vs {female_shoulder})"
347 );
369 );
348 assert!(translation(&male, "Hips")[1] > translation(&female, "Hips")[1]);
370 assert!(translation(&male, "Hips")[1] > translation(&female, "Hips")[1]);
349 assert!(translation(&female, "LeftUpperLeg")[0] < translation(&male, "LeftUpperLeg")[0]);
371 assert!(
372 translation(&female, "LeftUpperLeg")[0].abs() > translation(&male, "LeftUpperLeg")[0].abs()
373 );
350
374
351 let readme =
375 let readme =
352 std::fs::read_to_string(repo_root().join("tests/fixtures/bodies/README.md")).unwrap();
376 std::fs::read_to_string(repo_root().join("tests/fixtures/bodies/README.md")).unwrap();