Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/3d/look_at_modifier_3d.cpp
21155 views
1
/**************************************************************************/
2
/* look_at_modifier_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 "look_at_modifier_3d.h"
32
33
#include "core/config/engine.h"
34
#include "scene/resources/animation.h"
35
36
void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const {
37
if (Engine::get_singleton()->is_editor_hint() && (p_property.name == "bone_name" || p_property.name == "origin_bone_name")) {
38
Skeleton3D *skeleton = get_skeleton();
39
if (skeleton) {
40
p_property.hint = PROPERTY_HINT_ENUM_SUGGESTION;
41
p_property.hint_string = skeleton->get_concatenated_bone_names();
42
} else {
43
p_property.hint = PROPERTY_HINT_NONE;
44
p_property.hint_string = "";
45
}
46
}
47
48
if (origin_from == ORIGIN_FROM_SPECIFIC_BONE) {
49
if (p_property.name == "origin_external_node") {
50
p_property.usage = PROPERTY_USAGE_NONE;
51
}
52
} else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) {
53
if (p_property.name == "origin_bone" || p_property.name == "origin_bone_name") {
54
p_property.usage = PROPERTY_USAGE_NONE;
55
}
56
} else {
57
if (p_property.name == "origin_external_node" || p_property.name == "origin_bone" || p_property.name == "origin_bone_name") {
58
p_property.usage = PROPERTY_USAGE_NONE;
59
}
60
}
61
62
if ((!use_angle_limitation &&
63
(p_property.name == "symmetry_limitation" || p_property.name.ends_with("limit_angle") || p_property.name.ends_with("damp_threshold"))) ||
64
(!use_secondary_rotation && p_property.name.begins_with("secondary_")) ||
65
(!symmetry_limitation && (p_property.name == "primary_limit_angle" || p_property.name == "primary_damp_threshold" || p_property.name == "secondary_limit_angle" || p_property.name == "secondary_damp_threshold")) ||
66
(symmetry_limitation && (p_property.name.begins_with("primary_positive") || p_property.name.begins_with("primary_negative") || p_property.name.begins_with("secondary_positive") || (p_property.name.begins_with("secondary_negative"))))) {
67
p_property.usage = PROPERTY_USAGE_NONE;
68
}
69
}
70
71
PackedStringArray LookAtModifier3D::get_configuration_warnings() const {
72
PackedStringArray warnings = SkeletonModifier3D::get_configuration_warnings();
73
if (get_axis_from_bone_axis(forward_axis) == primary_rotation_axis) {
74
warnings.push_back(RTR("Forward axis and primary rotation axis must not be parallel."));
75
}
76
return warnings;
77
}
78
79
void LookAtModifier3D::_validate_bone_names() {
80
// Prior bone name.
81
if (!bone_name.is_empty()) {
82
set_bone_name(bone_name);
83
} else if (bone != -1) {
84
set_bone(bone);
85
}
86
if (!origin_bone_name.is_empty()) {
87
set_origin_bone_name(origin_bone_name);
88
} else if (origin_bone != -1) {
89
set_origin_bone(origin_bone);
90
}
91
}
92
93
void LookAtModifier3D::set_bone_name(const String &p_bone_name) {
94
bone_name = p_bone_name;
95
Skeleton3D *sk = get_skeleton();
96
if (sk) {
97
set_bone(sk->find_bone(bone_name));
98
}
99
}
100
101
String LookAtModifier3D::get_bone_name() const {
102
return bone_name;
103
}
104
105
void LookAtModifier3D::set_bone(int p_bone) {
106
bone = p_bone;
107
Skeleton3D *sk = get_skeleton();
108
if (sk) {
109
if (bone <= -1 || bone >= sk->get_bone_count()) {
110
WARN_PRINT("Bone index out of range!");
111
bone = -1;
112
} else {
113
bone_name = sk->get_bone_name(bone);
114
}
115
}
116
}
117
118
int LookAtModifier3D::get_bone() const {
119
return bone;
120
}
121
122
void LookAtModifier3D::set_forward_axis(BoneAxis p_axis) {
123
forward_axis = p_axis;
124
update_configuration_warnings();
125
}
126
127
SkeletonModifier3D::BoneAxis LookAtModifier3D::get_forward_axis() const {
128
return forward_axis;
129
}
130
131
void LookAtModifier3D::set_primary_rotation_axis(Vector3::Axis p_axis) {
132
primary_rotation_axis = p_axis;
133
update_configuration_warnings();
134
}
135
136
Vector3::Axis LookAtModifier3D::get_primary_rotation_axis() const {
137
return primary_rotation_axis;
138
}
139
140
void LookAtModifier3D::set_use_secondary_rotation(bool p_enabled) {
141
use_secondary_rotation = p_enabled;
142
notify_property_list_changed();
143
}
144
145
bool LookAtModifier3D::is_using_secondary_rotation() const {
146
return use_secondary_rotation;
147
}
148
149
void LookAtModifier3D::set_relative(bool p_enabled) {
150
relative = p_enabled;
151
}
152
153
bool LookAtModifier3D::is_relative() const {
154
return relative;
155
}
156
157
void LookAtModifier3D::set_target_node(const NodePath &p_target_node) {
158
if (target_node != p_target_node) {
159
init_transition();
160
}
161
target_node = p_target_node;
162
}
163
164
NodePath LookAtModifier3D::get_target_node() const {
165
return target_node;
166
}
167
168
// For origin settings.
169
170
void LookAtModifier3D::set_origin_from(OriginFrom p_origin_from) {
171
origin_from = p_origin_from;
172
notify_property_list_changed();
173
}
174
175
LookAtModifier3D::OriginFrom LookAtModifier3D::get_origin_from() const {
176
return origin_from;
177
}
178
179
void LookAtModifier3D::set_origin_bone_name(const String &p_bone_name) {
180
origin_bone_name = p_bone_name;
181
Skeleton3D *sk = get_skeleton();
182
if (sk) {
183
set_origin_bone(sk->find_bone(origin_bone_name));
184
}
185
}
186
187
String LookAtModifier3D::get_origin_bone_name() const {
188
return origin_bone_name;
189
}
190
191
void LookAtModifier3D::set_origin_bone(int p_bone) {
192
origin_bone = p_bone;
193
Skeleton3D *sk = get_skeleton();
194
if (sk) {
195
if (origin_bone <= -1 || origin_bone >= sk->get_bone_count()) {
196
WARN_PRINT("Bone index out of range!");
197
origin_bone = -1;
198
} else {
199
origin_bone_name = sk->get_bone_name(origin_bone);
200
}
201
}
202
}
203
204
int LookAtModifier3D::get_origin_bone() const {
205
return origin_bone;
206
}
207
208
void LookAtModifier3D::set_origin_external_node(const NodePath &p_external_node) {
209
origin_external_node = p_external_node;
210
}
211
212
NodePath LookAtModifier3D::get_origin_external_node() const {
213
return origin_external_node;
214
}
215
216
void LookAtModifier3D::set_origin_offset(const Vector3 &p_offset) {
217
origin_offset = p_offset;
218
}
219
220
Vector3 LookAtModifier3D::get_origin_offset() const {
221
return origin_offset;
222
}
223
224
void LookAtModifier3D::set_origin_safe_margin(float p_margin) {
225
origin_safe_margin = p_margin;
226
}
227
228
float LookAtModifier3D::get_origin_safe_margin() const {
229
return origin_safe_margin;
230
}
231
232
// For time-based interpolation.
233
234
void LookAtModifier3D::set_duration(float p_duration) {
235
duration = p_duration;
236
if (Math::is_zero_approx(p_duration)) {
237
time_step = 0;
238
remaining = 0;
239
} else {
240
time_step = 1.0 / p_duration; // Cache to avoid division.
241
}
242
}
243
244
float LookAtModifier3D::get_duration() const {
245
return duration;
246
}
247
248
void LookAtModifier3D::set_transition_type(Tween::TransitionType p_transition_type) {
249
transition_type = p_transition_type;
250
}
251
252
Tween::TransitionType LookAtModifier3D::get_transition_type() const {
253
return transition_type;
254
}
255
256
void LookAtModifier3D::set_ease_type(Tween::EaseType p_ease_type) {
257
ease_type = p_ease_type;
258
}
259
260
Tween::EaseType LookAtModifier3D::get_ease_type() const {
261
return ease_type;
262
}
263
264
// For angle limitation.
265
266
void LookAtModifier3D::set_use_angle_limitation(bool p_enabled) {
267
use_angle_limitation = p_enabled;
268
notify_property_list_changed();
269
}
270
271
bool LookAtModifier3D::is_using_angle_limitation() const {
272
return use_angle_limitation;
273
}
274
275
void LookAtModifier3D::set_symmetry_limitation(bool p_enabled) {
276
symmetry_limitation = p_enabled;
277
notify_property_list_changed();
278
}
279
280
bool LookAtModifier3D::is_limitation_symmetry() const {
281
return symmetry_limitation;
282
}
283
284
void LookAtModifier3D::set_primary_limit_angle(float p_angle) {
285
primary_limit_angle = p_angle;
286
}
287
288
float LookAtModifier3D::get_primary_limit_angle() const {
289
return primary_limit_angle;
290
}
291
292
void LookAtModifier3D::set_primary_damp_threshold(float p_power) {
293
primary_damp_threshold = p_power;
294
}
295
296
float LookAtModifier3D::get_primary_damp_threshold() const {
297
return primary_damp_threshold;
298
}
299
300
void LookAtModifier3D::set_primary_positive_limit_angle(float p_angle) {
301
primary_positive_limit_angle = p_angle;
302
}
303
304
float LookAtModifier3D::get_primary_positive_limit_angle() const {
305
return primary_positive_limit_angle;
306
}
307
308
void LookAtModifier3D::set_primary_positive_damp_threshold(float p_power) {
309
primary_positive_damp_threshold = p_power;
310
}
311
312
float LookAtModifier3D::get_primary_positive_damp_threshold() const {
313
return primary_positive_damp_threshold;
314
}
315
316
void LookAtModifier3D::set_primary_negative_limit_angle(float p_angle) {
317
primary_negative_limit_angle = p_angle;
318
}
319
320
float LookAtModifier3D::get_primary_negative_limit_angle() const {
321
return primary_negative_limit_angle;
322
}
323
324
void LookAtModifier3D::set_primary_negative_damp_threshold(float p_power) {
325
primary_negative_damp_threshold = p_power;
326
}
327
328
float LookAtModifier3D::get_primary_negative_damp_threshold() const {
329
return primary_negative_damp_threshold;
330
}
331
332
void LookAtModifier3D::set_secondary_limit_angle(float p_angle) {
333
secondary_limit_angle = p_angle;
334
}
335
336
float LookAtModifier3D::get_secondary_limit_angle() const {
337
return secondary_limit_angle;
338
}
339
340
void LookAtModifier3D::set_secondary_damp_threshold(float p_power) {
341
secondary_damp_threshold = p_power;
342
}
343
344
float LookAtModifier3D::get_secondary_damp_threshold() const {
345
return secondary_damp_threshold;
346
}
347
348
void LookAtModifier3D::set_secondary_positive_limit_angle(float p_angle) {
349
secondary_positive_limit_angle = p_angle;
350
}
351
352
float LookAtModifier3D::get_secondary_positive_limit_angle() const {
353
return secondary_positive_limit_angle;
354
}
355
356
void LookAtModifier3D::set_secondary_positive_damp_threshold(float p_power) {
357
secondary_positive_damp_threshold = p_power;
358
}
359
360
float LookAtModifier3D::get_secondary_positive_damp_threshold() const {
361
return secondary_positive_damp_threshold;
362
}
363
364
void LookAtModifier3D::set_secondary_negative_limit_angle(float p_angle) {
365
secondary_negative_limit_angle = p_angle;
366
}
367
368
float LookAtModifier3D::get_secondary_negative_limit_angle() const {
369
return secondary_negative_limit_angle;
370
}
371
372
void LookAtModifier3D::set_secondary_negative_damp_threshold(float p_power) {
373
secondary_negative_damp_threshold = p_power;
374
}
375
376
float LookAtModifier3D::get_secondary_negative_damp_threshold() const {
377
return secondary_negative_damp_threshold;
378
}
379
380
bool LookAtModifier3D::is_target_within_limitation() const {
381
return is_within_limitations;
382
}
383
384
float LookAtModifier3D::get_interpolation_remaining() const {
385
return remaining * duration;
386
}
387
388
bool LookAtModifier3D::is_interpolating() const {
389
return Math::is_zero_approx(remaining);
390
}
391
392
// General API.
393
394
void LookAtModifier3D::_bind_methods() {
395
ClassDB::bind_method(D_METHOD("set_target_node", "target_node"), &LookAtModifier3D::set_target_node);
396
ClassDB::bind_method(D_METHOD("get_target_node"), &LookAtModifier3D::get_target_node);
397
398
ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &LookAtModifier3D::set_bone_name);
399
ClassDB::bind_method(D_METHOD("get_bone_name"), &LookAtModifier3D::get_bone_name);
400
ClassDB::bind_method(D_METHOD("set_bone", "bone"), &LookAtModifier3D::set_bone);
401
ClassDB::bind_method(D_METHOD("get_bone"), &LookAtModifier3D::get_bone);
402
ClassDB::bind_method(D_METHOD("set_forward_axis", "forward_axis"), &LookAtModifier3D::set_forward_axis);
403
ClassDB::bind_method(D_METHOD("get_forward_axis"), &LookAtModifier3D::get_forward_axis);
404
ClassDB::bind_method(D_METHOD("set_primary_rotation_axis", "axis"), &LookAtModifier3D::set_primary_rotation_axis);
405
ClassDB::bind_method(D_METHOD("get_primary_rotation_axis"), &LookAtModifier3D::get_primary_rotation_axis);
406
ClassDB::bind_method(D_METHOD("set_use_secondary_rotation", "enabled"), &LookAtModifier3D::set_use_secondary_rotation);
407
ClassDB::bind_method(D_METHOD("is_using_secondary_rotation"), &LookAtModifier3D::is_using_secondary_rotation);
408
ClassDB::bind_method(D_METHOD("set_relative", "enabled"), &LookAtModifier3D::set_relative);
409
ClassDB::bind_method(D_METHOD("is_relative"), &LookAtModifier3D::is_relative);
410
411
ClassDB::bind_method(D_METHOD("set_origin_safe_margin", "margin"), &LookAtModifier3D::set_origin_safe_margin);
412
ClassDB::bind_method(D_METHOD("get_origin_safe_margin"), &LookAtModifier3D::get_origin_safe_margin);
413
ClassDB::bind_method(D_METHOD("set_origin_from", "origin_from"), &LookAtModifier3D::set_origin_from);
414
ClassDB::bind_method(D_METHOD("get_origin_from"), &LookAtModifier3D::get_origin_from);
415
ClassDB::bind_method(D_METHOD("set_origin_bone_name", "bone_name"), &LookAtModifier3D::set_origin_bone_name);
416
ClassDB::bind_method(D_METHOD("get_origin_bone_name"), &LookAtModifier3D::get_origin_bone_name);
417
ClassDB::bind_method(D_METHOD("set_origin_bone", "bone"), &LookAtModifier3D::set_origin_bone);
418
ClassDB::bind_method(D_METHOD("get_origin_bone"), &LookAtModifier3D::get_origin_bone);
419
ClassDB::bind_method(D_METHOD("set_origin_external_node", "external_node"), &LookAtModifier3D::set_origin_external_node);
420
ClassDB::bind_method(D_METHOD("get_origin_external_node"), &LookAtModifier3D::get_origin_external_node);
421
422
ClassDB::bind_method(D_METHOD("set_origin_offset", "offset"), &LookAtModifier3D::set_origin_offset);
423
ClassDB::bind_method(D_METHOD("get_origin_offset"), &LookAtModifier3D::get_origin_offset);
424
425
ClassDB::bind_method(D_METHOD("set_duration", "duration"), &LookAtModifier3D::set_duration);
426
ClassDB::bind_method(D_METHOD("get_duration"), &LookAtModifier3D::get_duration);
427
ClassDB::bind_method(D_METHOD("set_transition_type", "transition_type"), &LookAtModifier3D::set_transition_type);
428
ClassDB::bind_method(D_METHOD("get_transition_type"), &LookAtModifier3D::get_transition_type);
429
ClassDB::bind_method(D_METHOD("set_ease_type", "ease_type"), &LookAtModifier3D::set_ease_type);
430
ClassDB::bind_method(D_METHOD("get_ease_type"), &LookAtModifier3D::get_ease_type);
431
432
ClassDB::bind_method(D_METHOD("set_use_angle_limitation", "enabled"), &LookAtModifier3D::set_use_angle_limitation);
433
ClassDB::bind_method(D_METHOD("is_using_angle_limitation"), &LookAtModifier3D::is_using_angle_limitation);
434
ClassDB::bind_method(D_METHOD("set_symmetry_limitation", "enabled"), &LookAtModifier3D::set_symmetry_limitation);
435
ClassDB::bind_method(D_METHOD("is_limitation_symmetry"), &LookAtModifier3D::is_limitation_symmetry);
436
437
ClassDB::bind_method(D_METHOD("set_primary_limit_angle", "angle"), &LookAtModifier3D::set_primary_limit_angle);
438
ClassDB::bind_method(D_METHOD("get_primary_limit_angle"), &LookAtModifier3D::get_primary_limit_angle);
439
ClassDB::bind_method(D_METHOD("set_primary_damp_threshold", "power"), &LookAtModifier3D::set_primary_damp_threshold);
440
ClassDB::bind_method(D_METHOD("get_primary_damp_threshold"), &LookAtModifier3D::get_primary_damp_threshold);
441
442
ClassDB::bind_method(D_METHOD("set_primary_positive_limit_angle", "angle"), &LookAtModifier3D::set_primary_positive_limit_angle);
443
ClassDB::bind_method(D_METHOD("get_primary_positive_limit_angle"), &LookAtModifier3D::get_primary_positive_limit_angle);
444
ClassDB::bind_method(D_METHOD("set_primary_positive_damp_threshold", "power"), &LookAtModifier3D::set_primary_positive_damp_threshold);
445
ClassDB::bind_method(D_METHOD("get_primary_positive_damp_threshold"), &LookAtModifier3D::get_primary_positive_damp_threshold);
446
ClassDB::bind_method(D_METHOD("set_primary_negative_limit_angle", "angle"), &LookAtModifier3D::set_primary_negative_limit_angle);
447
ClassDB::bind_method(D_METHOD("get_primary_negative_limit_angle"), &LookAtModifier3D::get_primary_negative_limit_angle);
448
ClassDB::bind_method(D_METHOD("set_primary_negative_damp_threshold", "power"), &LookAtModifier3D::set_primary_negative_damp_threshold);
449
ClassDB::bind_method(D_METHOD("get_primary_negative_damp_threshold"), &LookAtModifier3D::get_primary_negative_damp_threshold);
450
451
ClassDB::bind_method(D_METHOD("set_secondary_limit_angle", "angle"), &LookAtModifier3D::set_secondary_limit_angle);
452
ClassDB::bind_method(D_METHOD("get_secondary_limit_angle"), &LookAtModifier3D::get_secondary_limit_angle);
453
ClassDB::bind_method(D_METHOD("set_secondary_damp_threshold", "power"), &LookAtModifier3D::set_secondary_damp_threshold);
454
ClassDB::bind_method(D_METHOD("get_secondary_damp_threshold"), &LookAtModifier3D::get_secondary_damp_threshold);
455
456
ClassDB::bind_method(D_METHOD("set_secondary_positive_limit_angle", "angle"), &LookAtModifier3D::set_secondary_positive_limit_angle);
457
ClassDB::bind_method(D_METHOD("get_secondary_positive_limit_angle"), &LookAtModifier3D::get_secondary_positive_limit_angle);
458
ClassDB::bind_method(D_METHOD("set_secondary_positive_damp_threshold", "power"), &LookAtModifier3D::set_secondary_positive_damp_threshold);
459
ClassDB::bind_method(D_METHOD("get_secondary_positive_damp_threshold"), &LookAtModifier3D::get_secondary_positive_damp_threshold);
460
ClassDB::bind_method(D_METHOD("set_secondary_negative_limit_angle", "angle"), &LookAtModifier3D::set_secondary_negative_limit_angle);
461
ClassDB::bind_method(D_METHOD("get_secondary_negative_limit_angle"), &LookAtModifier3D::get_secondary_negative_limit_angle);
462
ClassDB::bind_method(D_METHOD("set_secondary_negative_damp_threshold", "power"), &LookAtModifier3D::set_secondary_negative_damp_threshold);
463
ClassDB::bind_method(D_METHOD("get_secondary_negative_damp_threshold"), &LookAtModifier3D::get_secondary_negative_damp_threshold);
464
465
ClassDB::bind_method(D_METHOD("get_interpolation_remaining"), &LookAtModifier3D::get_interpolation_remaining);
466
ClassDB::bind_method(D_METHOD("is_interpolating"), &LookAtModifier3D::is_interpolating);
467
ClassDB::bind_method(D_METHOD("is_target_within_limitation"), &LookAtModifier3D::is_target_within_limitation);
468
469
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_target_node", "get_target_node");
470
471
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_bone_name", "get_bone_name");
472
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_bone", "get_bone");
473
ADD_PROPERTY(PropertyInfo(Variant::INT, "forward_axis", PROPERTY_HINT_ENUM, SkeletonModifier3D::get_hint_bone_axis()), "set_forward_axis", "get_forward_axis");
474
ADD_PROPERTY(PropertyInfo(Variant::INT, "primary_rotation_axis", PROPERTY_HINT_ENUM, "X,Y,Z"), "set_primary_rotation_axis", "get_primary_rotation_axis");
475
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_secondary_rotation"), "set_use_secondary_rotation", "is_using_secondary_rotation");
476
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "relative"), "set_relative", "is_relative");
477
478
ADD_GROUP("Origin Settings", "origin_");
479
ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_from", PROPERTY_HINT_ENUM, "Self,SpecificBone,ExternalNode"), "set_origin_from", "get_origin_from");
480
ADD_PROPERTY(PropertyInfo(Variant::STRING, "origin_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_origin_bone_name", "get_origin_bone_name");
481
ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_origin_bone", "get_origin_bone");
482
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "origin_external_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_origin_external_node", "get_origin_external_node");
483
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset");
484
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "origin_safe_margin", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_origin_safe_margin", "get_origin_safe_margin");
485
486
ADD_GROUP("Time Based Interpolation", "");
487
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "duration", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater,suffix:s"), "set_duration", "get_duration");
488
ADD_PROPERTY(PropertyInfo(Variant::INT, "transition_type", PROPERTY_HINT_ENUM, "Linear,Sine,Quint,Quart,Quad,Expo,Elastic,Cubic,Circ,Bounce,Back,Spring"), "set_transition_type", "get_transition_type");
489
ADD_PROPERTY(PropertyInfo(Variant::INT, "ease_type", PROPERTY_HINT_ENUM, "In,Out,InOut,OutIn"), "set_ease_type", "get_ease_type");
490
491
ADD_GROUP("Angle Limitation", "");
492
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_angle_limitation"), "set_use_angle_limitation", "is_using_angle_limitation");
493
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symmetry_limitation"), "set_symmetry_limitation", "is_limitation_symmetry");
494
495
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_limit_angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_primary_limit_angle", "get_primary_limit_angle");
496
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_damp_threshold", "get_primary_damp_threshold");
497
498
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_positive_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_primary_positive_limit_angle", "get_primary_positive_limit_angle");
499
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_positive_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_positive_damp_threshold", "get_primary_positive_damp_threshold");
500
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_negative_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_primary_negative_limit_angle", "get_primary_negative_limit_angle");
501
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "primary_negative_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_primary_negative_damp_threshold", "get_primary_negative_damp_threshold");
502
503
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_limit_angle", PROPERTY_HINT_RANGE, "0,360,0.01,radians_as_degrees"), "set_secondary_limit_angle", "get_secondary_limit_angle");
504
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_damp_threshold", "get_secondary_damp_threshold");
505
506
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_positive_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_secondary_positive_limit_angle", "get_secondary_positive_limit_angle");
507
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_positive_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_positive_damp_threshold", "get_secondary_positive_damp_threshold");
508
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_negative_limit_angle", PROPERTY_HINT_RANGE, "0,180,0.01,radians_as_degrees"), "set_secondary_negative_limit_angle", "get_secondary_negative_limit_angle");
509
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "secondary_negative_damp_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_secondary_negative_damp_threshold", "get_secondary_negative_damp_threshold");
510
511
BIND_ENUM_CONSTANT(ORIGIN_FROM_SELF);
512
BIND_ENUM_CONSTANT(ORIGIN_FROM_SPECIFIC_BONE);
513
BIND_ENUM_CONSTANT(ORIGIN_FROM_EXTERNAL_NODE);
514
}
515
516
void LookAtModifier3D::_process_modification(double p_delta) {
517
Skeleton3D *skeleton = get_skeleton();
518
if (!skeleton || bone < 0 || bone >= skeleton->get_bone_count()) {
519
return;
520
}
521
522
// Calculate bone rest space in the world.
523
Transform3D bone_rest = relative ? skeleton->get_bone_pose(bone) : skeleton->get_bone_rest(bone);
524
Transform3D bone_rest_space;
525
int parent_bone = skeleton->get_bone_parent(bone);
526
if (parent_bone < 0) {
527
bone_rest_space = skeleton->get_global_transform_interpolated();
528
bone_rest_space.translate_local(bone_rest.origin);
529
} else {
530
bone_rest_space = skeleton->get_global_transform_interpolated() * skeleton->get_bone_global_pose(parent_bone);
531
bone_rest_space.translate_local(bone_rest.origin);
532
}
533
534
// Calculate forward_vector and destination.
535
is_within_limitations = true;
536
Vector3 prev_forward_vector = forward_vector;
537
Quaternion destination;
538
Node3D *target = Object::cast_to<Node3D>(get_node_or_null(target_node));
539
if (!target) {
540
destination = skeleton->get_bone_pose_rotation(bone);
541
} else {
542
Transform3D origin_tr;
543
if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone >= 0 && origin_bone < skeleton->get_bone_count()) {
544
origin_tr = skeleton->get_global_transform_interpolated() * skeleton->get_bone_global_pose(origin_bone);
545
} else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) {
546
Node3D *origin_src = Object::cast_to<Node3D>(get_node_or_null(origin_external_node));
547
if (origin_src) {
548
origin_tr = origin_src->get_global_transform_interpolated();
549
} else {
550
origin_tr = bone_rest_space;
551
}
552
} else {
553
origin_tr = bone_rest_space;
554
}
555
forward_vector = bone_rest_space.orthonormalized().basis.xform_inv(target->get_global_transform_interpolated().origin - origin_tr.translated_local(origin_offset).origin);
556
forward_vector_nrm = forward_vector.normalized();
557
if (forward_vector_nrm.abs().is_equal_approx(get_vector_from_axis(primary_rotation_axis))) {
558
destination = skeleton->get_bone_pose_rotation(bone);
559
forward_vector = Vector3(0, 0, 0); // The zero-vector to be used for checking in the line immediately below to avoid animation glitch.
560
} else {
561
destination = look_at_with_axes(bone_rest).basis.get_rotation_quaternion();
562
forward_vector = bone_rest.basis.xform_inv(forward_vector_nrm); // Forward vector in the "current" bone rest.
563
}
564
}
565
566
// Detect flipping.
567
bool is_not_max_influence = influence < 1.0;
568
bool is_flippable = use_angle_limitation || is_not_max_influence;
569
Vector3::Axis current_forward_axis = get_axis_from_bone_axis(forward_axis);
570
if (is_intersecting_axis(prev_forward_vector, forward_vector, current_forward_axis, secondary_rotation_axis) ||
571
is_intersecting_axis(prev_forward_vector, forward_vector, primary_rotation_axis, primary_rotation_axis, true) ||
572
is_intersecting_axis(prev_forward_vector, forward_vector, secondary_rotation_axis, current_forward_axis) ||
573
(prev_forward_vector != Vector3(0, 0, 0) && forward_vector == Vector3(0, 0, 0)) ||
574
(prev_forward_vector == Vector3(0, 0, 0) && forward_vector != Vector3(0, 0, 0))) {
575
init_transition();
576
} else if (is_flippable && std::signbit(prev_forward_vector[secondary_rotation_axis]) != std::signbit(forward_vector[secondary_rotation_axis])) {
577
// Flipping by angle_limitation can be detected by sign of secondary rotation axes during forward_vector is rotated more than 90 degree from forward_axis (means dot production is negative).
578
Vector3 rest_forward_vector = get_vector_from_bone_axis(forward_axis);
579
if (symmetry_limitation) {
580
if ((is_not_max_influence || !Math::is_equal_approx(primary_limit_angle, (float)Math::TAU)) &&
581
prev_forward_vector.dot(rest_forward_vector) < 0 &&
582
forward_vector.dot(rest_forward_vector) < 0) {
583
init_transition();
584
}
585
} else {
586
if ((is_not_max_influence || !Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math::TAU)) &&
587
prev_forward_vector.dot(rest_forward_vector) < 0 &&
588
forward_vector.dot(rest_forward_vector) < 0) {
589
init_transition();
590
}
591
}
592
}
593
594
// Do time-based interpolation.
595
if (remaining > 0) {
596
remaining = MAX(0, remaining - time_step * p_delta);
597
if (is_flippable) {
598
// Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body.
599
Quaternion rest = bone_rest.basis.get_rotation_quaternion();
600
float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0);
601
destination = Animation::interpolate_via_rest(Animation::interpolate_via_rest(rest, from_q, 1 - weight, rest), destination, weight, rest);
602
} else {
603
destination = from_q.slerp(destination, Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0));
604
}
605
}
606
607
skeleton->set_bone_pose_rotation(bone, destination);
608
prev_q = destination;
609
}
610
611
bool LookAtModifier3D::is_intersecting_axis(const Vector3 &p_prev, const Vector3 &p_current, Vector3::Axis p_flipping_axis, Vector3::Axis p_check_axis, bool p_check_plane) const {
612
// Prevent that the angular velocity does not become too large.
613
// Check that is p_flipping_axis flipped nearby p_check_axis (close than origin_safe_margin) or not. If p_check_plane is true, check two axes of crossed plane.
614
if (p_check_plane) {
615
if (get_projection_vector(p_prev, p_check_axis).length() > origin_safe_margin && get_projection_vector(p_current, p_check_axis).length() > origin_safe_margin) {
616
return false;
617
}
618
} else if (Math::abs(p_prev[p_check_axis]) > origin_safe_margin && Math::abs(p_current[p_check_axis]) > origin_safe_margin) {
619
return false;
620
}
621
622
return std::signbit(p_prev[p_flipping_axis]) != std::signbit(p_current[p_flipping_axis]);
623
}
624
625
Vector3 LookAtModifier3D::get_basis_vector_from_bone_axis(const Basis &p_basis, BoneAxis p_axis) {
626
Vector3 ret;
627
switch (p_axis) {
628
case BONE_AXIS_PLUS_X: {
629
ret = p_basis.get_column(0);
630
} break;
631
case BONE_AXIS_MINUS_X: {
632
ret = -p_basis.get_column(0);
633
} break;
634
case BONE_AXIS_PLUS_Y: {
635
ret = p_basis.get_column(1);
636
} break;
637
case BONE_AXIS_MINUS_Y: {
638
ret = -p_basis.get_column(1);
639
} break;
640
case BONE_AXIS_PLUS_Z: {
641
ret = p_basis.get_column(2);
642
} break;
643
case BONE_AXIS_MINUS_Z: {
644
ret = -p_basis.get_column(2);
645
} break;
646
}
647
return ret;
648
}
649
650
Vector3::Axis LookAtModifier3D::get_secondary_rotation_axis(BoneAxis p_forward_axis, Vector3::Axis p_primary_rotation_axis) {
651
Vector3 secondary_plane = get_vector_from_bone_axis(p_forward_axis) + get_vector_from_axis(p_primary_rotation_axis);
652
return Math::is_zero_approx(secondary_plane.x) ? Vector3::AXIS_X : (Math::is_zero_approx(secondary_plane.y) ? Vector3::AXIS_Y : Vector3::AXIS_Z);
653
}
654
655
Vector2 LookAtModifier3D::get_projection_vector(const Vector3 &p_vector, Vector3::Axis p_axis) {
656
// NOTE: axis is swapped between 2D and 3D.
657
Vector2 ret;
658
switch (p_axis) {
659
case Vector3::AXIS_X: {
660
ret = Vector2(p_vector.z, p_vector.y);
661
} break;
662
case Vector3::AXIS_Y: {
663
ret = Vector2(p_vector.x, p_vector.z);
664
} break;
665
case Vector3::AXIS_Z: {
666
ret = Vector2(p_vector.y, p_vector.x);
667
} break;
668
}
669
return ret;
670
}
671
672
float LookAtModifier3D::remap_damped(float p_from, float p_to, float p_damp_threshold, float p_value) const {
673
float sign = std::signbit(p_value) ? -1.0f : 1.0f;
674
float abs_value = Math::abs(p_value);
675
676
if (Math::is_equal_approx(p_damp_threshold, 1.0f) || Math::is_zero_approx(p_to)) {
677
return sign * CLAMP(abs_value, p_from, p_to); // Avoid division by zero.
678
}
679
680
float value = Math::inverse_lerp(p_from, p_to, abs_value);
681
682
if (value <= p_damp_threshold) {
683
return sign * CLAMP(abs_value, p_from, p_to);
684
}
685
686
double limit = Math::PI;
687
double inv_to = 1.0 / p_to;
688
double end_x = limit * inv_to;
689
double position = abs_value * inv_to;
690
Vector2 start = Vector2(p_damp_threshold, p_damp_threshold);
691
Vector2 mid = Vector2(1.0, 1.0);
692
Vector2 end = Vector2(end_x, 1.0);
693
value = get_bspline_y(start, mid, end, position);
694
695
return sign * Math::lerp(p_from, p_to, value);
696
}
697
698
double LookAtModifier3D::get_bspline_y(const Vector2 &p_from, const Vector2 &p_control, const Vector2 &p_to, double p_x) const {
699
double a = p_from.x - 2.0 * p_control.x + p_to.x;
700
double b = -2.0 * p_from.x + 2.0 * p_control.x;
701
double c = p_from.x - p_x;
702
double t = 0.0;
703
if (Math::is_zero_approx(a)) {
704
t = -c / b; // Almost linear.
705
} else {
706
double discriminant = b * b - 4.0 * a * c;
707
double sqrt_discriminant = Math::sqrt(discriminant);
708
double e = 1.0 / (2.0 * a);
709
double t1 = (-b + sqrt_discriminant) * e;
710
t = (0.0 <= t1 && t1 <= 1.0) ? t1 : (-b - sqrt_discriminant) * e;
711
}
712
double u = 1.0 - t;
713
double y = u * u * p_from.y + 2.0 * u * t * p_control.y + t * t * p_to.y;
714
return y;
715
}
716
717
Transform3D LookAtModifier3D::look_at_with_axes(const Transform3D &p_rest) {
718
// Primary rotation by projection to 2D plane by xform_inv and picking elements.
719
Vector3 current_vector = get_basis_vector_from_bone_axis(p_rest.basis, forward_axis).normalized();
720
Vector2 src_vec2 = get_projection_vector(p_rest.basis.xform_inv(forward_vector_nrm), primary_rotation_axis).normalized();
721
Vector2 dst_vec2 = get_projection_vector(p_rest.basis.xform_inv(current_vector), primary_rotation_axis).normalized();
722
real_t calculated_angle = src_vec2.angle_to(dst_vec2);
723
Transform3D primary_result = p_rest.rotated_local(get_vector_from_axis(primary_rotation_axis), calculated_angle);
724
Transform3D current_result = primary_result; // primary_result will be used by calculation of secondary rotation, current_result is rotated by that.
725
float limit_angle = 0.0;
726
float damp_threshold = 0.0;
727
728
if (use_angle_limitation) {
729
if (symmetry_limitation) {
730
limit_angle = primary_limit_angle * 0.5f;
731
damp_threshold = primary_damp_threshold;
732
} else {
733
if (std::signbit(calculated_angle)) {
734
limit_angle = primary_negative_limit_angle;
735
damp_threshold = primary_negative_damp_threshold;
736
} else {
737
limit_angle = primary_positive_limit_angle;
738
damp_threshold = primary_positive_damp_threshold;
739
}
740
}
741
if (Math::abs(calculated_angle) > limit_angle) {
742
is_within_limitations = false;
743
}
744
calculated_angle = remap_damped(0, limit_angle, damp_threshold, calculated_angle);
745
current_result = p_rest.rotated_local(get_vector_from_axis(primary_rotation_axis), calculated_angle);
746
}
747
748
// Needs for detecting flipping even if use_secondary_rotation is false.
749
secondary_rotation_axis = get_secondary_rotation_axis(forward_axis, primary_rotation_axis);
750
751
if (!use_secondary_rotation) {
752
return current_result;
753
}
754
755
// Secondary rotation by projection to 2D plane by xform_inv and picking elements.
756
current_vector = get_basis_vector_from_bone_axis(primary_result.basis, forward_axis).normalized();
757
src_vec2 = get_projection_vector(primary_result.basis.xform_inv(forward_vector_nrm), secondary_rotation_axis).normalized();
758
dst_vec2 = get_projection_vector(primary_result.basis.xform_inv(current_vector), secondary_rotation_axis).normalized();
759
calculated_angle = src_vec2.angle_to(dst_vec2);
760
761
if (use_angle_limitation) {
762
if (symmetry_limitation) {
763
limit_angle = secondary_limit_angle * 0.5f;
764
damp_threshold = secondary_damp_threshold;
765
} else {
766
if (std::signbit(calculated_angle)) {
767
limit_angle = secondary_negative_limit_angle;
768
damp_threshold = secondary_negative_damp_threshold;
769
} else {
770
limit_angle = secondary_positive_limit_angle;
771
damp_threshold = secondary_positive_damp_threshold;
772
}
773
}
774
if (Math::abs(calculated_angle) > limit_angle) {
775
is_within_limitations = false;
776
}
777
calculated_angle = remap_damped(0, limit_angle, damp_threshold, calculated_angle);
778
}
779
780
current_result = current_result.rotated_local(get_vector_from_axis(secondary_rotation_axis), calculated_angle);
781
782
return current_result;
783
}
784
785
void LookAtModifier3D::init_transition() {
786
if (Math::is_zero_approx(duration)) {
787
return;
788
}
789
from_q = prev_q;
790
remaining = 1.0;
791
}
792
793