]> Untitled Git - william-skin.git/blob - TrackingBone3D_head.gd
Disabled jigglebones for now, since they use set_bone_global_pose_override() which...
[william-skin.git] / TrackingBone3D_head.gd
1 @tool
2
3 class_name TrackingBone3D_head
4 extends SkeletonModifier3D
5
6 @export_enum(" ") var bone: String
7 @export var target: Vector3 = Vector3(0, 0, 0)
8 @export var influence_lerp_seconds: float = 0.5
9 @export var rotation_lerp_weight: float = 0.5
10
11 var _skeleton: Skeleton3D
12 var _bone_idx: int
13
14 var old_current_pose: Transform3D = Transform3D.IDENTITY
15
16 func _validate_property(property: Dictionary) -> void:
17         if property.name == "bone":
18                 var skeleton: Skeleton3D = get_skeleton()
19                 if skeleton:
20                         property.hint = PROPERTY_HINT_ENUM
21                         property.hint_string = skeleton.get_concatenated_bone_names()
22
23
24 func _ready() -> void:
25         # for tweening influence
26         influence = 0
27         
28         # for tweening bone poses
29         _skeleton = get_skeleton()
30         if not _skeleton:
31                 push_error("Expected a skeleton!")
32         _bone_idx = _skeleton.find_bone(bone)
33         
34         # for smooth rotations
35         old_current_pose = _skeleton.global_transform * _skeleton.get_bone_global_pose(_bone_idx)
36
37
38 func tween_influence(goal_weight: float) -> void:
39         create_tween().tween_method(
40                 _interpolate_influence, influence, goal_weight, influence_lerp_seconds
41                 ).set_trans(Tween.TRANS_EXPO)
42
43
44 func _interpolate_influence(weight: float) -> void:
45         influence = weight
46
47
48
49 func _process_modification() -> void:
50         var current_pose: Transform3D = _skeleton.global_transform * _skeleton.get_bone_global_pose(_bone_idx)
51         current_pose.basis = old_current_pose.basis
52         var target_pose: Transform3D  = current_pose.looking_at(target, Vector3.UP)
53         
54         # TODO: replace this specific code with general purpose modifier
55         var axis: Vector3 = target_pose.basis.y
56         target_pose = target_pose.rotated(axis, PI)
57         
58         # https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
59         if rotation_lerp_weight > 0:
60                 var from := Quaternion(current_pose.basis.orthonormalized())
61                 var to := Quaternion(target_pose.basis.orthonormalized())
62                 var rot_target := from.slerp(to, rotation_lerp_weight)
63                 target_pose.basis = Basis(rot_target)
64         
65         # remember this!
66         old_current_pose = target_pose
67         
68         # yield until target pose has changed, then apply it?
69         _skeleton.set_bone_global_pose(
70                 _bone_idx,
71                 Transform3D(
72                         (_skeleton.global_transform.affine_inverse() * target_pose).basis.orthonormalized(), 
73                         _skeleton.get_bone_global_pose(_bone_idx).origin
74                         )
75                 )