1 | /**************************************************************************/ |
2 | /* skeleton_ik_3d.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef SKELETON_IK_3D_H |
32 | #define SKELETON_IK_3D_H |
33 | |
34 | #ifndef _3D_DISABLED |
35 | |
36 | #include "scene/3d/skeleton_3d.h" |
37 | |
38 | class FabrikInverseKinematic { |
39 | struct EndEffector { |
40 | BoneId tip_bone; |
41 | Transform3D goal_transform; |
42 | }; |
43 | |
44 | struct ChainItem { |
45 | Vector<ChainItem> children; |
46 | ChainItem *parent_item = nullptr; |
47 | |
48 | // Bone info |
49 | BoneId bone = -1; |
50 | |
51 | real_t length = 0.0; |
52 | /// Positions relative to root bone |
53 | Transform3D initial_transform; |
54 | Vector3 current_pos; |
55 | // Direction from this bone to child |
56 | Vector3 current_ori; |
57 | |
58 | ChainItem *find_child(const BoneId p_bone_id); |
59 | ChainItem *add_child(const BoneId p_bone_id); |
60 | }; |
61 | |
62 | struct ChainTip { |
63 | ChainItem *chain_item = nullptr; |
64 | const EndEffector *end_effector = nullptr; |
65 | |
66 | ChainTip() {} |
67 | |
68 | ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : |
69 | chain_item(p_chain_item), |
70 | end_effector(p_end_effector) {} |
71 | }; |
72 | |
73 | struct Chain { |
74 | ChainItem chain_root; |
75 | ChainItem *middle_chain_item = nullptr; |
76 | Vector<ChainTip> tips; |
77 | Vector3 magnet_position; |
78 | }; |
79 | |
80 | public: |
81 | struct Task { |
82 | RID self; |
83 | Skeleton3D *skeleton = nullptr; |
84 | |
85 | Chain chain; |
86 | |
87 | // Settings |
88 | real_t min_distance = 0.01; |
89 | int max_iterations = 10; |
90 | |
91 | // Bone data |
92 | BoneId root_bone = -1; |
93 | Vector<EndEffector> end_effectors; |
94 | |
95 | Transform3D goal_global_transform; |
96 | |
97 | Task() {} |
98 | }; |
99 | |
100 | private: |
101 | /// Init a chain that starts from the root to tip |
102 | static bool build_chain(Task *p_task, bool p_force_simple_chain = true); |
103 | |
104 | static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos); |
105 | /// Special solvers that solve only chains with one end effector |
106 | static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet); |
107 | static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos); |
108 | |
109 | public: |
110 | static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform); |
111 | static void free_task(Task *p_task); |
112 | // The goal of chain should be always in local space |
113 | static void set_goal(Task *p_task, const Transform3D &p_goal); |
114 | static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta); |
115 | static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); |
116 | |
117 | static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item); |
118 | }; |
119 | |
120 | class SkeletonIK3D : public Node { |
121 | GDCLASS(SkeletonIK3D, Node); |
122 | |
123 | StringName root_bone; |
124 | StringName tip_bone; |
125 | real_t interpolation = 1.0; |
126 | Transform3D target; |
127 | NodePath target_node_path_override; |
128 | bool override_tip_basis = true; |
129 | bool use_magnet = false; |
130 | Vector3 magnet_position; |
131 | |
132 | real_t min_distance = 0.01; |
133 | int max_iterations = 10; |
134 | |
135 | Variant skeleton_ref = Variant(); |
136 | Variant target_node_override_ref = Variant(); |
137 | FabrikInverseKinematic::Task *task = nullptr; |
138 | |
139 | protected: |
140 | void _validate_property(PropertyInfo &p_property) const; |
141 | |
142 | static void _bind_methods(); |
143 | virtual void _notification(int p_what); |
144 | |
145 | public: |
146 | SkeletonIK3D(); |
147 | virtual ~SkeletonIK3D(); |
148 | |
149 | void set_root_bone(const StringName &p_root_bone); |
150 | StringName get_root_bone() const; |
151 | |
152 | void set_tip_bone(const StringName &p_tip_bone); |
153 | StringName get_tip_bone() const; |
154 | |
155 | void set_interpolation(real_t p_interpolation); |
156 | real_t get_interpolation() const; |
157 | |
158 | void set_target_transform(const Transform3D &p_target); |
159 | const Transform3D &get_target_transform() const; |
160 | |
161 | void set_target_node(const NodePath &p_node); |
162 | NodePath get_target_node(); |
163 | |
164 | void set_override_tip_basis(bool p_override); |
165 | bool is_override_tip_basis() const; |
166 | |
167 | void set_use_magnet(bool p_use); |
168 | bool is_using_magnet() const; |
169 | |
170 | void set_magnet_position(const Vector3 &p_local_position); |
171 | const Vector3 &get_magnet_position() const; |
172 | |
173 | void set_min_distance(real_t p_min_distance); |
174 | real_t get_min_distance() const { return min_distance; } |
175 | |
176 | void set_max_iterations(int p_iterations); |
177 | int get_max_iterations() const { return max_iterations; } |
178 | |
179 | Skeleton3D *get_parent_skeleton() const; |
180 | |
181 | bool is_running(); |
182 | |
183 | void start(bool p_one_time = false); |
184 | void stop(); |
185 | |
186 | private: |
187 | Transform3D _get_target_transform(); |
188 | void reload_chain(); |
189 | void reload_goal(); |
190 | void _solve_chain(); |
191 | }; |
192 | |
193 | #endif // _3D_DISABLED |
194 | |
195 | #endif // SKELETON_IK_3D_H |
196 | |