Feat/14 movement overhaul #20
@@ -32,6 +32,8 @@ var _procedural_anims: Dictionary = {} # name -> {length, loop, tracks}
|
||||
|
||||
## Bone indices for procedural animation (cached at load time)
|
||||
var _bone_indices: Dictionary = {} # bone_name -> index
|
||||
var _bone_rest_local_rot: Dictionary = {} # bone_idx -> Quaternion (rest local rotation)
|
||||
var _bone_rest_local_pos: Dictionary = {} # bone_idx -> Vector3 (rest local position)
|
||||
|
||||
func _ready() -> void:
|
||||
if model_path != "":
|
||||
@@ -43,6 +45,8 @@ func load_model(path: String) -> void:
|
||||
_bone_cache.clear()
|
||||
_bone_indices.clear()
|
||||
_procedural_anims.clear()
|
||||
_bone_rest_local_rot.clear()
|
||||
_bone_rest_local_pos.clear()
|
||||
_current_anim_name = ""
|
||||
_anim_time = 0.0
|
||||
|
||||
@@ -92,6 +96,10 @@ func load_model(path: String) -> void:
|
||||
for i in range(skeleton.get_bone_count()):
|
||||
_bone_cache[skeleton.get_bone_name(i)] = i
|
||||
_bone_indices[skeleton.get_bone_name(i)] = i
|
||||
# Cache rest pose local transforms
|
||||
var rest = skeleton.get_bone_rest(i)
|
||||
_bone_rest_local_rot[i] = rest.basis.get_rotation_quaternion()
|
||||
_bone_rest_local_pos[i] = rest.origin
|
||||
else:
|
||||
print("SkinnedPlayerModel: WARNING - no skeleton found")
|
||||
|
||||
@@ -508,9 +516,8 @@ func _process(delta: float) -> void:
|
||||
# Reset all bones to rest pose before applying procedural animation
|
||||
if skeleton:
|
||||
for i in range(skeleton.get_bone_count()):
|
||||
skeleton.set_bone_pose_position(i, Vector3.ZERO)
|
||||
skeleton.set_bone_pose_rotation(i, Quaternion.IDENTITY)
|
||||
skeleton.set_bone_pose_scale(i, Vector3.ONE)
|
||||
skeleton.set_bone_pose_rotation(i, _bone_rest_local_rot.get(i, Quaternion.IDENTITY))
|
||||
skeleton.set_bone_pose_position(i, _bone_rest_local_pos.get(i, Vector3.ZERO))
|
||||
elif animation_player and animation_player.has_animation(target_anim):
|
||||
animation_player.active = true
|
||||
animation_player.play(target_anim)
|
||||
@@ -545,7 +552,7 @@ func _process(delta: float) -> void:
|
||||
|
||||
func _apply_procedural_animation(data: Dictionary, time: float) -> void:
|
||||
## Applies procedural animation data to the skeleton at the given time.
|
||||
## Uses global pose override to set bone transforms relative to their rest pose.
|
||||
## Rotates bones around their anatomical swing axis (world X for locomotion).
|
||||
if not skeleton:
|
||||
return
|
||||
var tracks = data["tracks"]
|
||||
@@ -577,12 +584,27 @@ func _apply_procedural_animation(data: Dictionary, time: float) -> void:
|
||||
|
||||
if prev_kf.has("rot") and next_kf.has("rot"):
|
||||
var rot: Vector3 = lerp(prev_kf["rot"], next_kf["rot"], t)
|
||||
# Get the bone's rest global pose
|
||||
# Get rest global pose
|
||||
var rest_global = skeleton.get_bone_global_pose(bone_idx)
|
||||
# Apply rotation offset around world axes (pre-multiply)
|
||||
# For locomotion: X = forward/back swing, Y = twist, Z = lateral
|
||||
var world_basis = Basis(Quaternion.from_euler(rot))
|
||||
var new_global = Transform3D(world_basis * rest_global.basis, rest_global.origin)
|
||||
# Apply rotation around the bone's swing axis
|
||||
# rot.x = forward/back swing (rotation around world X)
|
||||
# rot.y = twist (rotation around bone's forward axis)
|
||||
# rot.z = lateral (rotation around world Z)
|
||||
var new_basis = rest_global.basis
|
||||
if abs(rot.x) > 0.001:
|
||||
# Forward/back swing: rotate around world X axis
|
||||
var swing = Basis(Vector3(1, 0, 0), rot.x)
|
||||
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"):
|
||||
var pos: Vector3 = lerp(prev_kf["pos"], next_kf["pos"], t)
|
||||
|
||||
Reference in New Issue
Block a user