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