Narrow the pose-capture discrepancy to the harness and lock bone posing
Full build / Import + headless tests (push) Successful in 45s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-03 11:24:44 +01:00
1 parent 7a5b902797
commit 1d4f718376
5 files changed
+63 -26

No files matched your search

-2
View File
@@ -261,8 +261,6 @@ func _shoot() -> void:
while FileAccess.file_exists(path):
path = dir.path_join("%s-v%d.png" % [base, index])
index += 1
var hip_bone: int = int(_avatar._bone_indices.get("HipL", -1))
var knee_bone: int = int(_avatar._bone_indices.get("KneeL", -1))
var img: Image = root.get_texture().get_image()
var err := img.save_png(path)
print("%s -> err=%d" % [base, err])
+32
View File
@@ -147,6 +147,38 @@ func test_skinned_model_loads_and_keeps_the_head_contract() -> bool:
avatar.free()
return a and b and c
## The pose harness captures frames, but a regression here is cheap to catch:
## the pose logic must reach the skeleton bones, not only the proxy pivots.
func test_pose_reaches_the_skeleton_bones() -> bool:
var avatar := _avatar(Rules.NpcType.TOURIST, 55)
var sk := avatar._skeleton
var hip: int = int(avatar._bone_indices["HipL"])
var knee: int = int(avatar._bone_indices["KneeL"])
var rest_hip := sk.get_bone_rest(hip).basis.get_rotation_quaternion()
var rest_knee := sk.get_bone_rest(knee).basis.get_rotation_quaternion()
avatar.arrived = true
avatar.npc.intent = "sit"
for _i in 45:
avatar._animate(1.0 / 60.0)
var sit_hip := sk.get_bone_pose_rotation(hip)
var sit_knee := sk.get_bone_pose_rotation(knee)
var a := TestUtils.check(not sit_hip.is_equal_approx(rest_hip),
"seated pose rotates the HipL bone away from rest")
var b := TestUtils.check(not sit_knee.is_equal_approx(rest_knee),
"seated pose rotates the KneeL bone away from rest")
var c := TestUtils.check(avatar._rig.position.y < -0.25,
"seated pose lowers the rig (%.3f)" % avatar._rig.position.y)
# Returning to idle must restore the rest orientation, not leave the sit pose.
avatar.arrived = false
avatar.npc.intent = "roam"
avatar.velocity = Vector3.ZERO
for _i in 120:
avatar._animate(1.0 / 60.0)
var d := TestUtils.check(sk.get_bone_pose_rotation(hip).is_equal_approx(rest_hip),
"idle restores the HipL rest orientation")
avatar.free()
return a and b and c and d
func test_character_selection_is_deterministic_and_complete() -> bool:
var expected := {
Rules.NpcType.TOURIST: "npc_casual",