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
9903 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
// Adopted 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().x, joint_one_bone->get_global_scale().y);
154
float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y);
155
bool override_angles_due_to_out_of_range = false;
156
157
if (joint_one_to_target < target_minimum_distance) {
158
joint_one_to_target = target_minimum_distance;
159
}
160
if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) {
161
joint_one_to_target = target_maximum_distance;
162
}
163
164
if (bone_one_length + bone_two_length < joint_one_to_target) {
165
override_angles_due_to_out_of_range = true;
166
}
167
168
if (!override_angles_due_to_out_of_range) {
169
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));
170
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));
171
172
if (flip_bend_direction) {
173
angle_0 = -angle_0;
174
angle_1 = -angle_1;
175
}
176
177
if (std::isnan(angle_0) || std::isnan(angle_1)) {
178
// We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN.
179
} else {
180
joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle());
181
joint_two_bone->set_rotation(-Math::PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle());
182
}
183
} else {
184
joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle());
185
joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle());
186
}
187
188
stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true);
189
stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true);
190
}
191
192
void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) {
193
stack = p_stack;
194
195
if (stack) {
196
is_setup = true;
197
update_target_cache();
198
update_joint_one_bone2d_cache();
199
update_joint_two_bone2d_cache();
200
}
201
}
202
203
void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
204
if (!enabled || !is_setup) {
205
return;
206
}
207
208
Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx);
209
if (!operation_bone_one) {
210
return;
211
}
212
stack->skeleton->draw_set_transform(
213
stack->skeleton->to_local(operation_bone_one->get_global_position()),
214
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation());
215
216
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
217
#ifdef TOOLS_ENABLED
218
if (Engine::get_singleton()->is_editor_hint()) {
219
bone_ik_color = EDITOR_GET("editors/2d/bone_ik_color");
220
}
221
#endif // TOOLS_ENABLED
222
223
if (flip_bend_direction) {
224
float angle = -(Math::PI * 0.5) + operation_bone_one->get_bone_angle();
225
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);
226
} else {
227
float angle = (Math::PI * 0.5) + operation_bone_one->get_bone_angle();
228
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);
229
}
230
231
#ifdef TOOLS_ENABLED
232
if (Engine::get_singleton()->is_editor_hint()) {
233
if (editor_draw_min_max) {
234
if (target_maximum_distance != 0.0 || target_minimum_distance != 0.0) {
235
Vector2 target_direction = Vector2(0, 1);
236
if (target_node_cache.is_valid()) {
237
stack->skeleton->draw_set_transform(Vector2(0, 0), 0.0);
238
Node2D *target = ObjectDB::get_instance<Node2D>(target_node_cache);
239
target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position());
240
}
241
242
stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color);
243
stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color);
244
stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0);
245
}
246
}
247
}
248
#endif // TOOLS_ENABLED
249
}
250
251
void SkeletonModification2DTwoBoneIK::update_target_cache() {
252
if (!is_setup || !stack) {
253
if (is_setup) {
254
ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!");
255
}
256
return;
257
}
258
259
target_node_cache = ObjectID();
260
if (stack->skeleton) {
261
if (stack->skeleton->is_inside_tree()) {
262
if (stack->skeleton->has_node(target_node)) {
263
Node *node = stack->skeleton->get_node(target_node);
264
ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
265
"Cannot update target cache: node is this modification's skeleton or cannot be found!");
266
ERR_FAIL_COND_MSG(!node->is_inside_tree(),
267
"Cannot update target cache: node is not in the scene tree!");
268
target_node_cache = node->get_instance_id();
269
}
270
}
271
}
272
}
273
274
void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() {
275
if (!is_setup || !stack) {
276
if (is_setup) {
277
ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!");
278
}
279
return;
280
}
281
282
joint_one_bone2d_node_cache = ObjectID();
283
if (stack->skeleton) {
284
if (stack->skeleton->is_inside_tree()) {
285
if (stack->skeleton->has_node(joint_one_bone2d_node)) {
286
Node *node = stack->skeleton->get_node(joint_one_bone2d_node);
287
ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
288
"Cannot update joint one Bone2D cache: node is this modification's skeleton or cannot be found!");
289
ERR_FAIL_COND_MSG(!node->is_inside_tree(),
290
"Cannot update joint one Bone2D cache: node is not in the scene tree!");
291
joint_one_bone2d_node_cache = node->get_instance_id();
292
293
Bone2D *bone = Object::cast_to<Bone2D>(node);
294
if (bone) {
295
joint_one_bone_idx = bone->get_index_in_skeleton();
296
} else {
297
ERR_FAIL_MSG("Update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
298
}
299
}
300
}
301
}
302
}
303
304
void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() {
305
if (!is_setup || !stack) {
306
if (is_setup) {
307
ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!");
308
}
309
return;
310
}
311
312
joint_two_bone2d_node_cache = ObjectID();
313
if (stack->skeleton) {
314
if (stack->skeleton->is_inside_tree()) {
315
if (stack->skeleton->has_node(joint_two_bone2d_node)) {
316
Node *node = stack->skeleton->get_node(joint_two_bone2d_node);
317
ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
318
"Cannot update joint two Bone2D cache: node is this modification's skeleton or cannot be found!");
319
ERR_FAIL_COND_MSG(!node->is_inside_tree(),
320
"Cannot update joint two Bone2D cache: node is not in scene tree!");
321
joint_two_bone2d_node_cache = node->get_instance_id();
322
323
Bone2D *bone = Object::cast_to<Bone2D>(node);
324
if (bone) {
325
joint_two_bone_idx = bone->get_index_in_skeleton();
326
} else {
327
ERR_FAIL_MSG("Update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!");
328
}
329
}
330
}
331
}
332
}
333
334
void SkeletonModification2DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
335
target_node = p_target_node;
336
update_target_cache();
337
}
338
339
NodePath SkeletonModification2DTwoBoneIK::get_target_node() const {
340
return target_node;
341
}
342
343
void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) {
344
joint_one_bone2d_node = p_target_node;
345
update_joint_one_bone2d_cache();
346
notify_property_list_changed();
347
}
348
349
void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) {
350
ERR_FAIL_COND_MSG(p_distance < 0, "Target minimum distance cannot be less than zero!");
351
target_minimum_distance = p_distance;
352
}
353
354
float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const {
355
return target_minimum_distance;
356
}
357
358
void SkeletonModification2DTwoBoneIK::set_target_maximum_distance(float p_distance) {
359
ERR_FAIL_COND_MSG(p_distance < 0, "Target maximum distance cannot be less than zero!");
360
target_maximum_distance = p_distance;
361
}
362
363
float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const {
364
return target_maximum_distance;
365
}
366
367
void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) {
368
flip_bend_direction = p_flip_direction;
369
370
#ifdef TOOLS_ENABLED
371
if (stack && is_setup) {
372
stack->set_editor_gizmos_dirty(true);
373
}
374
#endif // TOOLS_ENABLED
375
}
376
377
bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const {
378
return flip_bend_direction;
379
}
380
381
NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const {
382
return joint_one_bone2d_node;
383
}
384
385
void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) {
386
joint_two_bone2d_node = p_target_node;
387
update_joint_two_bone2d_cache();
388
notify_property_list_changed();
389
}
390
391
NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const {
392
return joint_two_bone2d_node;
393
}
394
395
void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
396
ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
397
398
if (is_setup) {
399
if (stack->skeleton) {
400
ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
401
joint_one_bone_idx = p_bone_idx;
402
joint_one_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
403
joint_one_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
404
} else {
405
WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one...");
406
joint_one_bone_idx = p_bone_idx;
407
}
408
} else {
409
joint_one_bone_idx = p_bone_idx;
410
}
411
412
notify_property_list_changed();
413
}
414
415
int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const {
416
return joint_one_bone_idx;
417
}
418
419
void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
420
ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
421
422
if (is_setup) {
423
if (stack->skeleton) {
424
ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
425
joint_two_bone_idx = p_bone_idx;
426
joint_two_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id();
427
joint_two_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx));
428
} else {
429
WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two...");
430
joint_two_bone_idx = p_bone_idx;
431
}
432
} else {
433
joint_two_bone_idx = p_bone_idx;
434
}
435
436
notify_property_list_changed();
437
}
438
439
int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const {
440
return joint_two_bone_idx;
441
}
442
443
#ifdef TOOLS_ENABLED
444
void SkeletonModification2DTwoBoneIK::set_editor_draw_min_max(bool p_draw) {
445
editor_draw_min_max = p_draw;
446
}
447
448
bool SkeletonModification2DTwoBoneIK::get_editor_draw_min_max() const {
449
return editor_draw_min_max;
450
}
451
#endif // TOOLS_ENABLED
452
453
void SkeletonModification2DTwoBoneIK::_bind_methods() {
454
ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DTwoBoneIK::set_target_node);
455
ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DTwoBoneIK::get_target_node);
456
457
ClassDB::bind_method(D_METHOD("set_target_minimum_distance", "minimum_distance"), &SkeletonModification2DTwoBoneIK::set_target_minimum_distance);
458
ClassDB::bind_method(D_METHOD("get_target_minimum_distance"), &SkeletonModification2DTwoBoneIK::get_target_minimum_distance);
459
ClassDB::bind_method(D_METHOD("set_target_maximum_distance", "maximum_distance"), &SkeletonModification2DTwoBoneIK::set_target_maximum_distance);
460
ClassDB::bind_method(D_METHOD("get_target_maximum_distance"), &SkeletonModification2DTwoBoneIK::get_target_maximum_distance);
461
ClassDB::bind_method(D_METHOD("set_flip_bend_direction", "flip_direction"), &SkeletonModification2DTwoBoneIK::set_flip_bend_direction);
462
ClassDB::bind_method(D_METHOD("get_flip_bend_direction"), &SkeletonModification2DTwoBoneIK::get_flip_bend_direction);
463
464
ClassDB::bind_method(D_METHOD("set_joint_one_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node);
465
ClassDB::bind_method(D_METHOD("get_joint_one_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node);
466
ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx);
467
ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx);
468
469
ClassDB::bind_method(D_METHOD("set_joint_two_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node);
470
ClassDB::bind_method(D_METHOD("get_joint_two_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node);
471
ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx);
472
ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx);
473
474
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
475
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");
476
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");
477
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction", PROPERTY_HINT_NONE, ""), "set_flip_bend_direction", "get_flip_bend_direction");
478
ADD_GROUP("", "");
479
}
480
481
SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() {
482
stack = nullptr;
483
is_setup = false;
484
enabled = true;
485
editor_draw_gizmo = true;
486
}
487
488
SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() {
489
}
490
491