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