Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/3d/bone_twist_disperser_3d.cpp
21117 views
1
/**************************************************************************/
2
/* bone_twist_disperser_3d.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 "bone_twist_disperser_3d.h"
32
33
bool BoneTwistDisperser3D::_set(const StringName &p_path, const Variant &p_value) {
34
String path = p_path;
35
36
if (path.begins_with("settings/")) {
37
int which = path.get_slicec('/', 1).to_int();
38
String what = path.get_slicec('/', 2);
39
ERR_FAIL_INDEX_V(which, (int)settings.size(), false);
40
41
if (what == "root_bone_name") {
42
set_root_bone_name(which, p_value);
43
} else if (what == "root_bone") {
44
set_root_bone(which, p_value);
45
} else if (what == "end_bone_name") {
46
set_end_bone_name(which, p_value);
47
} else if (what == "end_bone") {
48
set_end_bone(which, p_value);
49
} else if (what == "end_bone_direction") {
50
set_end_bone_direction(which, static_cast<BoneDirection>((int)p_value));
51
} else if (what == "extend_end_bone") {
52
set_extend_end_bone(which, p_value);
53
} else if (what == "twist_from_rest") {
54
set_twist_from_rest(which, p_value);
55
} else if (what == "twist_from") {
56
set_twist_from(which, p_value);
57
} else if (what == "disperse_mode") {
58
set_disperse_mode(which, static_cast<DisperseMode>((int)p_value));
59
} else if (what == "weight_position") {
60
set_weight_position(which, p_value);
61
} else if (what == "damping_curve") {
62
set_damping_curve(which, p_value);
63
} else if (what == "joint_count") {
64
set_joint_count(which, p_value);
65
} else if (what == "joints") {
66
int idx = path.get_slicec('/', 3).to_int();
67
String prop = path.get_slicec('/', 4);
68
if (prop == "twist_amount") {
69
set_joint_twist_amount(which, idx, p_value);
70
} else {
71
return false;
72
}
73
} else {
74
return false;
75
}
76
}
77
return true;
78
}
79
80
bool BoneTwistDisperser3D::_get(const StringName &p_path, Variant &r_ret) const {
81
String path = p_path;
82
83
if (path.begins_with("settings/")) {
84
int which = path.get_slicec('/', 1).to_int();
85
String what = path.get_slicec('/', 2);
86
ERR_FAIL_INDEX_V(which, (int)settings.size(), false);
87
88
if (what == "root_bone_name") {
89
r_ret = get_root_bone_name(which);
90
} else if (what == "root_bone") {
91
r_ret = get_root_bone(which);
92
} else if (what == "end_bone_name") {
93
r_ret = get_end_bone_name(which);
94
} else if (what == "end_bone") {
95
r_ret = get_end_bone(which);
96
} else if (what == "end_bone_direction") {
97
r_ret = (int)get_end_bone_direction(which);
98
} else if (what == "reference_bone_name") {
99
r_ret = get_reference_bone_name(which);
100
} else if (what == "extend_end_bone") {
101
r_ret = is_end_bone_extended(which);
102
} else if (what == "twist_from_rest") {
103
r_ret = is_twist_from_rest(which);
104
} else if (what == "twist_from") {
105
r_ret = get_twist_from(which);
106
} else if (what == "disperse_mode") {
107
r_ret = (int)get_disperse_mode(which);
108
} else if (what == "weight_position") {
109
r_ret = get_weight_position(which);
110
} else if (what == "damping_curve") {
111
r_ret = get_damping_curve(which);
112
} else if (what == "joint_count") {
113
r_ret = get_joint_count(which);
114
} else if (what == "joints") {
115
int idx = path.get_slicec('/', 3).to_int();
116
String prop = path.get_slicec('/', 4);
117
if (prop == "bone_name") {
118
r_ret = get_joint_bone_name(which, idx);
119
} else if (prop == "bone") {
120
r_ret = get_joint_bone(which, idx);
121
} else if (prop == "twist_amount") {
122
r_ret = get_joint_twist_amount(which, idx);
123
} else {
124
return false;
125
}
126
} else {
127
return false;
128
}
129
}
130
return true;
131
}
132
133
void BoneTwistDisperser3D::_get_property_list(List<PropertyInfo> *p_list) const {
134
String enum_hint;
135
Skeleton3D *skeleton = get_skeleton();
136
if (skeleton) {
137
enum_hint = skeleton->get_concatenated_bone_names();
138
}
139
140
LocalVector<PropertyInfo> props;
141
142
for (uint32_t i = 0; i < settings.size(); i++) {
143
String path = "settings/" + itos(i) + "/";
144
props.push_back(PropertyInfo(Variant::STRING, path + "root_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
145
props.push_back(PropertyInfo(Variant::INT, path + "root_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
146
props.push_back(PropertyInfo(Variant::STRING, path + "end_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint));
147
props.push_back(PropertyInfo(Variant::INT, path + "end_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
148
props.push_back(PropertyInfo(Variant::BOOL, path + "extend_end_bone"));
149
props.push_back(PropertyInfo(Variant::INT, path + "end_bone_direction", PROPERTY_HINT_ENUM, SkeletonModifier3D::get_hint_bone_direction()));
150
151
props.push_back(PropertyInfo(Variant::STRING, path + "reference_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
152
props.push_back(PropertyInfo(Variant::BOOL, path + "twist_from_rest"));
153
props.push_back(PropertyInfo(Variant::QUATERNION, path + "twist_from"));
154
props.push_back(PropertyInfo(Variant::INT, path + "disperse_mode", PROPERTY_HINT_ENUM, "Even,Weighted,Custom"));
155
props.push_back(PropertyInfo(Variant::FLOAT, path + "weight_position", PROPERTY_HINT_RANGE, "0,1,0.001"));
156
props.push_back(PropertyInfo(Variant::OBJECT, path + "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, Curve::get_class_static()));
157
158
props.push_back(PropertyInfo(Variant::INT, path + "joint_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Joints," + path + "joints/,static,const"));
159
for (uint32_t j = 0; j < settings[i]->joints.size(); j++) {
160
String joint_path = path + "joints/" + itos(j) + "/";
161
props.push_back(PropertyInfo(Variant::STRING, joint_path + "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, enum_hint, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
162
props.push_back(PropertyInfo(Variant::INT, joint_path + "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY));
163
props.push_back(PropertyInfo(Variant::FLOAT, joint_path + "twist_amount", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,or_less"));
164
}
165
}
166
167
for (PropertyInfo &p : props) {
168
_validate_dynamic_prop(p);
169
p_list->push_back(p);
170
}
171
}
172
173
void BoneTwistDisperser3D::_validate_dynamic_prop(PropertyInfo &p_property) const {
174
PackedStringArray split = p_property.name.split("/");
175
if (split.size() > 2 && split[0] == "settings") {
176
int which = split[1].to_int();
177
178
// Extended end bone option.
179
bool force_hide = false;
180
if (split[2] == "extend_end_bone" && get_end_bone(which) == -1) {
181
p_property.usage = PROPERTY_USAGE_NONE;
182
force_hide = true;
183
}
184
if (force_hide || (split[2] == "end_bone_direction" && !is_end_bone_extended(which))) {
185
p_property.usage = PROPERTY_USAGE_NONE;
186
}
187
188
if (split[2] == "twist_from" && is_twist_from_rest(which)) {
189
p_property.usage = PROPERTY_USAGE_NONE;
190
}
191
192
if (split[2] == "weight_position" && get_disperse_mode(which) != DISPERSE_MODE_WEIGHTED) {
193
p_property.usage = PROPERTY_USAGE_NONE;
194
}
195
196
if (split[2] == "damping_curve" && get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {
197
p_property.usage = PROPERTY_USAGE_NONE;
198
}
199
200
if (split[2] == "joints" && split[4] == "twist_amount") {
201
bool mutable_amount = true;
202
if (get_disperse_mode(which) != DISPERSE_MODE_CUSTOM) {
203
mutable_amount = false;
204
} else if (!is_end_bone_extended(which)) {
205
int joint = split[3].to_int();
206
mutable_amount = joint < get_joint_count(which) - 1; // Hide child of reference bone.
207
}
208
if (get_damping_curve(which).is_valid()) {
209
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
210
}
211
if (!mutable_amount) {
212
p_property.usage = PROPERTY_USAGE_NONE;
213
}
214
}
215
}
216
}
217
218
void BoneTwistDisperser3D::_notification(int p_what) {
219
switch (p_what) {
220
case NOTIFICATION_ENTER_TREE: {
221
_make_all_joints_dirty();
222
} break;
223
}
224
}
225
226
void BoneTwistDisperser3D::_set_active(bool p_active) {
227
if (p_active) {
228
_make_all_joints_dirty();
229
}
230
}
231
232
void BoneTwistDisperser3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
233
_make_all_joints_dirty();
234
}
235
236
// Setting.
237
238
void BoneTwistDisperser3D::set_mutable_bone_axes(bool p_enabled) {
239
mutable_bone_axes = p_enabled;
240
}
241
242
bool BoneTwistDisperser3D::are_bone_axes_mutable() const {
243
return mutable_bone_axes;
244
}
245
246
void BoneTwistDisperser3D::set_root_bone_name(int p_index, const String &p_bone_name) {
247
ERR_FAIL_INDEX(p_index, (int)settings.size());
248
settings[p_index]->root_bone.name = p_bone_name;
249
Skeleton3D *sk = get_skeleton();
250
if (sk) {
251
set_root_bone(p_index, sk->find_bone(settings[p_index]->root_bone.name));
252
}
253
}
254
255
String BoneTwistDisperser3D::get_root_bone_name(int p_index) const {
256
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
257
return settings[p_index]->root_bone.name;
258
}
259
260
void BoneTwistDisperser3D::set_root_bone(int p_index, int p_bone) {
261
ERR_FAIL_INDEX(p_index, (int)settings.size());
262
bool changed = settings[p_index]->root_bone.bone != p_bone;
263
settings[p_index]->root_bone.bone = p_bone;
264
Skeleton3D *sk = get_skeleton();
265
if (sk) {
266
if (settings[p_index]->root_bone.bone <= -1 || settings[p_index]->root_bone.bone >= sk->get_bone_count()) {
267
WARN_PRINT("Root bone index out of range!");
268
settings[p_index]->root_bone.bone = -1;
269
} else {
270
settings[p_index]->root_bone.name = sk->get_bone_name(settings[p_index]->root_bone.bone);
271
}
272
}
273
if (changed) {
274
_make_joints_dirty(p_index);
275
}
276
}
277
278
int BoneTwistDisperser3D::get_root_bone(int p_index) const {
279
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
280
return settings[p_index]->root_bone.bone;
281
}
282
283
void BoneTwistDisperser3D::set_end_bone_name(int p_index, const String &p_bone_name) {
284
ERR_FAIL_INDEX(p_index, (int)settings.size());
285
settings[p_index]->end_bone.name = p_bone_name;
286
Skeleton3D *sk = get_skeleton();
287
if (sk) {
288
set_end_bone(p_index, sk->find_bone(settings[p_index]->end_bone.name));
289
}
290
}
291
292
String BoneTwistDisperser3D::get_end_bone_name(int p_index) const {
293
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
294
return settings[p_index]->end_bone.name;
295
}
296
297
void BoneTwistDisperser3D::set_end_bone(int p_index, int p_bone) {
298
ERR_FAIL_INDEX(p_index, (int)settings.size());
299
bool changed = settings[p_index]->end_bone.bone != p_bone;
300
settings[p_index]->end_bone.bone = p_bone;
301
Skeleton3D *sk = get_skeleton();
302
if (sk) {
303
if (settings[p_index]->end_bone.bone <= -1 || settings[p_index]->end_bone.bone >= sk->get_bone_count()) {
304
WARN_PRINT("End bone index out of range!");
305
settings[p_index]->end_bone.bone = -1;
306
} else {
307
settings[p_index]->end_bone.name = sk->get_bone_name(settings[p_index]->end_bone.bone);
308
}
309
}
310
if (changed) {
311
_make_joints_dirty(p_index);
312
}
313
notify_property_list_changed();
314
}
315
316
int BoneTwistDisperser3D::get_end_bone(int p_index) const {
317
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
318
return settings[p_index]->end_bone.bone;
319
}
320
321
void BoneTwistDisperser3D::set_extend_end_bone(int p_index, bool p_enabled) {
322
ERR_FAIL_INDEX(p_index, (int)settings.size());
323
settings[p_index]->extend_end_bone = p_enabled;
324
_update_reference_bone(p_index);
325
notify_property_list_changed();
326
}
327
328
bool BoneTwistDisperser3D::is_end_bone_extended(int p_index) const {
329
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), false);
330
return settings[p_index]->extend_end_bone;
331
}
332
333
void BoneTwistDisperser3D::set_end_bone_direction(int p_index, BoneDirection p_bone_direction) {
334
ERR_FAIL_INDEX(p_index, (int)settings.size());
335
settings[p_index]->end_bone_direction = p_bone_direction;
336
}
337
338
SkeletonModifier3D::BoneDirection BoneTwistDisperser3D::get_end_bone_direction(int p_index) const {
339
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), BONE_DIRECTION_FROM_PARENT);
340
return settings[p_index]->end_bone_direction;
341
}
342
343
void BoneTwistDisperser3D::set_twist_from_rest(int p_index, bool p_enabled) {
344
ERR_FAIL_INDEX(p_index, (int)settings.size());
345
settings[p_index]->twist_from_rest = p_enabled;
346
notify_property_list_changed();
347
}
348
349
bool BoneTwistDisperser3D::is_twist_from_rest(int p_index) const {
350
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), true);
351
return settings[p_index]->twist_from_rest;
352
}
353
354
void BoneTwistDisperser3D::set_twist_from(int p_index, const Quaternion &p_from) {
355
ERR_FAIL_INDEX(p_index, (int)settings.size());
356
settings[p_index]->twist_from = p_from;
357
}
358
359
Quaternion BoneTwistDisperser3D::get_twist_from(int p_index) const {
360
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Quaternion());
361
return settings[p_index]->twist_from;
362
}
363
364
void BoneTwistDisperser3D::_update_reference_bone(int p_index) {
365
ERR_FAIL_INDEX(p_index, (int)settings.size());
366
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
367
if (joints.size() >= 2) {
368
if (settings[p_index]->extend_end_bone) {
369
settings[p_index]->reference_bone = settings[p_index]->end_bone;
370
_update_curve(p_index);
371
return;
372
} else {
373
Skeleton3D *sk = get_skeleton();
374
if (sk) {
375
int parent = sk->get_bone_parent(settings[p_index]->end_bone.bone);
376
if (parent >= 0) {
377
settings[p_index]->reference_bone.bone = parent;
378
settings[p_index]->reference_bone.name = sk->get_bone_name(parent);
379
_update_curve(p_index);
380
return;
381
}
382
}
383
}
384
}
385
settings[p_index]->reference_bone.bone = -1;
386
settings[p_index]->reference_bone.name = String();
387
}
388
389
void BoneTwistDisperser3D::_update_curve(int p_index) {
390
Ref<Curve> curve = settings[p_index]->damping_curve;
391
if (curve.is_null()) {
392
return;
393
}
394
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
395
float unit = (int)joints.size() > 0 ? (1.0 / float((int)joints.size() - 1)) : 0.0;
396
for (uint32_t i = 0; i < joints.size(); i++) {
397
joints[i].custom_amount = curve->sample_baked(i * unit);
398
}
399
}
400
401
String BoneTwistDisperser3D::get_reference_bone_name(int p_index) const {
402
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
403
return settings[p_index]->reference_bone.name;
404
}
405
406
int BoneTwistDisperser3D::get_reference_bone(int p_index) const {
407
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
408
return settings[p_index]->reference_bone.bone;
409
}
410
411
void BoneTwistDisperser3D::set_disperse_mode(int p_index, DisperseMode p_disperse_mode) {
412
ERR_FAIL_INDEX(p_index, (int)settings.size());
413
settings[p_index]->disperse_mode = p_disperse_mode;
414
notify_property_list_changed();
415
}
416
417
BoneTwistDisperser3D::DisperseMode BoneTwistDisperser3D::get_disperse_mode(int p_index) const {
418
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), DISPERSE_MODE_EVEN);
419
return settings[p_index]->disperse_mode;
420
}
421
422
void BoneTwistDisperser3D::set_weight_position(int p_index, float p_position) {
423
ERR_FAIL_INDEX(p_index, (int)settings.size());
424
settings[p_index]->weight_position = p_position;
425
}
426
427
float BoneTwistDisperser3D::get_weight_position(int p_index) const {
428
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0.0);
429
return settings[p_index]->weight_position;
430
}
431
432
void BoneTwistDisperser3D::set_damping_curve(int p_index, const Ref<Curve> &p_damping_curve) {
433
ERR_FAIL_INDEX(p_index, (int)settings.size());
434
bool changed = settings[p_index]->damping_curve != p_damping_curve;
435
if (settings[p_index]->damping_curve.is_valid()) {
436
settings[p_index]->damping_curve->disconnect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve));
437
}
438
settings[p_index]->damping_curve = p_damping_curve;
439
if (settings[p_index]->damping_curve.is_valid()) {
440
settings[p_index]->damping_curve->connect_changed(callable_mp(this, &BoneTwistDisperser3D::_update_curve).bind(p_index));
441
}
442
if (changed) {
443
_make_joints_dirty(p_index);
444
}
445
notify_property_list_changed();
446
}
447
448
Ref<Curve> BoneTwistDisperser3D::get_damping_curve(int p_index) const {
449
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), Ref<Curve>());
450
return settings[p_index]->damping_curve;
451
}
452
453
// Individual joints.
454
455
String BoneTwistDisperser3D::get_joint_bone_name(int p_index, int p_joint) const {
456
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), String());
457
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
458
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), String());
459
return joints[p_joint].joint.name;
460
}
461
462
void BoneTwistDisperser3D::_set_joint_bone(int p_index, int p_joint, int p_bone) {
463
ERR_FAIL_INDEX(p_index, (int)settings.size());
464
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
465
ERR_FAIL_INDEX(p_joint, (int)joints.size());
466
joints[p_joint].joint.bone = p_bone;
467
Skeleton3D *sk = get_skeleton();
468
if (sk) {
469
if (joints[p_joint].joint.bone <= -1 || joints[p_joint].joint.bone >= sk->get_bone_count()) {
470
WARN_PRINT("Joint bone index out of range!");
471
joints[p_joint].joint.bone = -1;
472
} else {
473
joints[p_joint].joint.name = sk->get_bone_name(joints[p_joint].joint.bone);
474
}
475
}
476
}
477
478
int BoneTwistDisperser3D::get_joint_bone(int p_index, int p_joint) const {
479
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), -1);
480
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
481
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), -1);
482
return joints[p_joint].joint.bone;
483
}
484
485
void BoneTwistDisperser3D::set_joint_count(int p_index, int p_count) {
486
ERR_FAIL_INDEX(p_index, (int)settings.size());
487
ERR_FAIL_COND(p_count < 0);
488
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
489
joints.resize(p_count);
490
notify_property_list_changed();
491
}
492
493
int BoneTwistDisperser3D::get_joint_count(int p_index) const {
494
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);
495
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
496
return joints.size();
497
}
498
499
void BoneTwistDisperser3D::set_joint_twist_amount(int p_index, int p_joint, float p_amount) {
500
ERR_FAIL_INDEX(p_index, (int)settings.size());
501
LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
502
ERR_FAIL_INDEX(p_joint, (int)joints.size());
503
joints[p_joint].custom_amount = p_amount;
504
}
505
506
float BoneTwistDisperser3D::get_joint_twist_amount(int p_index, int p_joint) const {
507
ERR_FAIL_INDEX_V(p_index, (int)settings.size(), 0);
508
const LocalVector<DisperseJointSetting> &joints = settings[p_index]->joints;
509
ERR_FAIL_INDEX_V(p_joint, (int)joints.size(), 0);
510
return joints[p_joint].custom_amount;
511
}
512
513
void BoneTwistDisperser3D::_bind_methods() {
514
ClassDB::bind_method(D_METHOD("set_setting_count", "count"), &BoneTwistDisperser3D::set_setting_count);
515
ClassDB::bind_method(D_METHOD("get_setting_count"), &BoneTwistDisperser3D::get_setting_count);
516
ClassDB::bind_method(D_METHOD("clear_settings"), &BoneTwistDisperser3D::clear_settings);
517
518
ClassDB::bind_method(D_METHOD("set_mutable_bone_axes", "enabled"), &BoneTwistDisperser3D::set_mutable_bone_axes);
519
ClassDB::bind_method(D_METHOD("are_bone_axes_mutable"), &BoneTwistDisperser3D::are_bone_axes_mutable);
520
521
// Setting.
522
ClassDB::bind_method(D_METHOD("set_root_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_root_bone_name);
523
ClassDB::bind_method(D_METHOD("get_root_bone_name", "index"), &BoneTwistDisperser3D::get_root_bone_name);
524
ClassDB::bind_method(D_METHOD("set_root_bone", "index", "bone"), &BoneTwistDisperser3D::set_root_bone);
525
ClassDB::bind_method(D_METHOD("get_root_bone", "index"), &BoneTwistDisperser3D::get_root_bone);
526
527
ClassDB::bind_method(D_METHOD("set_end_bone_name", "index", "bone_name"), &BoneTwistDisperser3D::set_end_bone_name);
528
ClassDB::bind_method(D_METHOD("get_end_bone_name", "index"), &BoneTwistDisperser3D::get_end_bone_name);
529
ClassDB::bind_method(D_METHOD("set_end_bone", "index", "bone"), &BoneTwistDisperser3D::set_end_bone);
530
ClassDB::bind_method(D_METHOD("get_end_bone", "index"), &BoneTwistDisperser3D::get_end_bone);
531
532
ClassDB::bind_method(D_METHOD("get_reference_bone_name", "index"), &BoneTwistDisperser3D::get_reference_bone_name);
533
ClassDB::bind_method(D_METHOD("get_reference_bone", "index"), &BoneTwistDisperser3D::get_reference_bone);
534
535
ClassDB::bind_method(D_METHOD("set_extend_end_bone", "index", "enabled"), &BoneTwistDisperser3D::set_extend_end_bone);
536
ClassDB::bind_method(D_METHOD("is_end_bone_extended", "index"), &BoneTwistDisperser3D::is_end_bone_extended);
537
ClassDB::bind_method(D_METHOD("set_end_bone_direction", "index", "bone_direction"), &BoneTwistDisperser3D::set_end_bone_direction);
538
ClassDB::bind_method(D_METHOD("get_end_bone_direction", "index"), &BoneTwistDisperser3D::get_end_bone_direction);
539
540
ClassDB::bind_method(D_METHOD("set_twist_from_rest", "index", "enabled"), &BoneTwistDisperser3D::set_twist_from_rest);
541
ClassDB::bind_method(D_METHOD("is_twist_from_rest", "index"), &BoneTwistDisperser3D::is_twist_from_rest);
542
ClassDB::bind_method(D_METHOD("set_twist_from", "index", "from"), &BoneTwistDisperser3D::set_twist_from);
543
ClassDB::bind_method(D_METHOD("get_twist_from", "index"), &BoneTwistDisperser3D::get_twist_from);
544
545
ClassDB::bind_method(D_METHOD("set_disperse_mode", "index", "disperse_mode"), &BoneTwistDisperser3D::set_disperse_mode);
546
ClassDB::bind_method(D_METHOD("get_disperse_mode", "index"), &BoneTwistDisperser3D::get_disperse_mode);
547
ClassDB::bind_method(D_METHOD("set_weight_position", "index", "weight_position"), &BoneTwistDisperser3D::set_weight_position);
548
ClassDB::bind_method(D_METHOD("get_weight_position", "index"), &BoneTwistDisperser3D::get_weight_position);
549
ClassDB::bind_method(D_METHOD("set_damping_curve", "index", "curve"), &BoneTwistDisperser3D::set_damping_curve);
550
ClassDB::bind_method(D_METHOD("get_damping_curve", "index"), &BoneTwistDisperser3D::get_damping_curve);
551
552
// Individual joints.
553
ClassDB::bind_method(D_METHOD("get_joint_bone_name", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone_name);
554
ClassDB::bind_method(D_METHOD("get_joint_bone", "index", "joint"), &BoneTwistDisperser3D::get_joint_bone);
555
ClassDB::bind_method(D_METHOD("get_joint_twist_amount", "index", "joint"), &BoneTwistDisperser3D::get_joint_twist_amount);
556
ClassDB::bind_method(D_METHOD("set_joint_twist_amount", "index", "joint", "twist_amount"), &BoneTwistDisperser3D::set_joint_twist_amount);
557
558
ClassDB::bind_method(D_METHOD("get_joint_count", "index"), &BoneTwistDisperser3D::get_joint_count);
559
560
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mutable_bone_axes"), "set_mutable_bone_axes", "are_bone_axes_mutable");
561
ADD_ARRAY_COUNT("Settings", "setting_count", "set_setting_count", "get_setting_count", "settings/");
562
563
BIND_ENUM_CONSTANT(DISPERSE_MODE_EVEN);
564
BIND_ENUM_CONSTANT(DISPERSE_MODE_WEIGHTED);
565
BIND_ENUM_CONSTANT(DISPERSE_MODE_CUSTOM);
566
}
567
568
void BoneTwistDisperser3D::_validate_bone_names() {
569
for (uint32_t i = 0; i < settings.size(); i++) {
570
// Prior bone name.
571
if (!settings[i]->root_bone.name.is_empty()) {
572
set_root_bone_name(i, settings[i]->root_bone.name);
573
} else if (settings[i]->root_bone.bone != -1) {
574
set_root_bone(i, settings[i]->root_bone.bone);
575
}
576
// Prior bone name.
577
if (!settings[i]->end_bone.name.is_empty()) {
578
set_end_bone_name(i, settings[i]->end_bone.name);
579
} else if (settings[i]->end_bone.bone != -1) {
580
set_end_bone(i, settings[i]->end_bone.bone);
581
}
582
}
583
}
584
585
void BoneTwistDisperser3D::_make_all_joints_dirty() {
586
for (uint32_t i = 0; i < settings.size(); i++) {
587
_make_joints_dirty(i);
588
}
589
}
590
591
void BoneTwistDisperser3D::_make_joints_dirty(int p_index) {
592
ERR_FAIL_INDEX(p_index, (int)settings.size());
593
if (settings[p_index]->joints_dirty) {
594
return;
595
}
596
settings[p_index]->joints_dirty = true;
597
callable_mp(this, &BoneTwistDisperser3D::_update_joints).call_deferred(p_index);
598
}
599
600
void BoneTwistDisperser3D::_update_joints(int p_index) {
601
Skeleton3D *sk = get_skeleton();
602
int current_bone = settings[p_index]->end_bone.bone;
603
int root_bone = settings[p_index]->root_bone.bone;
604
if (!sk || current_bone < 0 || root_bone < 0) {
605
set_joint_count(p_index, 0);
606
settings[p_index]->joints_dirty = false;
607
return;
608
}
609
610
// Validation.
611
bool valid = false;
612
while (current_bone >= 0) {
613
current_bone = sk->get_bone_parent(current_bone);
614
if (current_bone == root_bone) {
615
valid = true;
616
break;
617
}
618
}
619
620
if (!valid) {
621
set_joint_count(p_index, 0);
622
_update_reference_bone(p_index);
623
settings[p_index]->joints_dirty = false;
624
ERR_FAIL_EDMSG("End bone must be a child of the root bone.");
625
}
626
627
Vector<int> new_joints;
628
current_bone = settings[p_index]->end_bone.bone;
629
while (current_bone != root_bone) {
630
new_joints.push_back(current_bone);
631
current_bone = sk->get_bone_parent(current_bone);
632
}
633
new_joints.push_back(current_bone);
634
new_joints.reverse();
635
636
set_joint_count(p_index, new_joints.size());
637
for (uint32_t i = 0; i < new_joints.size(); i++) {
638
_set_joint_bone(p_index, i, new_joints[i]);
639
}
640
641
_update_reference_bone(p_index);
642
settings[p_index]->joints_dirty = false;
643
}
644
645
int BoneTwistDisperser3D::get_setting_count() const {
646
return (int)settings.size();
647
}
648
649
void BoneTwistDisperser3D::set_setting_count(int p_count) {
650
ERR_FAIL_COND(p_count < 0);
651
int delta = p_count - settings.size();
652
if (delta < 0) {
653
for (int i = delta; i < 0; i++) {
654
memdelete(settings[settings.size() + i]);
655
settings[settings.size() + i] = nullptr;
656
}
657
}
658
settings.resize(p_count);
659
delta++;
660
if (delta > 1) {
661
for (int i = 1; i < delta; i++) {
662
settings[p_count - i] = memnew(BoneTwistDisperser3DSetting);
663
}
664
}
665
notify_property_list_changed();
666
}
667
668
void BoneTwistDisperser3D::clear_settings() {
669
set_setting_count(0);
670
}
671
672
void BoneTwistDisperser3D::_process_modification(double p_delta) {
673
Skeleton3D *skeleton = get_skeleton();
674
if (!skeleton) {
675
return;
676
}
677
for (BoneTwistDisperser3DSetting *setting : settings) {
678
if (!setting || setting->reference_bone.bone < 0) {
679
continue;
680
}
681
LocalVector<DisperseJointSetting> &joints = setting->joints;
682
// Calc amount.
683
int actual_joint_size = setting->extend_end_bone ? (int)joints.size() : (int)joints.size() - 1;
684
if (actual_joint_size <= 1) {
685
continue;
686
}
687
if (setting->disperse_mode == DISPERSE_MODE_EVEN) {
688
double div = 1.0 / actual_joint_size;
689
for (int i = 0; i < actual_joint_size; i++) {
690
joints[i].amount = ((double)i + 1.0) * div;
691
}
692
} else if (setting->disperse_mode == DISPERSE_MODE_WEIGHTED) {
693
// Assign length for each bone.
694
double total_length = 0.0;
695
double weight_sub = 1.0 - setting->weight_position;
696
if (mutable_bone_axes) {
697
for (int i = 0; i < actual_joint_size; i++) {
698
double length = 0.0;
699
if (i == 0) {
700
length = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * setting->weight_position;
701
} else if (i == actual_joint_size - 1) {
702
length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * weight_sub;
703
} else {
704
length = skeleton->get_bone_pose_position(joints[i].joint.bone).length() * setting->weight_position + skeleton->get_bone_pose_position(joints[i + 1].joint.bone).length() * weight_sub;
705
}
706
total_length += length;
707
joints[i].amount = total_length;
708
}
709
} else {
710
for (int i = 0; i < actual_joint_size; i++) {
711
double length = 0.0;
712
if (i == 0) {
713
length = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * setting->weight_position;
714
} else if (i == actual_joint_size - 1) {
715
length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * weight_sub;
716
} else {
717
length = skeleton->get_bone_rest(joints[i].joint.bone).origin.length() * setting->weight_position + skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.length() * weight_sub;
718
}
719
total_length += length;
720
joints[i].amount = total_length;
721
}
722
}
723
if (Math::is_zero_approx(total_length)) {
724
continue;
725
}
726
// Normalize.
727
double div = 1.0 / total_length;
728
for (int i = 0; i < actual_joint_size; i++) {
729
joints[i].amount *= div;
730
}
731
} else {
732
for (int i = 0; i < actual_joint_size; i++) {
733
joints[i].amount = joints[i].custom_amount;
734
}
735
}
736
int end = actual_joint_size - 1;
737
joints[end].amount -= 1.0; // Remove twist from current pose.
738
739
// Retrieve axes.
740
if (mutable_bone_axes) {
741
for (int i = 0; i < end; i++) {
742
joints[i].axis = skeleton->get_bone_pose_position(joints[i + 1].joint.bone).normalized();
743
if (joints[i].axis.is_zero_approx() && i > 0) {
744
joints[i].axis = joints[i - 1].axis;
745
}
746
}
747
} else {
748
for (int i = 0; i < end; i++) {
749
joints[i].axis = skeleton->get_bone_rest(joints[i + 1].joint.bone).origin.normalized();
750
if (joints[i].axis.is_zero_approx() && i > 0) {
751
joints[i].axis = joints[i - 1].axis;
752
}
753
}
754
}
755
756
if (!setting->extend_end_bone) {
757
joints[end].axis = mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin;
758
joints[end].axis.normalize();
759
} else if (setting->end_bone_direction == BONE_DIRECTION_FROM_PARENT) {
760
joints[end].axis = skeleton->get_bone_rest(setting->end_bone.bone).basis.xform_inv(mutable_bone_axes ? skeleton->get_bone_pose_position(setting->end_bone.bone) : skeleton->get_bone_rest(setting->end_bone.bone).origin);
761
joints[end].axis.normalize();
762
} else {
763
joints[end].axis = get_vector_from_bone_axis(static_cast<BoneAxis>((int)setting->end_bone_direction));
764
}
765
if (joints[end].axis.is_zero_approx() && end > 0) {
766
joints[end].axis = joints[end - 1].axis;
767
}
768
769
// Extract twist.
770
Quaternion twist_rest = setting->twist_from_rest ? skeleton->get_bone_rest(setting->reference_bone.bone).basis.get_rotation_quaternion() : setting->twist_from.normalized();
771
Quaternion ref_rot = twist_rest.inverse() * skeleton->get_bone_pose_rotation(setting->reference_bone.bone);
772
ref_rot.normalize();
773
double twist = get_roll_angle(ref_rot, joints[end].axis);
774
775
// Apply twist for each bone by their amount.
776
// Twist parent, then cancel all twists caused by this modifier in child, and re-apply accumulated twist.
777
Quaternion prev_rot;
778
if (mutable_bone_axes) {
779
for (int i = 0; i < actual_joint_size; i++) {
780
int bn = joints[i].joint.bone;
781
Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);
782
skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);
783
prev_rot = cur_rot;
784
}
785
} else {
786
for (int i = 0; i < actual_joint_size; i++) {
787
int bn = joints[i].joint.bone;
788
Quaternion cur_rot = Quaternion(joints[i].axis, twist * joints[i].amount);
789
skeleton->set_bone_pose_rotation(bn, prev_rot.inverse() * skeleton->get_bone_pose_rotation(bn) * cur_rot);
790
prev_rot = cur_rot;
791
}
792
}
793
}
794
}
795
796
BoneTwistDisperser3D::~BoneTwistDisperser3D() {
797
clear_settings();
798
}
799
800