1 | /**************************************************************************/ |
2 | /* skeleton_modification_2d_fabrik.cpp */ |
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 | #include "skeleton_modification_2d_fabrik.h" |
32 | #include "scene/2d/skeleton_2d.h" |
33 | |
34 | #ifdef TOOLS_ENABLED |
35 | #include "editor/editor_settings.h" |
36 | #endif // TOOLS_ENABLED |
37 | |
38 | bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant &p_value) { |
39 | String path = p_path; |
40 | |
41 | if (path.begins_with("joint_data/" )) { |
42 | int which = path.get_slicec('/', 1).to_int(); |
43 | String what = path.get_slicec('/', 2); |
44 | ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); |
45 | |
46 | if (what == "bone2d_node" ) { |
47 | set_fabrik_joint_bone2d_node(which, p_value); |
48 | } else if (what == "bone_index" ) { |
49 | set_fabrik_joint_bone_index(which, p_value); |
50 | } else if (what == "magnet_position" ) { |
51 | set_fabrik_joint_magnet_position(which, p_value); |
52 | } else if (what == "use_target_rotation" ) { |
53 | set_fabrik_joint_use_target_rotation(which, p_value); |
54 | } |
55 | } |
56 | |
57 | return true; |
58 | } |
59 | |
60 | bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret) const { |
61 | String path = p_path; |
62 | |
63 | if (path.begins_with("joint_data/" )) { |
64 | int which = path.get_slicec('/', 1).to_int(); |
65 | String what = path.get_slicec('/', 2); |
66 | ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); |
67 | |
68 | if (what == "bone2d_node" ) { |
69 | r_ret = get_fabrik_joint_bone2d_node(which); |
70 | } else if (what == "bone_index" ) { |
71 | r_ret = get_fabrik_joint_bone_index(which); |
72 | } else if (what == "magnet_position" ) { |
73 | r_ret = get_fabrik_joint_magnet_position(which); |
74 | } else if (what == "use_target_rotation" ) { |
75 | r_ret = get_fabrik_joint_use_target_rotation(which); |
76 | } |
77 | return true; |
78 | } |
79 | return true; |
80 | } |
81 | |
82 | void SkeletonModification2DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const { |
83 | for (int i = 0; i < fabrik_data_chain.size(); i++) { |
84 | String base_string = "joint_data/" + itos(i) + "/" ; |
85 | |
86 | p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
87 | p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D" , PROPERTY_USAGE_DEFAULT)); |
88 | |
89 | if (i > 0) { |
90 | p_list->push_back(PropertyInfo(Variant::VECTOR2, base_string + "magnet_position" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
91 | } |
92 | if (i == fabrik_data_chain.size() - 1) { |
93 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_rotation" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
94 | } |
95 | } |
96 | } |
97 | |
98 | void SkeletonModification2DFABRIK::_execute(float p_delta) { |
99 | ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, |
100 | "Modification is not setup and therefore cannot execute!" ); |
101 | if (!enabled) { |
102 | return; |
103 | } |
104 | |
105 | if (target_node_cache.is_null()) { |
106 | WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..." ); |
107 | update_target_cache(); |
108 | return; |
109 | } |
110 | |
111 | if (fabrik_data_chain.size() <= 1) { |
112 | ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!" ); |
113 | return; |
114 | } |
115 | |
116 | Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); |
117 | if (!target || !target->is_inside_tree()) { |
118 | ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!" ); |
119 | return; |
120 | } |
121 | target_global_pose = target->get_global_transform(); |
122 | |
123 | if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { |
124 | fabrik_joint_update_bone2d_cache(0); |
125 | WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..." ); |
126 | } |
127 | |
128 | Bone2D *origin_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); |
129 | if (!origin_bone2d_node || !origin_bone2d_node->is_inside_tree()) { |
130 | ERR_PRINT_ONCE("Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!" ); |
131 | return; |
132 | } |
133 | |
134 | origin_global_pose = origin_bone2d_node->get_global_transform(); |
135 | |
136 | if (fabrik_transform_chain.size() != fabrik_data_chain.size()) { |
137 | fabrik_transform_chain.resize(fabrik_data_chain.size()); |
138 | } |
139 | |
140 | for (int i = 0; i < fabrik_data_chain.size(); i++) { |
141 | // Update the transform chain |
142 | if (fabrik_data_chain[i].bone2d_node_cache.is_null() && !fabrik_data_chain[i].bone2d_node.is_empty()) { |
143 | WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..." ); |
144 | fabrik_joint_update_bone2d_cache(i); |
145 | } |
146 | Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); |
147 | if (!joint_bone2d_node) { |
148 | ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!" ); |
149 | return; |
150 | } |
151 | fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform(); |
152 | } |
153 | |
154 | Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache)); |
155 | float final_bone2d_angle = final_bone2d_node->get_global_rotation(); |
156 | if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { |
157 | final_bone2d_angle = target_global_pose.get_rotation(); |
158 | } |
159 | Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); |
160 | float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y); |
161 | float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); |
162 | chain_iterations = 0; |
163 | |
164 | while (target_distance > chain_tolarance) { |
165 | chain_backwards(); |
166 | chain_forwards(); |
167 | |
168 | final_bone2d_angle = final_bone2d_node->get_global_rotation(); |
169 | if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) { |
170 | final_bone2d_angle = target_global_pose.get_rotation(); |
171 | } |
172 | final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); |
173 | target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position()); |
174 | |
175 | chain_iterations += 1; |
176 | if (chain_iterations >= chain_max_iterations) { |
177 | break; |
178 | } |
179 | } |
180 | |
181 | // Apply all of the saved transforms to the Bone2D nodes |
182 | for (int i = 0; i < fabrik_data_chain.size(); i++) { |
183 | Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); |
184 | if (!joint_bone2d_node) { |
185 | ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set!" ); |
186 | continue; |
187 | } |
188 | Transform2D chain_trans = fabrik_transform_chain[i]; |
189 | |
190 | // Apply rotation |
191 | if (i + 1 < fabrik_data_chain.size()) { |
192 | chain_trans = chain_trans.looking_at(fabrik_transform_chain[i + 1].get_origin()); |
193 | } else { |
194 | if (fabrik_data_chain[i].use_target_rotation) { |
195 | chain_trans.set_rotation(target_global_pose.get_rotation()); |
196 | } else { |
197 | chain_trans = chain_trans.looking_at(target_global_pose.get_origin()); |
198 | } |
199 | } |
200 | // Adjust for the bone angle |
201 | chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle()); |
202 | |
203 | // Reset scale |
204 | chain_trans.set_scale(joint_bone2d_node->get_global_scale()); |
205 | |
206 | // Apply to the bone, and to the override |
207 | joint_bone2d_node->set_global_transform(chain_trans); |
208 | stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); |
209 | } |
210 | } |
211 | |
212 | void SkeletonModification2DFABRIK::chain_backwards() { |
213 | int final_joint_index = fabrik_data_chain.size() - 1; |
214 | Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache)); |
215 | Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index]; |
216 | |
217 | // Apply magnet position |
218 | if (final_joint_index != 0) { |
219 | final_bone2d_trans.set_origin(final_bone2d_trans.get_origin() + fabrik_data_chain[final_joint_index].magnet_position); |
220 | } |
221 | |
222 | // Set the rotation of the tip bone |
223 | final_bone2d_trans = final_bone2d_trans.looking_at(target_global_pose.get_origin()); |
224 | |
225 | // Set the position of the tip bone |
226 | float final_bone2d_angle = final_bone2d_trans.get_rotation(); |
227 | if (fabrik_data_chain[final_joint_index].use_target_rotation) { |
228 | final_bone2d_angle = target_global_pose.get_rotation(); |
229 | } |
230 | Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle)); |
231 | float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y); |
232 | final_bone2d_trans.set_origin(target_global_pose.get_origin() - (final_bone2d_direction * final_bone2d_length)); |
233 | |
234 | // Save the transform |
235 | fabrik_transform_chain.write[final_joint_index] = final_bone2d_trans; |
236 | |
237 | int i = final_joint_index; |
238 | while (i >= 1) { |
239 | Transform2D previous_pose = fabrik_transform_chain[i]; |
240 | i -= 1; |
241 | Bone2D *current_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); |
242 | Transform2D current_pose = fabrik_transform_chain[i]; |
243 | |
244 | // Apply magnet position |
245 | if (i != 0) { |
246 | current_pose.set_origin(current_pose.get_origin() + fabrik_data_chain[i].magnet_position); |
247 | } |
248 | |
249 | float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); |
250 | float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin())); |
251 | Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); |
252 | current_pose.set_origin(finish_position); |
253 | |
254 | // Save the transform |
255 | fabrik_transform_chain.write[i] = current_pose; |
256 | } |
257 | } |
258 | |
259 | void SkeletonModification2DFABRIK::chain_forwards() { |
260 | Transform2D origin_bone2d_trans = fabrik_transform_chain[0]; |
261 | origin_bone2d_trans.set_origin(origin_global_pose.get_origin()); |
262 | // Save the position |
263 | fabrik_transform_chain.write[0] = origin_bone2d_trans; |
264 | |
265 | for (int i = 0; i < fabrik_data_chain.size() - 1; i++) { |
266 | Bone2D *current_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); |
267 | Transform2D current_pose = fabrik_transform_chain[i]; |
268 | Transform2D next_pose = fabrik_transform_chain[i + 1]; |
269 | |
270 | float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); |
271 | float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin())); |
272 | Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); |
273 | current_pose.set_origin(finish_position); |
274 | |
275 | // Apply to the bone |
276 | fabrik_transform_chain.write[i + 1] = current_pose; |
277 | } |
278 | } |
279 | |
280 | void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) { |
281 | stack = p_stack; |
282 | |
283 | if (stack != nullptr) { |
284 | is_setup = true; |
285 | update_target_cache(); |
286 | } |
287 | } |
288 | |
289 | void SkeletonModification2DFABRIK::update_target_cache() { |
290 | if (!is_setup || !stack) { |
291 | ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!" ); |
292 | return; |
293 | } |
294 | |
295 | target_node_cache = ObjectID(); |
296 | if (stack->skeleton) { |
297 | if (stack->skeleton->is_inside_tree()) { |
298 | if (stack->skeleton->has_node(target_node)) { |
299 | Node *node = stack->skeleton->get_node(target_node); |
300 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
301 | "Cannot update target cache: node is this modification's skeleton or cannot be found!" ); |
302 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
303 | "Cannot update target cache: node is not in scene tree!" ); |
304 | target_node_cache = node->get_instance_id(); |
305 | } |
306 | } |
307 | } |
308 | } |
309 | |
310 | void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) { |
311 | ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!" ); |
312 | if (!is_setup || !stack) { |
313 | ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!" ); |
314 | return; |
315 | } |
316 | |
317 | fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); |
318 | if (stack->skeleton) { |
319 | if (stack->skeleton->is_inside_tree()) { |
320 | if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].bone2d_node)) { |
321 | Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].bone2d_node); |
322 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
323 | "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!" ); |
324 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
325 | "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!" ); |
326 | fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); |
327 | |
328 | Bone2D *bone = Object::cast_to<Bone2D>(node); |
329 | if (bone) { |
330 | fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); |
331 | } else { |
332 | ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!" ); |
333 | } |
334 | } |
335 | } |
336 | } |
337 | } |
338 | |
339 | void SkeletonModification2DFABRIK::set_target_node(const NodePath &p_target_node) { |
340 | target_node = p_target_node; |
341 | update_target_cache(); |
342 | } |
343 | |
344 | NodePath SkeletonModification2DFABRIK::get_target_node() const { |
345 | return target_node; |
346 | } |
347 | |
348 | void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) { |
349 | fabrik_data_chain.resize(p_length); |
350 | notify_property_list_changed(); |
351 | } |
352 | |
353 | int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() { |
354 | return fabrik_data_chain.size(); |
355 | } |
356 | |
357 | void SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { |
358 | ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!" ); |
359 | fabrik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; |
360 | fabrik_joint_update_bone2d_cache(p_joint_idx); |
361 | |
362 | notify_property_list_changed(); |
363 | } |
364 | |
365 | NodePath SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node(int p_joint_idx) const { |
366 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), NodePath(), "FABRIK joint out of range!" ); |
367 | return fabrik_data_chain[p_joint_idx].bone2d_node; |
368 | } |
369 | |
370 | void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) { |
371 | ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!" ); |
372 | ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!" ); |
373 | |
374 | if (is_setup) { |
375 | if (stack->skeleton) { |
376 | ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!" ); |
377 | fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
378 | fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); |
379 | fabrik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); |
380 | } else { |
381 | WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..." ); |
382 | fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
383 | } |
384 | } else { |
385 | WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..." ); |
386 | fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
387 | } |
388 | |
389 | notify_property_list_changed(); |
390 | } |
391 | |
392 | int SkeletonModification2DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const { |
393 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), -1, "FABRIK joint out of range!" ); |
394 | return fabrik_data_chain[p_joint_idx].bone_idx; |
395 | } |
396 | |
397 | void SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position) { |
398 | ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!" ); |
399 | fabrik_data_chain.write[p_joint_idx].magnet_position = p_magnet_position; |
400 | } |
401 | |
402 | Vector2 SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position(int p_joint_idx) const { |
403 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), Vector2(), "FABRIK joint out of range!" ); |
404 | return fabrik_data_chain[p_joint_idx].magnet_position; |
405 | } |
406 | |
407 | void SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation) { |
408 | ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!" ); |
409 | fabrik_data_chain.write[p_joint_idx].use_target_rotation = p_use_target_rotation; |
410 | } |
411 | |
412 | bool SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation(int p_joint_idx) const { |
413 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), false, "FABRIK joint out of range!" ); |
414 | return fabrik_data_chain[p_joint_idx].use_target_rotation; |
415 | } |
416 | |
417 | void SkeletonModification2DFABRIK::_bind_methods() { |
418 | ClassDB::bind_method(D_METHOD("set_target_node" , "target_nodepath" ), &SkeletonModification2DFABRIK::set_target_node); |
419 | ClassDB::bind_method(D_METHOD("get_target_node" ), &SkeletonModification2DFABRIK::get_target_node); |
420 | |
421 | ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length" , "length" ), &SkeletonModification2DFABRIK::set_fabrik_data_chain_length); |
422 | ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length" ), &SkeletonModification2DFABRIK::get_fabrik_data_chain_length); |
423 | |
424 | ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone2d_node" , "joint_idx" , "bone2d_nodepath" ), &SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node); |
425 | ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone2d_node" , "joint_idx" ), &SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node); |
426 | ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index" , "joint_idx" , "bone_idx" ), &SkeletonModification2DFABRIK::set_fabrik_joint_bone_index); |
427 | ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index" , "joint_idx" ), &SkeletonModification2DFABRIK::get_fabrik_joint_bone_index); |
428 | ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet_position" , "joint_idx" , "magnet_position" ), &SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position); |
429 | ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet_position" , "joint_idx" ), &SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position); |
430 | ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_rotation" , "joint_idx" , "use_target_rotation" ), &SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation); |
431 | ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_rotation" , "joint_idx" ), &SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation); |
432 | |
433 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D" ), "set_target_node" , "get_target_node" ); |
434 | ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length" , PROPERTY_HINT_RANGE, "0, 100, 1" ), "set_fabrik_data_chain_length" , "get_fabrik_data_chain_length" ); |
435 | } |
436 | |
437 | SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { |
438 | stack = nullptr; |
439 | is_setup = false; |
440 | enabled = true; |
441 | editor_draw_gizmo = false; |
442 | } |
443 | |
444 | SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { |
445 | } |
446 | |