1 | /**************************************************************************/ |
2 | /* skeleton_modification_2d_twoboneik.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_twoboneik.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 SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) { |
39 | String path = p_path; |
40 | |
41 | if (path == "joint_one_bone_idx" ) { |
42 | set_joint_one_bone_idx(p_value); |
43 | } else if (path == "joint_one_bone2d_node" ) { |
44 | set_joint_one_bone2d_node(p_value); |
45 | } else if (path == "joint_two_bone_idx" ) { |
46 | set_joint_two_bone_idx(p_value); |
47 | } else if (path == "joint_two_bone2d_node" ) { |
48 | set_joint_two_bone2d_node(p_value); |
49 | } |
50 | |
51 | #ifdef TOOLS_ENABLED |
52 | if (path.begins_with("editor/draw_gizmo" )) { |
53 | set_editor_draw_gizmo(p_value); |
54 | } else if (path.begins_with("editor/draw_min_max" )) { |
55 | set_editor_draw_min_max(p_value); |
56 | } |
57 | #endif // TOOLS_ENABLED |
58 | |
59 | return true; |
60 | } |
61 | |
62 | bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const { |
63 | String path = p_path; |
64 | |
65 | if (path == "joint_one_bone_idx" ) { |
66 | r_ret = get_joint_one_bone_idx(); |
67 | } else if (path == "joint_one_bone2d_node" ) { |
68 | r_ret = get_joint_one_bone2d_node(); |
69 | } else if (path == "joint_two_bone_idx" ) { |
70 | r_ret = get_joint_two_bone_idx(); |
71 | } else if (path == "joint_two_bone2d_node" ) { |
72 | r_ret = get_joint_two_bone2d_node(); |
73 | } |
74 | |
75 | #ifdef TOOLS_ENABLED |
76 | if (path.begins_with("editor/draw_gizmo" )) { |
77 | r_ret = get_editor_draw_gizmo(); |
78 | } else if (path.begins_with("editor/draw_min_max" )) { |
79 | r_ret = get_editor_draw_min_max(); |
80 | } |
81 | #endif // TOOLS_ENABLED |
82 | |
83 | return true; |
84 | } |
85 | |
86 | void SkeletonModification2DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const { |
87 | p_list->push_back(PropertyInfo(Variant::INT, "joint_one_bone_idx" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
88 | p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_one_bone2d_node" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D" , PROPERTY_USAGE_DEFAULT)); |
89 | |
90 | p_list->push_back(PropertyInfo(Variant::INT, "joint_two_bone_idx" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
91 | p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_two_bone2d_node" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D" , PROPERTY_USAGE_DEFAULT)); |
92 | |
93 | #ifdef TOOLS_ENABLED |
94 | if (Engine::get_singleton()->is_editor_hint()) { |
95 | p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
96 | p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_min_max" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
97 | } |
98 | #endif // TOOLS_ENABLED |
99 | } |
100 | |
101 | void SkeletonModification2DTwoBoneIK::_execute(float p_delta) { |
102 | ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, |
103 | "Modification is not setup and therefore cannot execute!" ); |
104 | if (!enabled) { |
105 | return; |
106 | } |
107 | |
108 | if (target_node_cache.is_null()) { |
109 | WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..." ); |
110 | update_target_cache(); |
111 | return; |
112 | } |
113 | |
114 | if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { |
115 | WARN_PRINT_ONCE("Joint one Bone2D node cache is out of date. Attempting to update..." ); |
116 | update_joint_one_bone2d_cache(); |
117 | } |
118 | if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { |
119 | WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update..." ); |
120 | update_joint_two_bone2d_cache(); |
121 | } |
122 | |
123 | Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); |
124 | if (!target || !target->is_inside_tree()) { |
125 | ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!" ); |
126 | return; |
127 | } |
128 | |
129 | Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx); |
130 | if (joint_one_bone == nullptr) { |
131 | ERR_PRINT_ONCE("Joint one bone_idx does not point to a valid bone! Cannot execute modification!" ); |
132 | return; |
133 | } |
134 | |
135 | Bone2D *joint_two_bone = stack->skeleton->get_bone(joint_two_bone_idx); |
136 | if (joint_two_bone == nullptr) { |
137 | ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!" ); |
138 | return; |
139 | } |
140 | |
141 | // Adopted from the links below: |
142 | // http://theorangeduck.com/page/simple-two-joint |
143 | // https://www.alanzucconi.com/2018/05/02/ik-2d-2/ |
144 | // With modifications by TwistedTwigleg |
145 | Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); |
146 | float joint_one_to_target = target_difference.length(); |
147 | float angle_atan = target_difference.angle(); |
148 | |
149 | float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); |
150 | float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); |
151 | bool override_angles_due_to_out_of_range = false; |
152 | |
153 | if (joint_one_to_target < target_minimum_distance) { |
154 | joint_one_to_target = target_minimum_distance; |
155 | } |
156 | if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { |
157 | joint_one_to_target = target_maximum_distance; |
158 | } |
159 | |
160 | if (bone_one_length + bone_two_length < joint_one_to_target) { |
161 | override_angles_due_to_out_of_range = true; |
162 | } |
163 | |
164 | if (!override_angles_due_to_out_of_range) { |
165 | float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); |
166 | float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); |
167 | |
168 | if (flip_bend_direction) { |
169 | angle_0 = -angle_0; |
170 | angle_1 = -angle_1; |
171 | } |
172 | |
173 | if (isnan(angle_0) || isnan(angle_1)) { |
174 | // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. |
175 | } else { |
176 | joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); |
177 | joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); |
178 | } |
179 | } else { |
180 | joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); |
181 | joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); |
182 | } |
183 | |
184 | stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true); |
185 | stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true); |
186 | } |
187 | |
188 | void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { |
189 | stack = p_stack; |
190 | |
191 | if (stack) { |
192 | is_setup = true; |
193 | update_target_cache(); |
194 | update_joint_one_bone2d_cache(); |
195 | update_joint_two_bone2d_cache(); |
196 | } |
197 | } |
198 | |
199 | void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { |
200 | if (!enabled || !is_setup) { |
201 | return; |
202 | } |
203 | |
204 | Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx); |
205 | if (!operation_bone_one) { |
206 | return; |
207 | } |
208 | stack->skeleton->draw_set_transform( |
209 | stack->skeleton->to_local(operation_bone_one->get_global_position()), |
210 | operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation()); |
211 | |
212 | Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4); |
213 | #ifdef TOOLS_ENABLED |
214 | if (Engine::get_singleton()->is_editor_hint()) { |
215 | bone_ik_color = EDITOR_GET("editors/2d/bone_ik_color" ); |
216 | } |
217 | #endif // TOOLS_ENABLED |
218 | |
219 | if (flip_bend_direction) { |
220 | float angle = -(Math_PI * 0.5) + operation_bone_one->get_bone_angle(); |
221 | stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); |
222 | } else { |
223 | float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); |
224 | stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); |
225 | } |
226 | |
227 | #ifdef TOOLS_ENABLED |
228 | if (Engine::get_singleton()->is_editor_hint()) { |
229 | if (editor_draw_min_max) { |
230 | if (target_maximum_distance != 0.0 || target_minimum_distance != 0.0) { |
231 | Vector2 target_direction = Vector2(0, 1); |
232 | if (target_node_cache.is_valid()) { |
233 | stack->skeleton->draw_set_transform(Vector2(0, 0), 0.0); |
234 | Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); |
235 | target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); |
236 | } |
237 | |
238 | stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); |
239 | stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); |
240 | stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); |
241 | } |
242 | } |
243 | } |
244 | #endif // TOOLS_ENABLED |
245 | } |
246 | |
247 | void SkeletonModification2DTwoBoneIK::update_target_cache() { |
248 | if (!is_setup || !stack) { |
249 | ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!" ); |
250 | return; |
251 | } |
252 | |
253 | target_node_cache = ObjectID(); |
254 | if (stack->skeleton) { |
255 | if (stack->skeleton->is_inside_tree()) { |
256 | if (stack->skeleton->has_node(target_node)) { |
257 | Node *node = stack->skeleton->get_node(target_node); |
258 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
259 | "Cannot update target cache: node is this modification's skeleton or cannot be found!" ); |
260 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
261 | "Cannot update target cache: node is not in the scene tree!" ); |
262 | target_node_cache = node->get_instance_id(); |
263 | } |
264 | } |
265 | } |
266 | } |
267 | |
268 | void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { |
269 | if (!is_setup || !stack) { |
270 | ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!" ); |
271 | return; |
272 | } |
273 | |
274 | joint_one_bone2d_node_cache = ObjectID(); |
275 | if (stack->skeleton) { |
276 | if (stack->skeleton->is_inside_tree()) { |
277 | if (stack->skeleton->has_node(joint_one_bone2d_node)) { |
278 | Node *node = stack->skeleton->get_node(joint_one_bone2d_node); |
279 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
280 | "Cannot update update joint one Bone2D cache: node is this modification's skeleton or cannot be found!" ); |
281 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
282 | "Cannot update update joint one Bone2D cache: node is not in the scene tree!" ); |
283 | joint_one_bone2d_node_cache = node->get_instance_id(); |
284 | |
285 | Bone2D *bone = Object::cast_to<Bone2D>(node); |
286 | if (bone) { |
287 | joint_one_bone_idx = bone->get_index_in_skeleton(); |
288 | } else { |
289 | ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!" ); |
290 | } |
291 | } |
292 | } |
293 | } |
294 | } |
295 | |
296 | void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { |
297 | if (!is_setup || !stack) { |
298 | ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!" ); |
299 | return; |
300 | } |
301 | |
302 | joint_two_bone2d_node_cache = ObjectID(); |
303 | if (stack->skeleton) { |
304 | if (stack->skeleton->is_inside_tree()) { |
305 | if (stack->skeleton->has_node(joint_two_bone2d_node)) { |
306 | Node *node = stack->skeleton->get_node(joint_two_bone2d_node); |
307 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
308 | "Cannot update update joint two Bone2D cache: node is this modification's skeleton or cannot be found!" ); |
309 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
310 | "Cannot update update joint two Bone2D cache: node is not in scene tree!" ); |
311 | joint_two_bone2d_node_cache = node->get_instance_id(); |
312 | |
313 | Bone2D *bone = Object::cast_to<Bone2D>(node); |
314 | if (bone) { |
315 | joint_two_bone_idx = bone->get_index_in_skeleton(); |
316 | } else { |
317 | ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!" ); |
318 | } |
319 | } |
320 | } |
321 | } |
322 | } |
323 | |
324 | void SkeletonModification2DTwoBoneIK::set_target_node(const NodePath &p_target_node) { |
325 | target_node = p_target_node; |
326 | update_target_cache(); |
327 | } |
328 | |
329 | NodePath SkeletonModification2DTwoBoneIK::get_target_node() const { |
330 | return target_node; |
331 | } |
332 | |
333 | void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) { |
334 | joint_one_bone2d_node = p_target_node; |
335 | update_joint_one_bone2d_cache(); |
336 | notify_property_list_changed(); |
337 | } |
338 | |
339 | void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) { |
340 | ERR_FAIL_COND_MSG(p_distance < 0, "Target minimum distance cannot be less than zero!" ); |
341 | target_minimum_distance = p_distance; |
342 | } |
343 | |
344 | float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const { |
345 | return target_minimum_distance; |
346 | } |
347 | |
348 | void SkeletonModification2DTwoBoneIK::set_target_maximum_distance(float p_distance) { |
349 | ERR_FAIL_COND_MSG(p_distance < 0, "Target maximum distance cannot be less than zero!" ); |
350 | target_maximum_distance = p_distance; |
351 | } |
352 | |
353 | float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const { |
354 | return target_maximum_distance; |
355 | } |
356 | |
357 | void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) { |
358 | flip_bend_direction = p_flip_direction; |
359 | |
360 | #ifdef TOOLS_ENABLED |
361 | if (stack && is_setup) { |
362 | stack->set_editor_gizmos_dirty(true); |
363 | } |
364 | #endif // TOOLS_ENABLED |
365 | } |
366 | |
367 | bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const { |
368 | return flip_bend_direction; |
369 | } |
370 | |
371 | NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const { |
372 | return joint_one_bone2d_node; |
373 | } |
374 | |
375 | void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) { |
376 | joint_two_bone2d_node = p_target_node; |
377 | update_joint_two_bone2d_cache(); |
378 | notify_property_list_changed(); |
379 | } |
380 | |
381 | NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const { |
382 | return joint_two_bone2d_node; |
383 | } |
384 | |
385 | void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { |
386 | ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!" ); |
387 | |
388 | if (is_setup) { |
389 | if (stack->skeleton) { |
390 | ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!" ); |
391 | joint_one_bone_idx = p_bone_idx; |
392 | joint_one_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); |
393 | joint_one_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); |
394 | } else { |
395 | WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..." ); |
396 | joint_one_bone_idx = p_bone_idx; |
397 | } |
398 | } else { |
399 | WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..." ); |
400 | joint_one_bone_idx = p_bone_idx; |
401 | } |
402 | |
403 | notify_property_list_changed(); |
404 | } |
405 | |
406 | int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const { |
407 | return joint_one_bone_idx; |
408 | } |
409 | |
410 | void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { |
411 | ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!" ); |
412 | |
413 | if (is_setup) { |
414 | if (stack->skeleton) { |
415 | ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!" ); |
416 | joint_two_bone_idx = p_bone_idx; |
417 | joint_two_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); |
418 | joint_two_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); |
419 | } else { |
420 | WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..." ); |
421 | joint_two_bone_idx = p_bone_idx; |
422 | } |
423 | } else { |
424 | WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..." ); |
425 | joint_two_bone_idx = p_bone_idx; |
426 | } |
427 | |
428 | notify_property_list_changed(); |
429 | } |
430 | |
431 | int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const { |
432 | return joint_two_bone_idx; |
433 | } |
434 | |
435 | #ifdef TOOLS_ENABLED |
436 | void SkeletonModification2DTwoBoneIK::set_editor_draw_min_max(bool p_draw) { |
437 | editor_draw_min_max = p_draw; |
438 | } |
439 | |
440 | bool SkeletonModification2DTwoBoneIK::get_editor_draw_min_max() const { |
441 | return editor_draw_min_max; |
442 | } |
443 | #endif // TOOLS_ENABLED |
444 | |
445 | void SkeletonModification2DTwoBoneIK::_bind_methods() { |
446 | ClassDB::bind_method(D_METHOD("set_target_node" , "target_nodepath" ), &SkeletonModification2DTwoBoneIK::set_target_node); |
447 | ClassDB::bind_method(D_METHOD("get_target_node" ), &SkeletonModification2DTwoBoneIK::get_target_node); |
448 | |
449 | ClassDB::bind_method(D_METHOD("set_target_minimum_distance" , "minimum_distance" ), &SkeletonModification2DTwoBoneIK::set_target_minimum_distance); |
450 | ClassDB::bind_method(D_METHOD("get_target_minimum_distance" ), &SkeletonModification2DTwoBoneIK::get_target_minimum_distance); |
451 | ClassDB::bind_method(D_METHOD("set_target_maximum_distance" , "maximum_distance" ), &SkeletonModification2DTwoBoneIK::set_target_maximum_distance); |
452 | ClassDB::bind_method(D_METHOD("get_target_maximum_distance" ), &SkeletonModification2DTwoBoneIK::get_target_maximum_distance); |
453 | ClassDB::bind_method(D_METHOD("set_flip_bend_direction" , "flip_direction" ), &SkeletonModification2DTwoBoneIK::set_flip_bend_direction); |
454 | ClassDB::bind_method(D_METHOD("get_flip_bend_direction" ), &SkeletonModification2DTwoBoneIK::get_flip_bend_direction); |
455 | |
456 | ClassDB::bind_method(D_METHOD("set_joint_one_bone2d_node" , "bone2d_node" ), &SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node); |
457 | ClassDB::bind_method(D_METHOD("get_joint_one_bone2d_node" ), &SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node); |
458 | ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx" , "bone_idx" ), &SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx); |
459 | ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx" ), &SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx); |
460 | |
461 | ClassDB::bind_method(D_METHOD("set_joint_two_bone2d_node" , "bone2d_node" ), &SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node); |
462 | ClassDB::bind_method(D_METHOD("get_joint_two_bone2d_node" ), &SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node); |
463 | ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx" , "bone_idx" ), &SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx); |
464 | ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx" ), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx); |
465 | |
466 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D" ), "set_target_node" , "get_target_node" ); |
467 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance" , PROPERTY_HINT_RANGE, "0,100000000,0.01,suffix:m" ), "set_target_minimum_distance" , "get_target_minimum_distance" ); |
468 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance" , PROPERTY_HINT_NONE, "0,100000000,0.01,suffix:m" ), "set_target_maximum_distance" , "get_target_maximum_distance" ); |
469 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction" , PROPERTY_HINT_NONE, "" ), "set_flip_bend_direction" , "get_flip_bend_direction" ); |
470 | ADD_GROUP("" , "" ); |
471 | } |
472 | |
473 | SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { |
474 | stack = nullptr; |
475 | is_setup = false; |
476 | enabled = true; |
477 | editor_draw_gizmo = true; |
478 | } |
479 | |
480 | SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { |
481 | } |
482 | |