extends SkeletonModifier3D
@export_enum(" ") var bone: String
-@export var target: Vector3 = Vector3(0, 0, 0)
+@export var target: Node3D = null
+@export var influence_lerp_seconds: float = 0.5
+@export var rotation_lerp_weight: float = 0.5
+var _skeleton: Skeleton3D
+var _bone_idx: int
+
+var old_current_pose: Transform3D = Transform3D.IDENTITY
func _validate_property(property: Dictionary) -> void:
if property.name == "bone":
property.hint_string = skeleton.get_concatenated_bone_names()
+func _ready() -> void:
+ # for tweening influence
+ influence = 0
+
+ # for tweening bone poses
+ _skeleton = get_skeleton()
+ if not _skeleton:
+ push_error("Expected a skeleton!")
+ _bone_idx = _skeleton.find_bone(bone)
+
+ # for smooth rotations
+ old_current_pose = _skeleton.global_transform * _skeleton.get_bone_global_pose(_bone_idx)
+
+
+func tween_influence(goal_weight: float) -> void:
+ create_tween().tween_method(
+ _interpolate_influence, influence, goal_weight, influence_lerp_seconds
+ ).set_trans(Tween.TRANS_EXPO)
+
+
+func _interpolate_influence(weight: float) -> void:
+ influence = weight
+
+
+
func _process_modification() -> void:
- var skeleton: Skeleton3D = get_skeleton()
- if !skeleton:
- return # Never happen, but for the safety.
- var bone_idx: int = skeleton.find_bone(bone)
- var _parent_idx: int = skeleton.get_bone_parent(bone_idx)
- var pose: Transform3D = skeleton.global_transform * skeleton.get_bone_global_pose(bone_idx)
+ if target == null:
+ return
+
+ var current_pose: Transform3D = _skeleton.global_transform * _skeleton.get_bone_global_pose(_bone_idx)
+ current_pose.basis = old_current_pose.basis
+ var target_pose: Transform3D = current_pose.looking_at(target.global_position, Vector3.UP)
+
+ # TODO: replace this specific code with general purpose modifier
+ var axis: Vector3 = target_pose.basis.y
+ target_pose = target_pose.rotated(axis, PI)
+
+ # https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
+ if rotation_lerp_weight > 0:
+ var from := Quaternion(current_pose.basis.orthonormalized())
+ var to := Quaternion(target_pose.basis.orthonormalized())
+ var rot_target := from.slerp(to, rotation_lerp_weight)
+ target_pose.basis = Basis(rot_target)
- #var looked_at: Transform3D = _w_look_at(pose, target)
- var looked_at: Transform3D = pose.looking_at(target, Vector3.UP)
- var axis: Vector3 = looked_at.basis.y
- looked_at = looked_at.rotated(axis, PI)
+ # remember this!
+ old_current_pose = target_pose
- skeleton.set_bone_global_pose(
- bone_idx,
+ # yield until target pose has changed, then apply it?
+ _skeleton.set_bone_global_pose(
+ _bone_idx,
Transform3D(
- (skeleton.global_transform.affine_inverse() * looked_at).basis.orthonormalized(),
- skeleton.get_bone_global_pose(bone_idx).origin
+ (_skeleton.global_transform.affine_inverse() * target_pose).basis.orthonormalized(),
+ _skeleton.get_bone_global_pose(_bone_idx).origin
)
)