diff --git a/src/game/npc_avatar.gd b/src/game/npc_avatar.gd index cd49a5a..1d2ed18 100644 --- a/src/game/npc_avatar.gd +++ b/src/game/npc_avatar.gd @@ -413,38 +413,38 @@ func _build_skinned_body(npc_type: int) -> bool: # Animation proxies: the existing _animate()/idle code drives these Node3D # pivots; _sync_skeleton() copies their rotations onto the armature bones. + _bind_bones() _head = Node3D.new() _head.name = "Head" - _head.position = Vector3(0, 1.62, 0) + _head.position = _rest_origin(["Head"]) _rig.add_child(_head) for side in [-1.0, 1.0]: var suffix := "L" if side < 0.0 else "R" var hip := Node3D.new() hip.name = "Hip" + suffix - hip.position = Vector3(0.13 * side, 0.64, 0) + hip.position = _rest_origin(["Hip" + suffix, "UpperLeg." + suffix]) _rig.add_child(hip) var knee := Node3D.new() knee.name = "Knee" - knee.position = Vector3(0, -0.34, 0) + knee.position = _rest_origin(["Knee" + suffix, "LowerLeg." + suffix]) - hip.position hip.add_child(knee) _legs.append(hip) _knees.append(knee) var shoulder := Node3D.new() shoulder.name = "Shoulder" + suffix - shoulder.position = Vector3(0.27 * side, 1.38, 0) + shoulder.position = _rest_origin(["Shoulder" + suffix, "UpperArm." + suffix]) _rig.add_child(shoulder) var elbow := Node3D.new() elbow.name = "Elbow" - elbow.position = Vector3(0, -0.30, 0) + elbow.position = _rest_origin(["Elbow" + suffix, "LowerArm." + suffix]) - shoulder.position shoulder.add_child(elbow) var hand := Node3D.new() hand.name = "Hand" - hand.position = Vector3(0, -0.30, 0.02) + hand.position = _rest_origin(["Hand" + suffix, "Wrist." + suffix]) - elbow.position elbow.add_child(hand) _arms.append(shoulder) _elbows.append(elbow) _hands.append(hand) - _bind_bones() _add_accessory(str(outfit["accessory"]), cloth) _add_character_gear() @@ -495,27 +495,42 @@ func _bind_bones() -> void: for index in _skeleton.get_bone_count(): _bone_indices[_skeleton.get_bone_name(index)] = index +## First-found rest origin for a list of candidate bone names. Lets the proxy +## pivots track the actual skeleton (old 13-core or Quaternius 62-joint). +func _rest_origin(names: Array) -> Vector3: + if _skeleton == null: + return Vector3.ZERO + for bone_name in names: + if _bone_indices.has(bone_name): + return _skeleton.get_bone_rest(_bone_indices[bone_name]).origin + return Vector3.ZERO + ## Copy the proxy pivot rotations onto armature bones. Limb bones point down, so ## an X rotation swings them forward/back; the Head bone points up, so a Y -## rotation is the look yaw. +## rotation is the look yaw. The Quaternius rig uses dot-suffixed names +## (UpperLeg.L, UpperArm.L, ...); the old 13-core rig uses HipL/ShoulderL, so +## both are tried and the first one present in the skeleton wins. func _sync_skeleton() -> void: if _skeleton == null or _bone_indices.is_empty(): return - var names := ["HipL", "HipR"] for i in 2: - _pose_bone(names[i], _legs[i].rotation.x, Vector3.RIGHT) - names = ["KneeL", "KneeR"] + _pose_bone_any(["UpperLeg.L", "HipL"], ["UpperLeg.R", "HipR"], i, _legs[i].rotation.x) for i in 2: - _pose_bone(names[i], _knees[i].rotation.x, Vector3.RIGHT) - names = ["ShoulderL", "ShoulderR"] + _pose_bone_any(["LowerLeg.L", "KneeL"], ["LowerLeg.R", "KneeR"], i, _knees[i].rotation.x) for i in 2: - _pose_bone(names[i], _arms[i].rotation.x, Vector3.RIGHT) - names = ["ElbowL", "ElbowR"] + _pose_bone_any(["UpperArm.L", "ShoulderL"], ["UpperArm.R", "ShoulderR"], i, _arms[i].rotation.x) for i in 2: - _pose_bone(names[i], _elbows[i].rotation.x, Vector3.RIGHT) + _pose_bone_any(["LowerArm.L", "ElbowL"], ["LowerArm.R", "ElbowR"], i, _elbows[i].rotation.x) if _head != null: _pose_bone("Head", _head.rotation.y, Vector3.UP) +func _pose_bone_any(left: Array, right: Array, index: int, angle: float) -> void: + var names: Array = left if index == 0 else right + for bone_name in names: + if _bone_indices.has(bone_name): + _pose_bone(bone_name, angle, Vector3.RIGHT) + return + func _pose_bone(bone_name: String, angle: float, axis: Vector3) -> void: if not _bone_indices.has(bone_name): return