Feat/14 movement overhaul #20

Merged
Dotts merged 86 commits from feat/14-movement-overhaul into main 2026-07-17 10:44:23 -07:00
Showing only changes of commit 6e73fd7703 - Show all commits
+14 -28
View File
@@ -461,17 +461,14 @@ func _warn_missing(anim_name: String) -> void:
func _process(delta: float) -> void: func _process(delta: float) -> void:
if not skeleton: if not skeleton:
print("SkinnedPlayerModel: _process - no skeleton!")
return return
# Get movement state from parent PlayerMovementController # Get movement state from parent PlayerMovementController
var parent = get_parent() var parent = get_parent()
if not parent: if not parent:
print("SkinnedPlayerModel: _process - no parent!")
return return
var sm = parent.get_node_or_null("MovementStateMachine") var sm = parent.get_node_or_null("MovementStateMachine")
if not sm: if not sm:
print("SkinnedPlayerModel: _process - no MovementStateMachine!")
return return
var state = sm.current_state var state = sm.current_state
@@ -539,7 +536,6 @@ func _process(delta: float) -> void:
_anim_time = min(_anim_time, length) _anim_time = min(_anim_time, length)
# Apply procedural animation to skeleton # Apply procedural animation to skeleton
_apply_procedural_animation(data, _anim_time) _apply_procedural_animation(data, _anim_time)
print("SkinnedPlayerModel: applied '%s' t=%.2f" % [_current_anim_name, _anim_time])
else: else:
# For non-procedural anims (Death, Crouch from GLB), use AP # For non-procedural anims (Death, Crouch from GLB), use AP
if animation_player: if animation_player:
@@ -558,7 +554,7 @@ func _process(delta: float) -> void:
func _apply_procedural_animation(data: Dictionary, time: float) -> void: func _apply_procedural_animation(data: Dictionary, time: float) -> void:
## Applies procedural animation data to the skeleton at the given time. ## Applies procedural animation data to the skeleton at the given time.
## Rotates bones around their anatomical swing axis (world X for locomotion). ## Sets local pose = rest_local * offset_quat for correct skinning.
if not skeleton: if not skeleton:
return return
var tracks = data["tracks"] var tracks = data["tracks"]
@@ -590,28 +586,18 @@ func _apply_procedural_animation(data: Dictionary, time: float) -> void:
if prev_kf.has("rot") and next_kf.has("rot"): if prev_kf.has("rot") and next_kf.has("rot"):
var rot: Vector3 = lerp(prev_kf["rot"], next_kf["rot"], t) var rot: Vector3 = lerp(prev_kf["rot"], next_kf["rot"], t)
# Get rest global pose # Compute offset quaternion from euler angles
var rest_global = skeleton.get_bone_global_pose(bone_idx) var offset_quat = Quaternion.from_euler(rot)
# Apply rotation around the bone's swing axis # Get rest pose local rotation for this bone
# rot.x = forward/back swing (rotation around world X) var rest_quat: Quaternion = _bone_rest_local_rot.get(bone_idx, Quaternion.IDENTITY)
# rot.y = twist (rotation around bone's forward axis) # local_pose = rest_local * offset (so final = rest * offset)
# rot.z = lateral (rotation around world Z) var new_local_quat = rest_quat * offset_quat
var new_basis = rest_global.basis skeleton.set_bone_pose_rotation(bone_idx, new_local_quat)
if abs(rot.x) > 0.001: # Keep rest pose local position
# Forward/back swing: rotate around world X axis var rest_pos: Vector3 = _bone_rest_local_pos.get(bone_idx, Vector3.ZERO)
var swing = Basis(Vector3(1, 0, 0), rot.x) skeleton.set_bone_pose_position(bone_idx, rest_pos)
new_basis = swing * new_basis
if abs(rot.y) > 0.001:
# Twist: rotate around the bone's Y axis (in world space)
var bone_y = new_basis.y
var twist = Basis(bone_y, rot.y)
new_basis = twist * new_basis
if abs(rot.z) > 0.001:
# Lateral: rotate around world Z
var lateral = Basis(Vector3(0, 0, 1), rot.z)
new_basis = lateral * new_basis
var new_global = Transform3D(new_basis, rest_global.origin)
skeleton.set_bone_global_pose_override(bone_idx, new_global, 1.0, true)
elif prev_kf.has("pos") and next_kf.has("pos"): elif prev_kf.has("pos") and next_kf.has("pos"):
var pos: Vector3 = lerp(prev_kf["pos"], next_kf["pos"], t) var pos: Vector3 = lerp(prev_kf["pos"], next_kf["pos"], t)
skeleton.set_bone_pose_position(bone_idx, pos) # Position is additive on top of rest pose position
var rest_pos: Vector3 = _bone_rest_local_pos.get(bone_idx, Vector3.ZERO)
skeleton.set_bone_pose_position(bone_idx, rest_pos + pos)