1 | /**************************************************************************/ |
2 | /* skeleton_modification_2d_ccdik.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_ccdik.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 SkeletonModification2DCCDIK::_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, ccdik_data_chain.size(), false); |
45 | |
46 | if (what == "bone2d_node" ) { |
47 | set_ccdik_joint_bone2d_node(which, p_value); |
48 | } else if (what == "bone_index" ) { |
49 | set_ccdik_joint_bone_index(which, p_value); |
50 | } else if (what == "rotate_from_joint" ) { |
51 | set_ccdik_joint_rotate_from_joint(which, p_value); |
52 | } else if (what == "enable_constraint" ) { |
53 | set_ccdik_joint_enable_constraint(which, p_value); |
54 | } else if (what == "constraint_angle_min" ) { |
55 | set_ccdik_joint_constraint_angle_min(which, Math::deg_to_rad(float(p_value))); |
56 | } else if (what == "constraint_angle_max" ) { |
57 | set_ccdik_joint_constraint_angle_max(which, Math::deg_to_rad(float(p_value))); |
58 | } else if (what == "constraint_angle_invert" ) { |
59 | set_ccdik_joint_constraint_angle_invert(which, p_value); |
60 | } else if (what == "constraint_in_localspace" ) { |
61 | set_ccdik_joint_constraint_in_localspace(which, p_value); |
62 | } |
63 | |
64 | #ifdef TOOLS_ENABLED |
65 | if (what.begins_with("editor_draw_gizmo" )) { |
66 | set_ccdik_joint_editor_draw_gizmo(which, p_value); |
67 | } |
68 | #endif // TOOLS_ENABLED |
69 | |
70 | return true; |
71 | } |
72 | |
73 | #ifdef TOOLS_ENABLED |
74 | if (path.begins_with("editor/draw_gizmo" )) { |
75 | set_editor_draw_gizmo(p_value); |
76 | } |
77 | #endif // TOOLS_ENABLED |
78 | |
79 | return true; |
80 | } |
81 | |
82 | bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) const { |
83 | String path = p_path; |
84 | |
85 | if (path.begins_with("joint_data/" )) { |
86 | int which = path.get_slicec('/', 1).to_int(); |
87 | String what = path.get_slicec('/', 2); |
88 | ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false); |
89 | |
90 | if (what == "bone2d_node" ) { |
91 | r_ret = get_ccdik_joint_bone2d_node(which); |
92 | } else if (what == "bone_index" ) { |
93 | r_ret = get_ccdik_joint_bone_index(which); |
94 | } else if (what == "rotate_from_joint" ) { |
95 | r_ret = get_ccdik_joint_rotate_from_joint(which); |
96 | } else if (what == "enable_constraint" ) { |
97 | r_ret = get_ccdik_joint_enable_constraint(which); |
98 | } else if (what == "constraint_angle_min" ) { |
99 | r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_min(which)); |
100 | } else if (what == "constraint_angle_max" ) { |
101 | r_ret = Math::rad_to_deg(get_ccdik_joint_constraint_angle_max(which)); |
102 | } else if (what == "constraint_angle_invert" ) { |
103 | r_ret = get_ccdik_joint_constraint_angle_invert(which); |
104 | } else if (what == "constraint_in_localspace" ) { |
105 | r_ret = get_ccdik_joint_constraint_in_localspace(which); |
106 | } |
107 | |
108 | #ifdef TOOLS_ENABLED |
109 | if (what.begins_with("editor_draw_gizmo" )) { |
110 | r_ret = get_ccdik_joint_editor_draw_gizmo(which); |
111 | } |
112 | #endif // TOOLS_ENABLED |
113 | |
114 | return true; |
115 | } |
116 | |
117 | #ifdef TOOLS_ENABLED |
118 | if (path.begins_with("editor/draw_gizmo" )) { |
119 | r_ret = get_editor_draw_gizmo(); |
120 | } |
121 | #endif // TOOLS_ENABLED |
122 | |
123 | return true; |
124 | } |
125 | |
126 | void SkeletonModification2DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const { |
127 | for (int i = 0; i < ccdik_data_chain.size(); i++) { |
128 | String base_string = "joint_data/" + itos(i) + "/" ; |
129 | |
130 | p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
131 | p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D" , PROPERTY_USAGE_DEFAULT)); |
132 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "rotate_from_joint" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
133 | |
134 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
135 | if (ccdik_data_chain[i].enable_constraint) { |
136 | p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_min" , PROPERTY_HINT_RANGE, "-360, 360, 0.01" , PROPERTY_USAGE_DEFAULT)); |
137 | p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_max" , PROPERTY_HINT_RANGE, "-360, 360, 0.01" , PROPERTY_USAGE_DEFAULT)); |
138 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_angle_invert" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
139 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_in_localspace" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
140 | } |
141 | |
142 | #ifdef TOOLS_ENABLED |
143 | if (Engine::get_singleton()->is_editor_hint()) { |
144 | p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "editor_draw_gizmo" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
145 | } |
146 | #endif // TOOLS_ENABLED |
147 | } |
148 | |
149 | #ifdef TOOLS_ENABLED |
150 | if (Engine::get_singleton()->is_editor_hint()) { |
151 | p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT)); |
152 | } |
153 | #endif // TOOLS_ENABLED |
154 | } |
155 | |
156 | void SkeletonModification2DCCDIK::_execute(float p_delta) { |
157 | ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, |
158 | "Modification is not setup and therefore cannot execute!" ); |
159 | if (!enabled) { |
160 | return; |
161 | } |
162 | |
163 | if (target_node_cache.is_null()) { |
164 | WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..." ); |
165 | update_target_cache(); |
166 | return; |
167 | } |
168 | if (tip_node_cache.is_null()) { |
169 | WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update..." ); |
170 | update_tip_cache(); |
171 | return; |
172 | } |
173 | |
174 | Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); |
175 | if (!target || !target->is_inside_tree()) { |
176 | ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!" ); |
177 | return; |
178 | } |
179 | |
180 | Node2D *tip = Object::cast_to<Node2D>(ObjectDB::get_instance(tip_node_cache)); |
181 | if (!tip || !tip->is_inside_tree()) { |
182 | ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!" ); |
183 | return; |
184 | } |
185 | |
186 | for (int i = 0; i < ccdik_data_chain.size(); i++) { |
187 | _execute_ccdik_joint(i, target, tip); |
188 | } |
189 | } |
190 | |
191 | void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip) { |
192 | CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; |
193 | if (ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count()) { |
194 | ERR_PRINT_ONCE("2D CCDIK joint: bone index not found!" ); |
195 | return; |
196 | } |
197 | |
198 | Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data.bone_idx); |
199 | Transform2D operation_transform = operation_bone->get_global_transform(); |
200 | |
201 | if (ccdik_data.rotate_from_joint) { |
202 | // To rotate from the joint, simply look at the target! |
203 | operation_transform.set_rotation( |
204 | operation_transform.looking_at(p_target->get_global_position()).get_rotation() - operation_bone->get_bone_angle()); |
205 | } else { |
206 | // How to rotate from the tip: get the difference of rotation needed from the tip to the target, from the perspective of the joint. |
207 | // Because we are only using the offset, we do not need to account for the bone angle of the Bone2D node. |
208 | float joint_to_tip = p_tip->get_global_position().angle_to_point(operation_transform.get_origin()); |
209 | float joint_to_target = p_target->get_global_position().angle_to_point(operation_transform.get_origin()); |
210 | operation_transform.set_rotation( |
211 | operation_transform.get_rotation() + (joint_to_target - joint_to_tip)); |
212 | } |
213 | |
214 | // Reset scale |
215 | operation_transform.set_scale(operation_bone->get_global_scale()); |
216 | |
217 | // Apply constraints in globalspace: |
218 | if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) { |
219 | operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert)); |
220 | } |
221 | |
222 | // Convert from a global transform to a delta and then apply the delta to the local transform. |
223 | operation_bone->set_global_transform(operation_transform); |
224 | operation_transform = operation_bone->get_transform(); |
225 | |
226 | // Apply constraints in localspace: |
227 | if (ccdik_data.enable_constraint && ccdik_data.constraint_in_localspace) { |
228 | operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert)); |
229 | } |
230 | |
231 | // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. |
232 | stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, operation_transform, stack->strength, true); |
233 | operation_bone->set_transform(operation_transform); |
234 | operation_bone->notification(operation_bone->NOTIFICATION_TRANSFORM_CHANGED); |
235 | } |
236 | |
237 | void SkeletonModification2DCCDIK::_setup_modification(SkeletonModificationStack2D *p_stack) { |
238 | stack = p_stack; |
239 | |
240 | if (stack != nullptr) { |
241 | is_setup = true; |
242 | update_target_cache(); |
243 | update_tip_cache(); |
244 | } |
245 | } |
246 | |
247 | void SkeletonModification2DCCDIK::_draw_editor_gizmo() { |
248 | if (!enabled || !is_setup) { |
249 | return; |
250 | } |
251 | |
252 | for (int i = 0; i < ccdik_data_chain.size(); i++) { |
253 | if (!ccdik_data_chain[i].editor_draw_gizmo) { |
254 | continue; |
255 | } |
256 | |
257 | Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data_chain[i].bone_idx); |
258 | editor_draw_angle_constraints(operation_bone, ccdik_data_chain[i].constraint_angle_min, ccdik_data_chain[i].constraint_angle_max, |
259 | ccdik_data_chain[i].enable_constraint, ccdik_data_chain[i].constraint_in_localspace, ccdik_data_chain[i].constraint_angle_invert); |
260 | } |
261 | } |
262 | |
263 | void SkeletonModification2DCCDIK::update_target_cache() { |
264 | if (!is_setup || !stack) { |
265 | ERR_PRINT_ONCE("Cannot update target cache: modification is not properly setup!" ); |
266 | return; |
267 | } |
268 | |
269 | target_node_cache = ObjectID(); |
270 | if (stack->skeleton) { |
271 | if (stack->skeleton->is_inside_tree()) { |
272 | if (stack->skeleton->has_node(target_node)) { |
273 | Node *node = stack->skeleton->get_node(target_node); |
274 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
275 | "Cannot update target cache: node is this modification's skeleton or cannot be found!" ); |
276 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
277 | "Cannot update target cache: node is not in the scene tree!" ); |
278 | target_node_cache = node->get_instance_id(); |
279 | } |
280 | } |
281 | } |
282 | } |
283 | |
284 | void SkeletonModification2DCCDIK::update_tip_cache() { |
285 | if (!is_setup || !stack) { |
286 | ERR_PRINT_ONCE("Cannot update tip cache: modification is not properly setup!" ); |
287 | return; |
288 | } |
289 | |
290 | tip_node_cache = ObjectID(); |
291 | if (stack->skeleton) { |
292 | if (stack->skeleton->is_inside_tree()) { |
293 | if (stack->skeleton->has_node(tip_node)) { |
294 | Node *node = stack->skeleton->get_node(tip_node); |
295 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
296 | "Cannot update tip cache: node is this modification's skeleton or cannot be found!" ); |
297 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
298 | "Cannot update tip cache: node is not in the scene tree!" ); |
299 | tip_node_cache = node->get_instance_id(); |
300 | } |
301 | } |
302 | } |
303 | } |
304 | |
305 | void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { |
306 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!" ); |
307 | if (!is_setup || !stack) { |
308 | ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!" ); |
309 | return; |
310 | } |
311 | |
312 | ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); |
313 | if (stack->skeleton) { |
314 | if (stack->skeleton->is_inside_tree()) { |
315 | if (stack->skeleton->has_node(ccdik_data_chain[p_joint_idx].bone2d_node)) { |
316 | Node *node = stack->skeleton->get_node(ccdik_data_chain[p_joint_idx].bone2d_node); |
317 | ERR_FAIL_COND_MSG(!node || stack->skeleton == node, |
318 | "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!" ); |
319 | ERR_FAIL_COND_MSG(!node->is_inside_tree(), |
320 | "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in the scene tree!" ); |
321 | ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); |
322 | |
323 | Bone2D *bone = Object::cast_to<Bone2D>(node); |
324 | if (bone) { |
325 | ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); |
326 | } else { |
327 | ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!" ); |
328 | } |
329 | } |
330 | } |
331 | } |
332 | } |
333 | |
334 | void SkeletonModification2DCCDIK::set_target_node(const NodePath &p_target_node) { |
335 | target_node = p_target_node; |
336 | update_target_cache(); |
337 | } |
338 | |
339 | NodePath SkeletonModification2DCCDIK::get_target_node() const { |
340 | return target_node; |
341 | } |
342 | |
343 | void SkeletonModification2DCCDIK::set_tip_node(const NodePath &p_tip_node) { |
344 | tip_node = p_tip_node; |
345 | update_tip_cache(); |
346 | } |
347 | |
348 | NodePath SkeletonModification2DCCDIK::get_tip_node() const { |
349 | return tip_node; |
350 | } |
351 | |
352 | void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) { |
353 | ccdik_data_chain.resize(p_length); |
354 | notify_property_list_changed(); |
355 | } |
356 | |
357 | int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() { |
358 | return ccdik_data_chain.size(); |
359 | } |
360 | |
361 | void SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { |
362 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
363 | ccdik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; |
364 | ccdik_joint_update_bone2d_cache(p_joint_idx); |
365 | |
366 | notify_property_list_changed(); |
367 | } |
368 | |
369 | NodePath SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node(int p_joint_idx) const { |
370 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), NodePath(), "CCDIK joint out of range!" ); |
371 | return ccdik_data_chain[p_joint_idx].bone2d_node; |
372 | } |
373 | |
374 | void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) { |
375 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCCDIK joint out of range!" ); |
376 | ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!" ); |
377 | |
378 | if (is_setup) { |
379 | if (stack->skeleton) { |
380 | ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!" ); |
381 | ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
382 | ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); |
383 | ccdik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); |
384 | } else { |
385 | WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..." ); |
386 | ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
387 | } |
388 | } else { |
389 | WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..." ); |
390 | ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; |
391 | } |
392 | |
393 | notify_property_list_changed(); |
394 | } |
395 | |
396 | int SkeletonModification2DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const { |
397 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), -1, "CCDIK joint out of range!" ); |
398 | return ccdik_data_chain[p_joint_idx].bone_idx; |
399 | } |
400 | |
401 | void SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint) { |
402 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
403 | ccdik_data_chain.write[p_joint_idx].rotate_from_joint = p_rotate_from_joint; |
404 | } |
405 | |
406 | bool SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint(int p_joint_idx) const { |
407 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!" ); |
408 | return ccdik_data_chain[p_joint_idx].rotate_from_joint; |
409 | } |
410 | |
411 | void SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint) { |
412 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
413 | ccdik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; |
414 | notify_property_list_changed(); |
415 | |
416 | #ifdef TOOLS_ENABLED |
417 | if (stack && is_setup) { |
418 | stack->set_editor_gizmos_dirty(true); |
419 | } |
420 | #endif // TOOLS_ENABLED |
421 | } |
422 | |
423 | bool SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const { |
424 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!" ); |
425 | return ccdik_data_chain[p_joint_idx].enable_constraint; |
426 | } |
427 | |
428 | void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) { |
429 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
430 | ccdik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min; |
431 | |
432 | #ifdef TOOLS_ENABLED |
433 | if (stack && is_setup) { |
434 | stack->set_editor_gizmos_dirty(true); |
435 | } |
436 | #endif // TOOLS_ENABLED |
437 | } |
438 | |
439 | float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const { |
440 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!" ); |
441 | return ccdik_data_chain[p_joint_idx].constraint_angle_min; |
442 | } |
443 | |
444 | void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) { |
445 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
446 | ccdik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max; |
447 | |
448 | #ifdef TOOLS_ENABLED |
449 | if (stack && is_setup) { |
450 | stack->set_editor_gizmos_dirty(true); |
451 | } |
452 | #endif // TOOLS_ENABLED |
453 | } |
454 | |
455 | float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const { |
456 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!" ); |
457 | return ccdik_data_chain[p_joint_idx].constraint_angle_max; |
458 | } |
459 | |
460 | void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) { |
461 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
462 | ccdik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert; |
463 | |
464 | #ifdef TOOLS_ENABLED |
465 | if (stack && is_setup) { |
466 | stack->set_editor_gizmos_dirty(true); |
467 | } |
468 | #endif // TOOLS_ENABLED |
469 | } |
470 | |
471 | bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const { |
472 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!" ); |
473 | return ccdik_data_chain[p_joint_idx].constraint_angle_invert; |
474 | } |
475 | |
476 | void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { |
477 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
478 | ccdik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace; |
479 | |
480 | #ifdef TOOLS_ENABLED |
481 | if (stack && is_setup) { |
482 | stack->set_editor_gizmos_dirty(true); |
483 | } |
484 | #endif // TOOLS_ENABLED |
485 | } |
486 | |
487 | bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const { |
488 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!" ); |
489 | return ccdik_data_chain[p_joint_idx].constraint_in_localspace; |
490 | } |
491 | |
492 | void SkeletonModification2DCCDIK::set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { |
493 | ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!" ); |
494 | ccdik_data_chain.write[p_joint_idx].editor_draw_gizmo = p_draw_gizmo; |
495 | |
496 | #ifdef TOOLS_ENABLED |
497 | if (stack && is_setup) { |
498 | stack->set_editor_gizmos_dirty(true); |
499 | } |
500 | #endif // TOOLS_ENABLED |
501 | } |
502 | |
503 | bool SkeletonModification2DCCDIK::get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const { |
504 | ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!" ); |
505 | return ccdik_data_chain[p_joint_idx].editor_draw_gizmo; |
506 | } |
507 | |
508 | void SkeletonModification2DCCDIK::_bind_methods() { |
509 | ClassDB::bind_method(D_METHOD("set_target_node" , "target_nodepath" ), &SkeletonModification2DCCDIK::set_target_node); |
510 | ClassDB::bind_method(D_METHOD("get_target_node" ), &SkeletonModification2DCCDIK::get_target_node); |
511 | ClassDB::bind_method(D_METHOD("set_tip_node" , "tip_nodepath" ), &SkeletonModification2DCCDIK::set_tip_node); |
512 | ClassDB::bind_method(D_METHOD("get_tip_node" ), &SkeletonModification2DCCDIK::get_tip_node); |
513 | |
514 | ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length" , "length" ), &SkeletonModification2DCCDIK::set_ccdik_data_chain_length); |
515 | ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length" ), &SkeletonModification2DCCDIK::get_ccdik_data_chain_length); |
516 | |
517 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone2d_node" , "joint_idx" , "bone2d_nodepath" ), &SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node); |
518 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone2d_node" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node); |
519 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index" , "joint_idx" , "bone_idx" ), &SkeletonModification2DCCDIK::set_ccdik_joint_bone_index); |
520 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_bone_index); |
521 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_rotate_from_joint" , "joint_idx" , "rotate_from_joint" ), &SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint); |
522 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_rotate_from_joint" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint); |
523 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_constraint" , "joint_idx" , "enable_constraint" ), &SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint); |
524 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_constraint" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint); |
525 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min" , "joint_idx" , "angle_min" ), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min); |
526 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min); |
527 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max" , "joint_idx" , "angle_max" ), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max); |
528 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max); |
529 | ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_invert" , "joint_idx" , "invert" ), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert); |
530 | ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_invert" , "joint_idx" ), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert); |
531 | |
532 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D" ), "set_target_node" , "get_target_node" ); |
533 | ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath" , PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D" ), "set_tip_node" , "get_tip_node" ); |
534 | ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length" , PROPERTY_HINT_RANGE, "0, 100, 1" ), "set_ccdik_data_chain_length" , "get_ccdik_data_chain_length" ); |
535 | } |
536 | |
537 | SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { |
538 | stack = nullptr; |
539 | is_setup = false; |
540 | enabled = true; |
541 | editor_draw_gizmo = true; |
542 | } |
543 | |
544 | SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { |
545 | } |
546 | |