summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/animation.cpp244
-rw-r--r--scene/resources/animation.h114
-rw-r--r--scene/resources/audio_stream_sample.cpp6
-rw-r--r--scene/resources/canvas_item_material.cpp306
-rw-r--r--scene/resources/canvas_item_material.h151
-rw-r--r--scene/resources/capsule_shape_2d.cpp24
-rw-r--r--scene/resources/capsule_shape_2d.h2
-rw-r--r--scene/resources/capsule_shape_3d.cpp12
-rw-r--r--scene/resources/capsule_shape_3d.h2
-rw-r--r--scene/resources/curve.cpp105
-rw-r--r--scene/resources/curve.h6
-rw-r--r--scene/resources/default_theme/default_theme.cpp58
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin0 -> 2170 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.pngbin0 -> 242 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h16
-rw-r--r--scene/resources/height_map_shape_3d.cpp18
-rw-r--r--scene/resources/height_map_shape_3d.h6
-rw-r--r--scene/resources/material.cpp12
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--scene/resources/mesh_data_tool.cpp4
-rw-r--r--scene/resources/navigation_mesh.cpp80
-rw-r--r--scene/resources/navigation_mesh.h20
-rw-r--r--scene/resources/packed_scene.cpp8
-rw-r--r--scene/resources/particles_material.cpp438
-rw-r--r--scene/resources/particles_material.h103
-rw-r--r--scene/resources/polygon_path_finder.cpp8
-rw-r--r--scene/resources/primitive_meshes.cpp28
-rw-r--r--scene/resources/primitive_meshes.h6
-rw-r--r--scene/resources/ray_shape_2d.cpp119
-rw-r--r--scene/resources/resource_format_text.cpp12
-rw-r--r--scene/resources/shader.cpp3
-rw-r--r--scene/resources/skeleton_modification_2d.cpp20
-rw-r--r--scene/resources/skeleton_modification_2d.h4
-rw-r--r--scene/resources/skeleton_modification_2d_ccdik.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp29
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp6
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp2
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d.cpp162
-rw-r--r--scene/resources/skeleton_modification_3d.h (renamed from scene/resources/ray_shape_3d.h)57
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.cpp474
-rw-r--r--scene/resources/skeleton_modification_3d_ccdik.h114
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp628
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h122
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp573
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.h138
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp265
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.h (renamed from scene/resources/ray_shape_3d.cpp)122
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.cpp104
-rw-r--r--scene/resources/skeleton_modification_3d_stackholder.h (renamed from scene/resources/ray_shape_2d.h)38
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp599
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.h118
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp222
-rw-r--r--scene/resources/skeleton_modification_stack_3d.h91
-rw-r--r--scene/resources/sky_material.cpp15
-rw-r--r--scene/resources/sprite_frames.cpp4
-rw-r--r--scene/resources/sprite_frames.h6
-rw-r--r--scene/resources/style_box.cpp143
-rw-r--r--scene/resources/style_box.h25
-rw-r--r--scene/resources/syntax_highlighter.cpp24
-rw-r--r--scene/resources/syntax_highlighter.h9
-rw-r--r--scene/resources/text_line.cpp65
-rw-r--r--scene/resources/text_line.h20
-rw-r--r--scene/resources/text_paragraph.cpp331
-rw-r--r--scene/resources/text_paragraph.h30
-rw-r--r--scene/resources/texture.cpp9
-rw-r--r--scene/resources/texture.h1
-rw-r--r--scene/resources/theme.cpp75
-rw-r--r--scene/resources/theme.h3
-rw-r--r--scene/resources/visual_shader.cpp221
-rw-r--r--scene/resources/visual_shader.h15
-rw-r--r--scene/resources/visual_shader_nodes.cpp518
-rw-r--r--scene/resources/visual_shader_nodes.h117
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp25
-rw-r--r--scene/resources/visual_shader_particle_nodes.h4
-rw-r--r--scene/resources/world_margin_shape_2d.cpp (renamed from scene/resources/line_shape_2d.cpp)36
-rw-r--r--scene/resources/world_margin_shape_2d.h (renamed from scene/resources/line_shape_2d.h)16
77 files changed, 6130 insertions, 1395 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 640ec50eb9..80e338c8e8 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -77,17 +77,19 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "keys" || what == "key_values") {
if (track_get_type(track) == TYPE_TRANSFORM3D) {
TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
- Vector<float> values = p_value;
+ Vector<real_t> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 11
+ ERR_FAIL_COND_V(vcount % 12, false); // should be multiple of 12
- const float *r = values.ptr();
+ const real_t *r = values.ptr();
- tt->transforms.resize(vcount / 12);
+ int transform3d_size = (int)sizeof(Transform3D);
- for (int i = 0; i < (vcount / 12); i++) {
+ tt->transforms.resize(vcount / transform3d_size);
+
+ for (int i = 0; i < (vcount / transform3d_size); i++) {
TKey<TransformKey> &tk = tt->transforms.write[i];
- const float *ofs = &r[i * 12];
+ const real_t *ofs = &r[i * 12];
tk.time = ofs[0];
tk.transition = ofs[1];
@@ -125,7 +127,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
vt->update_mode = UpdateMode(um);
}
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array values = d["values"];
ERR_FAIL_COND_V(times.size() != values.size(), false);
@@ -133,7 +135,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
vt->values.resize(valcount);
@@ -143,10 +145,10 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
if (d.has("transitions")) {
- Vector<float> transitions = d["transitions"];
+ Vector<real_t> transitions = d["transitions"];
ERR_FAIL_COND_V(transitions.size() != valcount, false);
- const float *rtr = transitions.ptr();
+ const real_t *rtr = transitions.ptr();
for (int i = 0; i < valcount; i++) {
vt->values.write[i].transition = rtr[i];
@@ -165,7 +167,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("values"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array values = d["values"];
ERR_FAIL_COND_V(times.size() != values.size(), false);
@@ -173,17 +175,17 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
for (int i = 0; i < valcount; i++) {
track_insert_key(track, rt[i], values[i]);
}
if (d.has("transitions")) {
- Vector<float> transitions = d["transitions"];
+ Vector<real_t> transitions = d["transitions"];
ERR_FAIL_COND_V(transitions.size() != valcount, false);
- const float *rtr = transitions.ptr();
+ const real_t *rtr = transitions.ptr();
for (int i = 0; i < valcount; i++) {
track_set_key_transition(track, i, rtr[i]);
@@ -196,16 +198,16 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("points"), false);
- Vector<float> times = d["times"];
- PackedFloat32Array values = d["points"];
+ Vector<real_t> times = d["times"];
+ Vector<real_t> values = d["points"];
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
- const float *rv = values.ptr();
+ const real_t *rt = times.ptr();
+ const real_t *rv = values.ptr();
bt->values.resize(valcount);
@@ -227,7 +229,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("clips"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Array clips = d["clips"];
ERR_FAIL_COND_V(clips.size() != times.size(), false);
@@ -235,7 +237,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
ad->values.clear();
@@ -268,7 +270,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("times"), false);
ERR_FAIL_COND_V(!d.has("clips"), false);
- Vector<float> times = d["times"];
+ Vector<real_t> times = d["times"];
Vector<String> clips = d["clips"];
ERR_FAIL_COND_V(clips.size() != times.size(), false);
@@ -276,7 +278,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (times.size()) {
int valcount = times.size();
- const float *rt = times.ptr();
+ const real_t *rt = times.ptr();
const String *rc = clips.ptr();
an->values.resize(valcount);
@@ -352,9 +354,9 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = track_is_enabled(track);
} else if (what == "keys") {
if (track_get_type(track) == TYPE_TRANSFORM3D) {
- Vector<float> keys;
+ Vector<real_t> keys;
int kk = track_get_key_count(track);
- keys.resize(kk * 12);
+ keys.resize(kk * sizeof(Transform3D));
real_t *w = keys.ptrw();
@@ -389,8 +391,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_transitions;
+ Vector<real_t> key_times;
+ Vector<real_t> key_transitions;
Array key_values;
int kk = vt->values.size();
@@ -399,8 +401,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_transitions.resize(kk);
key_values.resize(kk);
- float *wti = key_times.ptrw();
- float *wtr = key_transitions.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wtr = key_transitions.ptrw();
int idx = 0;
@@ -427,8 +429,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (track_get_type(track) == TYPE_METHOD) {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_transitions;
+ Vector<real_t> key_times;
+ Vector<real_t> key_transitions;
Array key_values;
int kk = track_get_key_count(track);
@@ -437,8 +439,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_transitions.resize(kk);
key_values.resize(kk);
- float *wti = key_times.ptrw();
- float *wtr = key_transitions.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wtr = key_transitions.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
@@ -463,16 +465,16 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
- Vector<float> key_points;
+ Vector<real_t> key_times;
+ Vector<real_t> key_points;
int kk = bt->values.size();
key_times.resize(kk);
key_points.resize(kk * 5);
- float *wti = key_times.ptrw();
- float *wpo = key_points.ptrw();
+ real_t *wti = key_times.ptrw();
+ real_t *wpo = key_points.ptrw();
int idx = 0;
@@ -499,14 +501,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
+ Vector<real_t> key_times;
Array clips;
int kk = ad->values.size();
key_times.resize(kk);
- float *wti = key_times.ptrw();
+ real_t *wti = key_times.ptrw();
int idx = 0;
@@ -533,7 +535,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
Dictionary d;
- Vector<float> key_times;
+ Vector<real_t> key_times;
Vector<String> clips;
int kk = an->values.size();
@@ -541,7 +543,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
key_times.resize(kk);
clips.resize(kk);
- float *wti = key_times.ptrw();
+ real_t *wti = key_times.ptrw();
String *wcl = clips.ptrw();
const TKey<StringName> *vls = an->values.ptr();
@@ -722,7 +724,7 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
// transform
/*
template<class T>
-int Animation::_insert_pos(float p_time, T& p_keys) {
+int Animation::_insert_pos(double p_time, T& p_keys) {
// simple, linear time inset that should be fast enough in reality.
int idx=p_keys.size();
@@ -745,12 +747,12 @@ int Animation::_insert_pos(float p_time, T& p_keys) {
*/
template <class T, class V>
-int Animation::_insert(float p_time, T &p_keys, const V &p_value) {
+int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
int idx = p_keys.size();
while (true) {
// Condition for replacement.
- if (idx > 0 && Math::is_equal_approx(p_keys[idx - 1].time, p_time)) {
+ if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
float transition = p_keys[idx - 1].transition;
p_keys.write[idx - 1] = p_value;
p_keys.write[idx - 1].transition = transition;
@@ -794,7 +796,7 @@ Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc,
return OK;
}
-int Animation::transform_track_insert_key(int p_track, float p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
+int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1);
@@ -812,7 +814,7 @@ int Animation::transform_track_insert_key(int p_track, float p_time, const Vecto
return ret;
}
-void Animation::track_remove_key_at_time(int p_track, float p_time) {
+void Animation::track_remove_key_at_time(int p_track, double p_time) {
int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
track_remove_key(p_track, idx);
@@ -864,7 +866,7 @@ void Animation::track_remove_key(int p_track, int p_idx) {
emit_changed();
}
-int Animation::track_find_key(int p_track, float p_time, bool p_exact) const {
+int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
@@ -946,7 +948,7 @@ int Animation::track_find_key(int p_track, float p_time, bool p_exact) const {
return -1;
}
-void Animation::track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition) {
+void Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
@@ -1151,7 +1153,7 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
ERR_FAIL_V(Variant());
}
-float Animation::track_get_key_time(int p_track, int p_key_idx) const {
+double Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
@@ -1196,7 +1198,7 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const {
ERR_FAIL_V(-1);
}
-void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
+void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
@@ -1260,7 +1262,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) {
ERR_FAIL();
}
-float Animation::track_get_key_transition(int p_track, int p_key_idx) const {
+real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
@@ -1379,7 +1381,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
emit_changed();
}
-void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_transition) {
+void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_transition) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
@@ -1412,7 +1414,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra
}
template <class K>
-int Animation::_find(const Vector<K> &p_keys, float p_time) const {
+int Animation::_find(const Vector<K> &p_keys, double p_time) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@@ -1433,7 +1435,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
while (low <= high) {
middle = (low + high) / 2;
- if (Math::is_equal_approx(p_time, keys[middle].time)) { //match
+ if (Math::is_equal_approx(p_time, (double)keys[middle].time)) { //match
return middle;
} else if (p_time < keys[middle].time) {
high = middle - 1; //search low end of array
@@ -1449,7 +1451,7 @@ int Animation::_find(const Vector<K> &p_keys, float p_time) const {
return middle;
}
-Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, float p_c) const {
+Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const {
TransformKey ret;
ret.loc = _interpolate(p_a.loc, p_b.loc, p_c);
ret.rot = _interpolate(p_a.rot, p_b.rot, p_c);
@@ -1458,25 +1460,25 @@ Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p
return ret;
}
-Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_c) const {
+Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const {
return p_a.lerp(p_b, p_c);
}
-Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, float p_c) const {
+Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const {
return p_a.slerp(p_b, p_c);
}
-Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, float p_c) const {
+Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const {
Variant dst;
Variant::interpolate(p_a, p_b, p_c, dst);
return dst;
}
-float Animation::_interpolate(const float &p_a, const float &p_b, float p_c) const {
+real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const {
return p_a * (1.0 - p_c) + p_b * p_c;
}
-Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, float p_c) const {
+Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const {
Animation::TransformKey tk;
tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c);
@@ -1486,15 +1488,15 @@ Animation::TransformKey Animation::_cubic_interpolate(const Animation::Transform
return tk;
}
-Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, float p_c) const {
+Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const {
return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
-Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, float p_c) const {
+Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const {
return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c);
}
-Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, float p_c) const {
+Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const {
Variant::Type type_a = p_a.get_type();
Variant::Type type_b = p_b.get_type();
Variant::Type type_pa = p_pre_a.get_type();
@@ -1515,9 +1517,9 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
real_t p2 = p_b;
real_t p3 = p_post_b;
- float t = p_c;
- float t2 = t * t;
- float t3 = t2 * t;
+ real_t t = p_c;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
return 0.5f * ((p1 * 2.0f) +
(-p0 + p2) * t +
@@ -1579,12 +1581,12 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
}
}
-float Animation::_cubic_interpolate(const float &p_pre_a, const float &p_a, const float &p_b, const float &p_post_b, float p_c) const {
+real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const {
return _interpolate(p_a, p_b, p_c);
}
template <class T>
-T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
+T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@@ -1608,7 +1610,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
bool result = true;
int next = 0;
- float c = 0.0;
+ real_t c = 0.0;
// prepare for all cases of interpolation
if (loop && p_loop_wrap) {
@@ -1616,8 +1618,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
- float delta = p_keys[next].time - p_keys[idx].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1627,8 +1629,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
} else {
next = 0;
- float delta = (length - p_keys[idx].time) + p_keys[next].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1641,12 +1643,12 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
// on loop, behind first key
idx = len - 1;
next = 0;
- float endtime = (length - p_keys[idx].time);
+ real_t endtime = (length - p_keys[idx].time);
if (endtime < 0) { // may be keys past the end
endtime = 0;
}
- float delta = endtime + p_keys[next].time;
- float from = endtime + p_time;
+ real_t delta = endtime + p_keys[next].time;
+ real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1660,8 +1662,8 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
if (idx >= 0) {
if ((idx + 1) < len) {
next = idx + 1;
- float delta = p_keys[next].time - p_keys[idx].time;
- float from = p_time - p_keys[idx].time;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1690,7 +1692,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
return T();
}
- float tr = p_keys[idx].transition;
+ real_t tr = p_keys[idx].transition;
if (tr == 0 || idx == next) {
// don't interpolate if not needed
@@ -1728,7 +1730,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, float p_time, Interpola
// do a barrel roll
}
-Error Animation::transform_track_interpolate(int p_track, float p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
+Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
@@ -1758,7 +1760,7 @@ Error Animation::transform_track_interpolate(int p_track, float p_time, Vector3
return OK;
}
-Variant Animation::value_track_interpolate(int p_track, float p_time) const {
+Variant Animation::value_track_interpolate(int p_track, double p_time) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_VALUE, Variant());
@@ -1775,7 +1777,7 @@ Variant Animation::value_track_interpolate(int p_track, float p_time) const {
return Variant();
}
-void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.001; //include a little more if at the end
}
@@ -1812,15 +1814,15 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, floa
}
}
-void Animation::value_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
ValueTrack *vt = static_cast<ValueTrack *>(t);
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -1875,7 +1877,7 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
}
template <class T>
-void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.01; //include a little more if at the end
}
@@ -1908,12 +1910,12 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, float
}
}
-void Animation::track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -2021,7 +2023,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float
}
}
-void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const {
+void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
to_time = length * 1.01; //include a little more if at the end
}
@@ -2054,15 +2056,15 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, fl
}
}
-void Animation::method_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const {
+void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
MethodTrack *mt = static_cast<MethodTrack *>(t);
- float from_time = p_time - p_delta;
- float to_time = p_time;
+ double from_time = p_time - p_delta;
+ double to_time = p_time;
if (from_time > to_time) {
SWAP(from_time, to_time);
@@ -2128,7 +2130,7 @@ StringName Animation::method_track_get_name(int p_track, int p_key_idx) const {
return pm->methods[p_key_idx].method;
}
-int Animation::bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) {
+int Animation::bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BEZIER, -1);
@@ -2154,7 +2156,7 @@ int Animation::bezier_track_insert_key(int p_track, float p_time, float p_value,
return key;
}
-void Animation::bezier_track_set_key_value(int p_track, int p_index, float p_value) {
+void Animation::bezier_track_set_key_value(int p_track, int p_index, real_t p_value) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_BEZIER);
@@ -2199,7 +2201,7 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const
emit_changed();
}
-float Animation::bezier_track_get_key_value(int p_track, int p_index) const {
+real_t Animation::bezier_track_get_key_value(int p_track, int p_index) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BEZIER, 0);
@@ -2246,7 +2248,7 @@ static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, con
return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
}
-float Animation::bezier_track_interpolate(int p_track, float p_time) const {
+real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
//this uses a different interpolation scheme
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *track = tracks[p_track];
@@ -2277,14 +2279,14 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
return bt->values[bt->values.size() - 1].value.value;
}
- float t = p_time - bt->values[idx].time;
+ double t = p_time - bt->values[idx].time;
int iterations = 10;
- float duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
- float low = 0.0; // 0% of the current animation segment
- float high = 1.0; // 100% of the current animation segment
- float middle;
+ real_t duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes
+ real_t low = 0.0; // 0% of the current animation segment
+ real_t high = 1.0; // 100% of the current animation segment
+ real_t middle;
Vector2 start(0, bt->values[idx].value.value);
Vector2 start_out = start + bt->values[idx].value.out_handle;
@@ -2307,12 +2309,12 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const {
//interpolate the result:
Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
- float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
+ real_t c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.lerp(high_pos, c).y;
}
-int Animation::audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset, float p_end_offset) {
+int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset, real_t p_end_offset) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1);
@@ -2352,7 +2354,7 @@ void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_
emit_changed();
}
-void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p_offset) {
+void Animation::audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_AUDIO);
@@ -2370,7 +2372,7 @@ void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p
emit_changed();
}
-void Animation::audio_track_set_key_end_offset(int p_track, int p_key, float p_offset) {
+void Animation::audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_AUDIO);
@@ -2400,7 +2402,7 @@ RES Animation::audio_track_get_key_stream(int p_track, int p_key) const {
return at->values[p_key].value.stream;
}
-float Animation::audio_track_get_key_start_offset(int p_track, int p_key) const {
+real_t Animation::audio_track_get_key_start_offset(int p_track, int p_key) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
const Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
@@ -2412,7 +2414,7 @@ float Animation::audio_track_get_key_start_offset(int p_track, int p_key) const
return at->values[p_key].value.start_offset;
}
-float Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
+real_t Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
const Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_AUDIO, 0);
@@ -2426,7 +2428,7 @@ float Animation::audio_track_get_key_end_offset(int p_track, int p_key) const {
//
-int Animation::animation_track_insert_key(int p_track, float p_time, const StringName &p_animation) {
+int Animation::animation_track_insert_key(int p_track, double p_time, const StringName &p_animation) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_ANIMATION, -1);
@@ -2470,7 +2472,7 @@ StringName Animation::animation_track_get_key_animation(int p_track, int p_key)
return at->values[p_key].value;
}
-void Animation::set_length(float p_length) {
+void Animation::set_length(real_t p_length) {
if (p_length < ANIM_MIN_LENGTH) {
p_length = ANIM_MIN_LENGTH;
}
@@ -2478,7 +2480,7 @@ void Animation::set_length(float p_length) {
emit_changed();
}
-float Animation::get_length() const {
+real_t Animation::get_length() const {
return length;
}
@@ -2558,12 +2560,12 @@ void Animation::track_swap(int p_track, int p_with_track) {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-void Animation::set_step(float p_step) {
+void Animation::set_step(real_t p_step) {
step = p_step;
emit_changed();
}
-float Animation::get_step() const {
+real_t Animation::get_step() const {
return step;
}
@@ -2708,7 +2710,7 @@ void Animation::clear() {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err, float p_alowed_angular_err, float p_max_optimizable_angle, const Vector3 &p_norm) {
+bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) {
real_t c = (t1.time - t0.time) / (t2.time - t0.time);
real_t t[3] = { -1, -1, -1 };
@@ -2727,9 +2729,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
} else {
Vector3 pd = (v2 - v0);
- float d0 = pd.dot(v0);
- float d1 = pd.dot(v1);
- float d2 = pd.dot(v2);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
if (d1 < d0 || d1 > d2) {
return false;
}
@@ -2817,9 +2819,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
} else {
Vector3 pd = (v2 - v0);
- float d0 = pd.dot(v0);
- float d1 = pd.dot(v1);
- float d2 = pd.dot(v2);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
if (d1 < d0 || d1 > d2) {
return false; //beyond segment range
}
@@ -2875,7 +2877,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
return erase;
}
-void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err, float p_allowed_angular_err, float p_max_optimizable_angle) {
+void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
ERR_FAIL_INDEX(p_idx, tracks.size());
ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
@@ -2915,7 +2917,7 @@ void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err,
}
}
-void Animation::optimize(float p_allowed_linear_err, float p_allowed_angular_err, float p_max_optimizable_angle) {
+void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
if (tracks[i]->type == TYPE_TRANSFORM3D) {
_transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 920ee2e5ab..6227f6967f 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -76,8 +76,8 @@ private:
};
struct Key {
- float transition = 1.0;
- float time = 0.0; // time in secs
+ real_t transition = 1.0;
+ double time = 0.0; // time in secs
};
// transform key holds either Vector3 or Quaternion
@@ -129,7 +129,7 @@ private:
struct BezierKey {
Vector2 in_handle; //relative (x always <0)
Vector2 out_handle; //relative (x always >0)
- float value = 0.0;
+ real_t value = 0.0;
};
struct BezierTrack : public Track {
@@ -144,8 +144,8 @@ private:
struct AudioKey {
RES stream;
- float start_offset = 0.0; //offset from start
- float end_offset = 0.0; //offset from end, if 0 then full length or infinite
+ real_t start_offset = 0.0; //offset from start
+ real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite
AudioKey() {
}
};
@@ -172,46 +172,46 @@ private:
/*
template<class T>
- int _insert_pos(float p_time, T& p_keys);*/
+ int _insert_pos(double p_time, T& p_keys);*/
template <class T>
void _clear(T &p_keys);
template <class T, class V>
- int _insert(float p_time, T &p_keys, const V &p_value);
+ int _insert(double p_time, T &p_keys, const V &p_value);
template <class K>
- inline int _find(const Vector<K> &p_keys, float p_time) const;
+ inline int _find(const Vector<K> &p_keys, double p_time) const;
- _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, float p_c) const;
+ _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
- _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_c) const;
- _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, float p_c) const;
- _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, float p_c) const;
- _FORCE_INLINE_ float _interpolate(const float &p_a, const float &p_b, float p_c) const;
+ _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
+ _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
+ _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
+ _FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
- _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, float p_c) const;
- _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, float p_c) const;
- _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, float p_c) const;
- _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, float p_c) const;
- _FORCE_INLINE_ float _cubic_interpolate(const float &p_pre_a, const float &p_a, const float &p_b, const float &p_post_b, float p_c) const;
+ _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
+ _FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const;
template <class T>
- _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, float p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
template <class T>
- _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, float from_time, float to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
- _FORCE_INLINE_ void _value_track_get_key_indices_in_range(const ValueTrack *vt, float from_time, float to_time, List<int> *p_indices) const;
- _FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, float from_time, float to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const;
+ _FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const;
- float length = 1.0;
- float step = 0.1;
+ double length = 1.0;
+ real_t step = 0.1;
bool loop = false;
// bind helpers
private:
- Array _transform_track_interpolate(int p_track, float p_time) const {
+ Array _transform_track_interpolate(int p_track, double p_time) const {
Vector3 loc;
Quaternion rot;
Vector3 scale;
@@ -223,7 +223,7 @@ private:
return ret;
}
- Vector<int> _value_track_get_key_indices(int p_track, float p_time, float p_delta) const {
+ Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
Vector<int> idxr;
@@ -233,7 +233,7 @@ private:
}
return idxr;
}
- Vector<int> _method_track_get_key_indices(int p_track, float p_time, float p_delta) const {
+ Vector<int> _method_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
method_track_get_key_indices(p_track, p_time, p_delta, &idxs);
Vector<int> idxr;
@@ -244,8 +244,8 @@ private:
return idxr;
}
- bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err, float p_alowed_angular_err, float p_max_optimizable_angle, const Vector3 &p_norm);
- void _transform_track_optimize(int p_idx, float p_allowed_linear_err = 0.05, float p_allowed_angular_err = 0.01, float p_max_optimizable_angle = Math_PI * 0.125);
+ bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
+ void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -279,75 +279,75 @@ public:
void track_set_enabled(int p_track, bool p_enabled);
bool track_is_enabled(int p_track) const;
- void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
- void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
+ void track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1);
+ void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition);
void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value);
- void track_set_key_time(int p_track, int p_key_idx, float p_time);
- int track_find_key(int p_track, float p_time, bool p_exact = false) const;
+ void track_set_key_time(int p_track, int p_key_idx, double p_time);
+ int track_find_key(int p_track, double p_time, bool p_exact = false) const;
void track_remove_key(int p_track, int p_idx);
- void track_remove_key_at_time(int p_track, float p_time);
+ void track_remove_key_at_time(int p_track, double p_time);
int track_get_key_count(int p_track) const;
Variant track_get_key_value(int p_track, int p_key_idx) const;
- float track_get_key_time(int p_track, int p_key_idx) const;
- float track_get_key_transition(int p_track, int p_key_idx) const;
+ double track_get_key_time(int p_track, int p_key_idx) const;
+ real_t track_get_key_transition(int p_track, int p_key_idx) const;
- int transform_track_insert_key(int p_track, float p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
+ int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
- int bezier_track_insert_key(int p_track, float p_time, float p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle);
- void bezier_track_set_key_value(int p_track, int p_index, float p_value);
+ int bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle);
+ void bezier_track_set_key_value(int p_track, int p_index, real_t p_value);
void bezier_track_set_key_in_handle(int p_track, int p_index, const Vector2 &p_handle);
void bezier_track_set_key_out_handle(int p_track, int p_index, const Vector2 &p_handle);
- float bezier_track_get_key_value(int p_track, int p_index) const;
+ real_t bezier_track_get_key_value(int p_track, int p_index) const;
Vector2 bezier_track_get_key_in_handle(int p_track, int p_index) const;
Vector2 bezier_track_get_key_out_handle(int p_track, int p_index) const;
- float bezier_track_interpolate(int p_track, float p_time) const;
+ real_t bezier_track_interpolate(int p_track, double p_time) const;
- int audio_track_insert_key(int p_track, float p_time, const RES &p_stream, float p_start_offset = 0, float p_end_offset = 0);
+ int audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0);
void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream);
- void audio_track_set_key_start_offset(int p_track, int p_key, float p_offset);
- void audio_track_set_key_end_offset(int p_track, int p_key, float p_offset);
+ void audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset);
+ void audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset);
RES audio_track_get_key_stream(int p_track, int p_key) const;
- float audio_track_get_key_start_offset(int p_track, int p_key) const;
- float audio_track_get_key_end_offset(int p_track, int p_key) const;
+ real_t audio_track_get_key_start_offset(int p_track, int p_key) const;
+ real_t audio_track_get_key_end_offset(int p_track, int p_key) const;
- int animation_track_insert_key(int p_track, float p_time, const StringName &p_animation);
+ int animation_track_insert_key(int p_track, double p_time, const StringName &p_animation);
void animation_track_set_key_animation(int p_track, int p_key, const StringName &p_animation);
StringName animation_track_get_key_animation(int p_track, int p_key) const;
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Error transform_track_interpolate(int p_track, float p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
+ Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
- Variant value_track_interpolate(int p_track, float p_time) const;
- void value_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ Variant value_track_interpolate(int p_track, double p_time) const;
+ void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
- void method_track_get_key_indices(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, float p_time, float p_delta, List<int> *p_indices) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
- void set_length(float p_length);
- float get_length() const;
+ void set_length(real_t p_length);
+ real_t get_length() const;
void set_loop(bool p_enabled);
bool has_loop() const;
- void set_step(float p_step);
- float get_step() const;
+ void set_step(real_t p_step);
+ real_t get_step() const;
void clear();
- void optimize(float p_allowed_linear_err = 0.05, float p_allowed_angular_err = 0.01, float p_max_optimizable_angle = Math_PI * 0.125);
+ void optimize(real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
Animation();
~Animation();
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 8ffd2df112..ef070589e4 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -261,11 +261,11 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in
sign = -1;
}
- float global_rate_scale = AudioServer::get_singleton()->get_global_rate_scale();
- float base_rate = AudioServer::get_singleton()->get_mix_rate() * global_rate_scale;
+ float base_rate = AudioServer::get_singleton()->get_mix_rate();
float srate = base->mix_rate;
srate *= p_rate_scale;
- float fincrement = srate / base_rate;
+ float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale();
+ float fincrement = (srate * playback_speed_scale) / base_rate;
int32_t increment = int32_t(MAX(fincrement * MIX_FRAC_LEN, 1));
increment *= sign;
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
new file mode 100644
index 0000000000..7501efea9e
--- /dev/null
+++ b/scene/resources/canvas_item_material.cpp
@@ -0,0 +1,306 @@
+/*************************************************************************/
+/* canvas_item_material.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "canvas_item_material.h"
+
+#include "core/version.h"
+
+Mutex CanvasItemMaterial::material_mutex;
+SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr;
+Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr;
+
+void CanvasItemMaterial::init_shaders() {
+ dirty_materials = memnew(SelfList<CanvasItemMaterial>::List);
+
+ shader_names = memnew(ShaderNames);
+
+ shader_names->particles_anim_h_frames = "particles_anim_h_frames";
+ shader_names->particles_anim_v_frames = "particles_anim_v_frames";
+ shader_names->particles_anim_loop = "particles_anim_loop";
+}
+
+void CanvasItemMaterial::finish_shaders() {
+ memdelete(dirty_materials);
+ memdelete(shader_names);
+ dirty_materials = nullptr;
+}
+
+void CanvasItemMaterial::_update_shader() {
+ dirty_materials->remove(&element);
+
+ MaterialKey mk = _compute_key();
+ if (mk.key == current_key.key) {
+ return; //no update required in the end
+ }
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ RS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+ }
+
+ current_key = mk;
+
+ if (shader_map.has(mk)) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+ shader_map[mk].users++;
+ return;
+ }
+
+ //must create a shader!
+
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s CanvasItemMaterial.\n\n";
+
+ code += "shader_type canvas_item;\nrender_mode ";
+ switch (blend_mode) {
+ case BLEND_MODE_MIX:
+ code += "blend_mix";
+ break;
+ case BLEND_MODE_ADD:
+ code += "blend_add";
+ break;
+ case BLEND_MODE_SUB:
+ code += "blend_sub";
+ break;
+ case BLEND_MODE_MUL:
+ code += "blend_mul";
+ break;
+ case BLEND_MODE_PREMULT_ALPHA:
+ code += "blend_premul_alpha";
+ break;
+ case BLEND_MODE_DISABLED:
+ code += "blend_disabled";
+ break;
+ }
+
+ switch (light_mode) {
+ case LIGHT_MODE_NORMAL:
+ break;
+ case LIGHT_MODE_UNSHADED:
+ code += ",unshaded";
+ break;
+ case LIGHT_MODE_LIGHT_ONLY:
+ code += ",light_only";
+ break;
+ }
+
+ code += ";\n";
+
+ if (particles_animation) {
+ code += "uniform int particles_anim_h_frames;\n";
+ code += "uniform int particles_anim_v_frames;\n";
+ code += "uniform bool particles_anim_loop;\n\n";
+
+ code += "void vertex() {\n";
+ code += " float h_frames = float(particles_anim_h_frames);\n";
+ code += " float v_frames = float(particles_anim_v_frames);\n";
+ code += " VERTEX.xy /= vec2(h_frames, v_frames);\n";
+ code += " float particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n";
+ code += " float particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
+ code += " if (!particles_anim_loop) {\n";
+ code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n";
+ code += " } else {\n";
+ code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
+ code += " }";
+ code += " UV /= vec2(h_frames, v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += "}\n";
+ }
+
+ ShaderData shader_data;
+ shader_data.shader = RS::get_singleton()->shader_create();
+ shader_data.users = 1;
+
+ RS::get_singleton()->shader_set_code(shader_data.shader, code);
+
+ shader_map[mk] = shader_data;
+
+ RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+}
+
+void CanvasItemMaterial::flush_changes() {
+ MutexLock lock(material_mutex);
+
+ while (dirty_materials->first()) {
+ dirty_materials->first()->self()->_update_shader();
+ }
+}
+
+void CanvasItemMaterial::_queue_shader_change() {
+ MutexLock lock(material_mutex);
+
+ if (!element.in_list()) {
+ dirty_materials->add(&element);
+ }
+}
+
+bool CanvasItemMaterial::_is_shader_dirty() const {
+ MutexLock lock(material_mutex);
+
+ return element.in_list();
+}
+
+void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) {
+ blend_mode = p_blend_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const {
+ return blend_mode;
+}
+
+void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) {
+ light_mode = p_light_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
+ return light_mode;
+}
+
+void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) {
+ particles_animation = p_particles_anim;
+ _queue_shader_change();
+ notify_property_list_changed();
+}
+
+bool CanvasItemMaterial::get_particles_animation() const {
+ return particles_animation;
+}
+
+void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) {
+ particles_anim_h_frames = p_frames;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_h_frames() const {
+ return particles_anim_h_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) {
+ particles_anim_v_frames = p_frames;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
+}
+
+int CanvasItemMaterial::get_particles_anim_v_frames() const {
+ return particles_anim_v_frames;
+}
+
+void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) {
+ particles_anim_loop = p_loop;
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
+}
+
+bool CanvasItemMaterial::get_particles_anim_loop() const {
+ return particles_anim_loop;
+}
+
+void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("particles_anim_") && !particles_animation) {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+}
+
+RID CanvasItemMaterial::get_shader_rid() const {
+ ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
+ return shader_map[current_key].shader;
+}
+
+Shader::Mode CanvasItemMaterial::get_shader_mode() const {
+ return Shader::MODE_CANVAS_ITEM;
+}
+
+void CanvasItemMaterial::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode);
+
+ ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
+ ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
+
+ ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation);
+ ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames);
+
+ ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);
+ ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop");
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
+ BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
+ BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
+ BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
+ BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+
+ BIND_ENUM_CONSTANT(LIGHT_MODE_NORMAL);
+ BIND_ENUM_CONSTANT(LIGHT_MODE_UNSHADED);
+ BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
+}
+
+CanvasItemMaterial::CanvasItemMaterial() :
+ element(this) {
+ set_particles_anim_h_frames(1);
+ set_particles_anim_v_frames(1);
+ set_particles_anim_loop(false);
+
+ current_key.invalid_key = 1;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::~CanvasItemMaterial() {
+ MutexLock lock(material_mutex);
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ RS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+
+ RS::get_singleton()->material_set_shader(_get_material(), RID());
+ }
+}
diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h
new file mode 100644
index 0000000000..0a813e0ae5
--- /dev/null
+++ b/scene/resources/canvas_item_material.h
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* canvas_item_material.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CANVAS_ITEM_MATERIAL_H
+#define CANVAS_ITEM_MATERIAL_H
+
+#include "scene/resources/material.h"
+
+class CanvasItemMaterial : public Material {
+ GDCLASS(CanvasItemMaterial, Material);
+
+public:
+ enum BlendMode {
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
+ };
+
+ enum LightMode {
+ LIGHT_MODE_NORMAL,
+ LIGHT_MODE_UNSHADED,
+ LIGHT_MODE_LIGHT_ONLY
+ };
+
+private:
+ union MaterialKey {
+ struct {
+ uint32_t blend_mode : 4;
+ uint32_t light_mode : 4;
+ uint32_t particles_animation : 1;
+ uint32_t invalid_key : 1;
+ };
+
+ uint32_t key = 0;
+
+ bool operator<(const MaterialKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ struct ShaderNames {
+ StringName particles_anim_h_frames;
+ StringName particles_anim_v_frames;
+ StringName particles_anim_loop;
+ };
+
+ static ShaderNames *shader_names;
+
+ struct ShaderData {
+ RID shader;
+ int users = 0;
+ };
+
+ static Map<MaterialKey, ShaderData> shader_map;
+
+ MaterialKey current_key;
+
+ _FORCE_INLINE_ MaterialKey _compute_key() const {
+ MaterialKey mk;
+ mk.key = 0;
+ mk.blend_mode = blend_mode;
+ mk.light_mode = light_mode;
+ mk.particles_animation = particles_animation;
+ return mk;
+ }
+
+ static Mutex material_mutex;
+ static SelfList<CanvasItemMaterial>::List *dirty_materials;
+ SelfList<CanvasItemMaterial> element;
+
+ void _update_shader();
+ _FORCE_INLINE_ void _queue_shader_change();
+ _FORCE_INLINE_ bool _is_shader_dirty() const;
+
+ BlendMode blend_mode = BLEND_MODE_MIX;
+ LightMode light_mode = LIGHT_MODE_NORMAL;
+ bool particles_animation = false;
+
+ // Initialized in the constructor.
+ int particles_anim_h_frames;
+ int particles_anim_v_frames;
+ bool particles_anim_loop;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
+
+public:
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
+ void set_light_mode(LightMode p_light_mode);
+ LightMode get_light_mode() const;
+
+ void set_particles_animation(bool p_particles_anim);
+ bool get_particles_animation() const;
+
+ void set_particles_anim_h_frames(int p_frames);
+ int get_particles_anim_h_frames() const;
+ void set_particles_anim_v_frames(int p_frames);
+ int get_particles_anim_v_frames() const;
+
+ void set_particles_anim_loop(bool p_loop);
+ bool get_particles_anim_loop() const;
+
+ static void init_shaders();
+ static void finish_shaders();
+ static void flush_changes();
+
+ virtual RID get_shader_rid() const override;
+
+ virtual Shader::Mode get_shader_mode() const override;
+
+ CanvasItemMaterial();
+ virtual ~CanvasItemMaterial();
+};
+
+VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
+VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)
+
+#endif // CANVAS_ITEM_MATERIAL_H
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index e5edba8a67..0818e4fd99 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -38,11 +38,11 @@ Vector<Vector2> CapsuleShape2D::_get_points() const {
Vector<Vector2> points;
const real_t turn_step = Math_TAU / 24.0;
for (int i = 0; i < 24; i++) {
- Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5);
+ Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -height * 0.5 + radius : height * 0.5 - radius);
- points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * get_radius() + ofs);
+ points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius + ofs);
if (i == 6 || i == 18) {
- points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * get_radius() - ofs);
+ points.push_back(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * radius - ofs);
}
}
@@ -60,6 +60,9 @@ void CapsuleShape2D::_update_shape() {
void CapsuleShape2D::set_radius(real_t p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ height = radius * 2.0;
+ }
_update_shape();
}
@@ -69,10 +72,9 @@ real_t CapsuleShape2D::get_radius() const {
void CapsuleShape2D::set_height(real_t p_height) {
height = p_height;
- if (height < 0) {
- height = 0;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
}
-
_update_shape();
}
@@ -93,15 +95,11 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
}
Rect2 CapsuleShape2D::get_rect() const {
- Vector2 he = Point2(get_radius(), get_radius() + get_height() * 0.5);
- Rect2 rect;
- rect.position = -he;
- rect.size = he * 2.0;
- return rect;
+ return Rect2(0, 0, radius, height);
}
real_t CapsuleShape2D::get_enclosing_radius() const {
- return radius + height * 0.5;
+ return height * 0.5;
}
void CapsuleShape2D::_bind_methods() {
@@ -113,6 +111,8 @@ void CapsuleShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height"), "set_height", "get_height");
+ ADD_LINKED_PROPERTY("radius", "height");
+ ADD_LINKED_PROPERTY("height", "radius");
}
CapsuleShape2D::CapsuleShape2D() :
diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h
index 439b67e8c3..37b6c52c0e 100644
--- a/scene/resources/capsule_shape_2d.h
+++ b/scene/resources/capsule_shape_2d.h
@@ -36,7 +36,7 @@
class CapsuleShape2D : public Shape2D {
GDCLASS(CapsuleShape2D, Shape2D);
- real_t height = 20.0;
+ real_t height = 30.0;
real_t radius = 10.0;
void _update_shape();
diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp
index 226fe1ecd2..afec7b1877 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -37,7 +37,7 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
Vector<Vector3> points;
- Vector3 d(0, height * 0.5, 0);
+ Vector3 d(0, height * 0.5 - radius, 0);
for (int i = 0; i < 360; i++) {
float ra = Math::deg2rad((float)i);
float rb = Math::deg2rad((float)i + 1);
@@ -67,7 +67,7 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const {
}
real_t CapsuleShape3D::get_enclosing_radius() const {
- return radius + height * 0.5;
+ return height * 0.5;
}
void CapsuleShape3D::_update_shape() {
@@ -80,6 +80,9 @@ void CapsuleShape3D::_update_shape() {
void CapsuleShape3D::set_radius(float p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ height = radius * 2.0;
+ }
_update_shape();
notify_change_to_owners();
}
@@ -90,6 +93,9 @@ float CapsuleShape3D::get_radius() const {
void CapsuleShape3D::set_height(float p_height) {
height = p_height;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
+ }
_update_shape();
notify_change_to_owners();
}
@@ -106,6 +112,8 @@ void CapsuleShape3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height");
+ ADD_LINKED_PROPERTY("radius", "height");
+ ADD_LINKED_PROPERTY("height", "radius");
}
CapsuleShape3D::CapsuleShape3D() :
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index 25645ecf9d..f09b4fb77d 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -36,7 +36,7 @@
class CapsuleShape3D : public Shape3D {
GDCLASS(CapsuleShape3D, Shape3D);
float radius = 1.0;
- float height = 1.0;
+ float height = 3.0;
protected:
static void _bind_methods();
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index c13db83d6d..a364a27e80 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -662,19 +662,27 @@ void Curve2D::_bake() const {
if (points.size() == 0) {
baked_point_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
if (points.size() == 1) {
baked_point_cache.resize(1);
baked_point_cache.set(0, points[0].pos);
+
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
return;
}
Vector2 pos = points[0].pos;
+ float dist = 0.0;
+
List<Vector2> pointlist;
+ List<float> distlist;
pointlist.push_back(pos); //start always from origin
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
@@ -712,7 +720,10 @@ void Curve2D::_bake() const {
pos = npp;
p = mid;
+ dist += d;
+
pointlist.push_back(pos);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -722,16 +733,20 @@ void Curve2D::_bake() const {
Vector2 lastpos = points[points.size() - 1].pos;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(lastpos);
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
+ baked_dist_cache.resize(distlist.size());
+
Vector2 *w = baked_point_cache.ptrw();
- int idx = 0;
+ float *wd = baked_dist_cache.ptrw();
- for (const Vector2 &E : pointlist) {
- w[idx] = E;
- idx++;
+ for (int i = 0; i < pointlist.size(); i++) {
+ w[i] = pointlist[i];
+ wd[i] = distlist[i];
}
}
@@ -766,19 +781,26 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, (float)bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
@@ -1145,6 +1167,7 @@ void Curve3D::_bake() const {
baked_point_cache.resize(0);
baked_tilt_cache.resize(0);
baked_up_vector_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
@@ -1153,6 +1176,8 @@ void Curve3D::_bake() const {
baked_point_cache.set(0, points[0].pos);
baked_tilt_cache.resize(1);
baked_tilt_cache.set(0, points[0].tilt);
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
if (up_vector_enabled) {
baked_up_vector_cache.resize(1);
@@ -1165,8 +1190,12 @@ void Curve3D::_bake() const {
}
Vector3 pos = points[0].pos;
+ float dist = 0.0;
List<Plane> pointlist;
+ List<float> distlist;
+
pointlist.push_back(Plane(pos, points[0].tilt));
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
@@ -1207,7 +1236,10 @@ void Curve3D::_bake() const {
Plane post;
post.normal = pos;
post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid);
+ dist += d;
+
pointlist.push_back(post);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -1218,8 +1250,10 @@ void Curve3D::_bake() const {
float lastilt = points[points.size() - 1].tilt;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(Plane(lastpos, lastilt));
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
Vector3 *w = baked_point_cache.ptrw();
@@ -1231,6 +1265,9 @@ void Curve3D::_bake() const {
baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0);
Vector3 *up_write = baked_up_vector_cache.ptrw();
+ baked_dist_cache.resize(pointlist.size());
+ float *wd = baked_dist_cache.ptrw();
+
Vector3 sideways;
Vector3 up;
Vector3 forward;
@@ -1242,6 +1279,7 @@ void Curve3D::_bake() const {
for (const Plane &E : pointlist) {
w[idx] = E.normal;
wt[idx] = E.d;
+ wd[idx] = distlist[idx];
if (!up_vector_enabled) {
idx++;
@@ -1308,19 +1346,26 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
@@ -1366,7 +1411,7 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const {
frac /= bake_interval;
}
- return Math::lerp(r[idx], r[idx + 1], frac);
+ return Math::lerp(r[idx], r[idx + 1], (real_t)frac);
}
Vector3 Curve3D::interpolate_baked_up_vector(float p_offset, bool p_apply_tilt) const {
@@ -1424,7 +1469,7 @@ PackedVector3Array Curve3D::get_baked_points() const {
return baked_point_cache;
}
-PackedFloat32Array Curve3D::get_baked_tilts() const {
+Vector<real_t> Curve3D::get_baked_tilts() const {
if (baked_cache_dirty) {
_bake();
}
@@ -1545,7 +1590,7 @@ Dictionary Curve3D::_get_data() const {
PackedVector3Array d;
d.resize(points.size() * 3);
Vector3 *w = d.ptrw();
- PackedFloat32Array t;
+ Vector<real_t> t;
t.resize(points.size());
real_t *wt = t.ptrw();
@@ -1571,7 +1616,7 @@ void Curve3D::_set_data(const Dictionary &p_data) {
ERR_FAIL_COND(pc % 3 != 0);
points.resize(pc / 3);
const Vector3 *r = rp.ptr();
- PackedFloat32Array rtl = p_data["tilts"];
+ Vector<real_t> rtl = p_data["tilts"];
const real_t *rt = rtl.ptr();
for (int i = 0; i < points.size(); i++) {
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 746c6fa597..5808fd6508 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -161,6 +161,7 @@ class Curve2D : public Resource {
mutable bool baked_cache_dirty = false;
mutable PackedVector2Array baked_point_cache;
+ mutable PackedFloat32Array baked_dist_cache;
mutable float baked_max_ofs = 0.0;
void _bake() const;
@@ -222,8 +223,9 @@ class Curve3D : public Resource {
mutable bool baked_cache_dirty = false;
mutable PackedVector3Array baked_point_cache;
- mutable PackedFloat32Array baked_tilt_cache;
+ mutable Vector<real_t> baked_tilt_cache;
mutable PackedVector3Array baked_up_vector_cache;
+ mutable PackedFloat32Array baked_dist_cache;
mutable float baked_max_ofs = 0.0;
void _bake() const;
@@ -265,7 +267,7 @@ public:
float interpolate_baked_tilt(float p_offset) const;
Vector3 interpolate_baked_up_vector(float p_offset, bool p_apply_tilt = false) const;
PackedVector3Array get_baked_points() const; //useful for going through
- PackedFloat32Array get_baked_tilts() const; //useful for going through
+ Vector<real_t> get_baked_tilts() const; //useful for going through
PackedVector3Array get_baked_up_vectors() const;
Vector3 get_closest_point(const Vector3 &p_to_point) const;
float get_closest_offset(const Vector3 &p_to_point) const;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index d0dee2b5e3..dd7b348498 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -212,26 +212,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("outline_size", "LinkButton", 0);
theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
- // ColorPickerButton
-
- theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
- theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
- theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
- theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
- theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
-
- theme->set_font("font", "ColorPickerButton", Ref<Font>());
- theme->set_font_size("font_size", "ColorPickerButton", -1);
-
- theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
- theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
- theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
-
- theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
- theme->set_constant("outline_size", "ColorPickerButton", 0);
-
// OptionButton
Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
@@ -704,6 +684,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("checked", "Tree", make_icon(checked_png));
theme->set_icon("unchecked", "Tree", make_icon(unchecked_png));
+ theme->set_icon("indeterminate", "Tree", make_icon(indeterminate_png));
theme->set_icon("updown", "Tree", make_icon(updown_png));
theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png));
theme->set_icon("arrow", "Tree", make_icon(arrow_down_png));
@@ -858,12 +839,42 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png));
theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
- theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png));
+ theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png));
theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png));
theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png));
+ // ColorPickerButton
+
theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
+ theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
+ theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
+ theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
+ theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
+ theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
+
+ theme->set_font("font", "ColorPickerButton", Ref<Font>());
+ theme->set_font_size("font_size", "ColorPickerButton", -1);
+
+ theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
+ theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
+ theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
+
+ theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
+ theme->set_constant("outline_size", "ColorPickerButton", 0);
+
+ // ColorPresetButton
+
+ Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2);
+ preset_sb->set_corner_radius_all(2);
+ preset_sb->set_corner_detail(2);
+ preset_sb->set_anti_aliased(false);
+
+ theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
+ theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png));
+ theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png));
// TooltipPanel
@@ -914,7 +925,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
- theme->set_constant("line_separation", "RichTextLabel", 1 * scale);
+ theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
@@ -953,6 +964,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
+ theme->set_icon("layout", "GraphEdit", make_icon(icon_grid_layout_png));
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
@@ -964,7 +976,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Visual Node Ports
- theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
+ theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
new file mode 100644
index 0000000000..a249252a79
--- /dev/null
+++ b/scene/resources/default_theme/icon_grid_layout.png
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.png b/scene/resources/default_theme/indeterminate.png
new file mode 100644
index 0000000000..28a457b251
--- /dev/null
+++ b/scene/resources/default_theme/indeterminate.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index cdb1bc527b..6a556c1112 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -50,10 +50,6 @@ static const unsigned char checked_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char checker_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
static const unsigned char close_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -182,6 +178,10 @@ static const unsigned char icon_folder_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char icon_grid_layout_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x5, 0x52, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3d, 0x22, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3d, 0x22, 0x32, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3d, 0x22, 0x33, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x22, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x47, 0x72, 0x69, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x2f, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x10, 0xfa, 0x51, 0xae, 0x0, 0x0, 0x1, 0x82, 0x69, 0x43, 0x43, 0x50, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0xbf, 0x4b, 0x42, 0x51, 0x14, 0xc7, 0x3f, 0x5a, 0xa1, 0x94, 0x61, 0x50, 0x43, 0x43, 0x83, 0x84, 0x45, 0x83, 0x85, 0x15, 0x84, 0x2d, 0xd, 0x4a, 0xbf, 0xa0, 0x1a, 0xd4, 0x20, 0xab, 0x45, 0x9f, 0xbf, 0x2, 0xb5, 0xc7, 0x7b, 0x4a, 0x48, 0x6b, 0xd0, 0x2a, 0x14, 0x44, 0x2d, 0xfd, 0x1a, 0xea, 0x2f, 0xa8, 0x35, 0x68, 0xe, 0x82, 0xa2, 0x8, 0xa2, 0x2d, 0x68, 0x2e, 0x6a, 0xa9, 0x78, 0x9d, 0xa7, 0x82, 0x11, 0x79, 0x2e, 0xe7, 0x9e, 0xcf, 0xfd, 0xde, 0x7b, 0xe, 0xf7, 0x9e, 0xb, 0xd6, 0x70, 0x46, 0xc9, 0xea, 0x8d, 0x5e, 0xc8, 0xe6, 0xf2, 0x5a, 0x70, 0xd2, 0xef, 0x5a, 0x88, 0x2c, 0xba, 0x6c, 0xcf, 0xd8, 0x71, 0x62, 0xa3, 0xf, 0x5f, 0x54, 0xd1, 0xd5, 0xd9, 0xd0, 0x44, 0x98, 0xba, 0xf6, 0x71, 0x87, 0xc5, 0x8c, 0x37, 0xfd, 0x66, 0xad, 0xfa, 0xe7, 0xfe, 0xb5, 0x96, 0x78, 0x42, 0x57, 0xc0, 0x62, 0x17, 0x1e, 0x53, 0x54, 0x2d, 0x2f, 0x3c, 0x25, 0x3c, 0xb3, 0x96, 0x57, 0x4d, 0xde, 0x16, 0xee, 0x50, 0xd2, 0xd1, 0xb8, 0xf0, 0xa9, 0xb0, 0x47, 0x93, 0xb, 0xa, 0xdf, 0x9a, 0x7a, 0xac, 0xc2, 0x2f, 0x26, 0xa7, 0x2a, 0xfc, 0x65, 0xb2, 0x16, 0xe, 0x6, 0xc0, 0xda, 0x26, 0xec, 0x4a, 0xfd, 0xe2, 0xd8, 0x2f, 0x56, 0xd2, 0x5a, 0x56, 0x58, 0x5e, 0x8e, 0x3b, 0x9b, 0x29, 0x28, 0xd5, 0xfb, 0x98, 0x2f, 0x71, 0x24, 0x72, 0xf3, 0x21, 0x89, 0xdd, 0xe2, 0x5d, 0xe8, 0x4, 0x99, 0xc4, 0x8f, 0x8b, 0x69, 0xc6, 0x9, 0x30, 0xc2, 0x20, 0xa3, 0x32, 0x8f, 0xd0, 0xcf, 0x10, 0x3, 0xb2, 0xa2, 0x4e, 0xbe, 0xb7, 0x9c, 0x3f, 0xc7, 0xaa, 0xe4, 0x2a, 0x32, 0xab, 0x14, 0xd1, 0x58, 0x21, 0x45, 0x9a, 0x3c, 0x1e, 0x51, 0xb, 0x52, 0x3d, 0x21, 0x31, 0x29, 0x7a, 0x42, 0x46, 0x86, 0xa2, 0xd9, 0xff, 0xbf, 0x7d, 0xd5, 0x93, 0xc3, 0x43, 0x95, 0xea, 0xe, 0x3f, 0x34, 0x3d, 0x19, 0xc6, 0x5b, 0xf, 0xd8, 0xb6, 0xe0, 0xbb, 0x64, 0x18, 0x9f, 0x87, 0x86, 0xf1, 0x7d, 0x4, 0xd, 0x8f, 0x70, 0x91, 0xab, 0xe5, 0xaf, 0x1e, 0x80, 0xef, 0x5d, 0xf4, 0x52, 0x4d, 0x73, 0xef, 0x83, 0x73, 0x3, 0xce, 0x2e, 0x6b, 0x5a, 0x6c, 0x7, 0xce, 0x37, 0xa1, 0xf3, 0x41, 0x8d, 0x6a, 0xd1, 0xb2, 0xd4, 0x20, 0x6e, 0x4d, 0x26, 0xe1, 0xf5, 0x4, 0x5a, 0x23, 0xd0, 0x7e, 0xd, 0xcd, 0x4b, 0x95, 0x9e, 0x55, 0xf7, 0x39, 0xbe, 0x87, 0xf0, 0xba, 0x7c, 0xd5, 0x15, 0xec, 0xee, 0x41, 0xaf, 0x9c, 0x77, 0x2e, 0xff, 0x0, 0xa6, 0xc4, 0x68, 0x3, 0x1f, 0xd7, 0x32, 0xd8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x1, 0x40, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x9d, 0xd2, 0xbd, 0x4a, 0x5d, 0x41, 0x14, 0x86, 0xe1, 0xe7, 0x84, 0xa3, 0x51, 0x22, 0x82, 0x41, 0x8c, 0x12, 0x2, 0xeb, 0x1a, 0x2c, 0x84, 0x4, 0x52, 0x88, 0xb1, 0xd2, 0x26, 0x76, 0x62, 0x67, 0xeb, 0xd, 0x78, 0x19, 0x56, 0x69, 0x82, 0x62, 0x67, 0x25, 0x82, 0x60, 0x97, 0xc2, 0x52, 0x8, 0x24, 0x95, 0x16, 0xa9, 0x6, 0x8c, 0x18, 0xb5, 0x30, 0x88, 0xa7, 0x90, 0x88, 0x5a, 0xec, 0x39, 0x66, 0xd8, 0x7a, 0x54, 0x5c, 0xb0, 0xd8, 0xcc, 0xbb, 0xe7, 0xfb, 0x58, 0x3f, 0xd3, 0x50, 0x8b, 0x94, 0xd2, 0x6b, 0xcc, 0x60, 0x25, 0x22, 0xae, 0xea, 0xff, 0xeb, 0xd1, 0xbc, 0x47, 0xfc, 0xd, 0xbd, 0x58, 0x7e, 0x4c, 0xc, 0x8d, 0x42, 0x3c, 0x82, 0x9f, 0xe8, 0xc1, 0x26, 0xce, 0x3b, 0x68, 0xae, 0xf1, 0x25, 0x22, 0x76, 0xeb, 0x15, 0xf4, 0xa3, 0xf, 0x97, 0x18, 0xce, 0xdf, 0x4e, 0x6, 0xfd, 0x77, 0x2a, 0xc8, 0x55, 0x4, 0xb6, 0xf1, 0x1b, 0x1f, 0x23, 0xe2, 0xfa, 0x49, 0x2d, 0xa4, 0x94, 0x7a, 0x30, 0x98, 0xd9, 0x5b, 0x4c, 0x63, 0x49, 0x35, 0xb, 0x68, 0xe1, 0x2f, 0x46, 0xf0, 0x22, 0xb3, 0x93, 0x88, 0xb8, 0x68, 0x1f, 0x36, 0xb0, 0x9f, 0x73, 0x7, 0x87, 0xf8, 0x53, 0xb0, 0x23, 0x2c, 0xe0, 0xa0, 0x60, 0xeb, 0xa, 0xb7, 0x1, 0xac, 0x60, 0x34, 0x5f, 0x1a, 0x52, 0xcd, 0x67, 0x16, 0x73, 0xe8, 0xca, 0x6c, 0x3f, 0xdf, 0x59, 0xcd, 0x9a, 0x5b, 0x83, 0x67, 0x47, 0x7b, 0xb, 0xa7, 0x98, 0xcf, 0x9, 0xc7, 0xaa, 0x2d, 0xac, 0xe5, 0xf3, 0xbf, 0xcc, 0xde, 0xe1, 0x47, 0x66, 0x5b, 0xa5, 0xc1, 0x67, 0xff, 0x87, 0x78, 0xa5, 0x9a, 0xc1, 0x1a, 0x5e, 0x65, 0xd6, 0xc2, 0x24, 0x3e, 0xe4, 0x36, 0xe0, 0x84, 0xda, 0x1a, 0x1f, 0x8a, 0x94, 0xd2, 0x6, 0xc6, 0x30, 0x1e, 0x11, 0xbf, 0xda, 0xbc, 0x7c, 0x89, 0xc3, 0x58, 0x44, 0x77, 0x7, 0x8f, 0x2e, 0x4c, 0xa9, 0x1e, 0xd1, 0xa7, 0x88, 0xd8, 0x29, 0x5b, 0xa0, 0x7a, 0xc2, 0xf1, 0x80, 0x41, 0x23, 0xe7, 0x4b, 0xbc, 0x79, 0x6a, 0xe5, 0x65, 0xb, 0x5f, 0x53, 0x4a, 0x67, 0x29, 0xa5, 0xf7, 0x25, 0x6f, 0x76, 0x12, 0xdc, 0x13, 0x7b, 0x98, 0x88, 0x88, 0xef, 0x25, 0xbc, 0x1, 0x6c, 0x4d, 0x56, 0x9e, 0x2a, 0x4e, 0x48, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char icon_grid_minimap_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -214,6 +214,14 @@ static const unsigned char icon_zoom_reset_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0xa, 0x3c, 0xc, 0x7b, 0xf0, 0xff, 0xc1, 0x7f, 0x9c, 0x22, 0xcf, 0x44, 0x1e, 0xbc, 0x84, 0x72, 0xb1, 0x8b, 0x3c, 0x58, 0x5, 0xe4, 0x40, 0xb8, 0x38, 0x45, 0x18, 0x60, 0x5c, 0x84, 0x30, 0x59, 0xa, 0xa0, 0x80, 0x6e, 0xa, 0x86, 0x92, 0x2f, 0x8, 0x3, 0x0, 0x69, 0xc8, 0x86, 0x87, 0x72, 0xca, 0x85, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char indeterminate_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+static const unsigned char indeterminate_disabled_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x3c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0x9e, 0x9e, 0x9e, 0x8c, 0x93, 0x80, 0x95, 0x0, 0x0, 0x0, 0x14, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xb9, 0x8d, 0x1e, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xd1, 0xe, 0x80, 0x20, 0x8, 0x85, 0xe1, 0x4, 0xd4, 0x30, 0x51, 0xb6, 0xde, 0xff, 0x5d, 0x23, 0x97, 0xad, 0xa5, 0xad, 0xff, 0xf2, 0xbb, 0x90, 0xe3, 0xb2, 0xc, 0x39, 0x40, 0xf2, 0x2d, 0x42, 0x70, 0x6, 0x10, 0x58, 0x6b, 0x4b, 0x39, 0x80, 0x1, 0x72, 0x91, 0xdc, 0x92, 0xc2, 0x68, 0x40, 0x2a, 0xdb, 0x95, 0x28, 0x19, 0xf8, 0x9a, 0x3b, 0xe4, 0xea, 0xe7, 0xb0, 0xdf, 0x7d, 0xc1, 0xdf, 0x1b, 0xc3, 0xd9, 0xe7, 0xb0, 0x74, 0xe, 0x83, 0x98, 0xfa, 0xf4, 0x35, 0xc2, 0xec, 0x73, 0xaf, 0xe, 0x57, 0x20, 0x8, 0x2c, 0x1a, 0x56, 0xe5, 0x32, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char line_edit_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp
index de5da944bc..d1a958ad38 100644
--- a/scene/resources/height_map_shape_3d.cpp
+++ b/scene/resources/height_map_shape_3d.cpp
@@ -41,7 +41,7 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
Vector2 size(map_width - 1, map_depth - 1);
Vector2 start = size * -0.5;
- const float *r = map_data.ptr();
+ const real_t *r = map_data.ptr();
// reserve some memory for our points..
points.resize(((map_width - 1) * map_depth * 2) + (map_width * (map_depth - 1) * 2) + ((map_width - 1) * (map_depth - 1) * 2));
@@ -105,7 +105,7 @@ void HeightMapShape3D::set_map_width(int p_new) {
int new_size = map_width * map_depth;
map_data.resize(map_width * map_depth);
- float *w = map_data.ptrw();
+ real_t *w = map_data.ptrw();
while (was_size < new_size) {
w[was_size++] = 0.0;
}
@@ -129,7 +129,7 @@ void HeightMapShape3D::set_map_depth(int p_new) {
int new_size = map_width * map_depth;
map_data.resize(new_size);
- float *w = map_data.ptrw();
+ real_t *w = map_data.ptrw();
while (was_size < new_size) {
w[was_size++] = 0.0;
}
@@ -143,7 +143,7 @@ int HeightMapShape3D::get_map_depth() const {
return map_depth;
}
-void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
+void HeightMapShape3D::set_map_data(Vector<real_t> p_new) {
int size = (map_width * map_depth);
if (p_new.size() != size) {
// fail
@@ -151,10 +151,10 @@ void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
}
// copy
- float *w = map_data.ptrw();
- const float *r = p_new.ptr();
+ real_t *w = map_data.ptrw();
+ const real_t *r = p_new.ptr();
for (int i = 0; i < size; i++) {
- float val = r[i];
+ real_t val = r[i];
w[i] = val;
if (i == 0) {
min_height = val;
@@ -174,7 +174,7 @@ void HeightMapShape3D::set_map_data(PackedFloat32Array p_new) {
notify_change_to_owners();
}
-PackedFloat32Array HeightMapShape3D::get_map_data() const {
+Vector<real_t> HeightMapShape3D::get_map_data() const {
return map_data;
}
@@ -194,7 +194,7 @@ void HeightMapShape3D::_bind_methods() {
HeightMapShape3D::HeightMapShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_HEIGHTMAP)) {
map_data.resize(map_width * map_depth);
- float *w = map_data.ptrw();
+ real_t *w = map_data.ptrw();
w[0] = 0.0;
w[1] = 0.0;
w[2] = 0.0;
diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h
index 1219791c56..1273aee040 100644
--- a/scene/resources/height_map_shape_3d.h
+++ b/scene/resources/height_map_shape_3d.h
@@ -38,7 +38,7 @@ class HeightMapShape3D : public Shape3D {
int map_width = 2;
int map_depth = 2;
- PackedFloat32Array map_data;
+ Vector<real_t> map_data;
real_t min_height = 0.0;
real_t max_height = 0.0;
@@ -51,8 +51,8 @@ public:
int get_map_width() const;
void set_map_depth(int p_new);
int get_map_depth() const;
- void set_map_data(PackedFloat32Array p_new);
- PackedFloat32Array get_map_data() const;
+ void set_map_data(Vector<real_t> p_new);
+ Vector<real_t> get_map_data() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 08f7274ff6..1d2a2ef26c 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -31,6 +31,7 @@
#include "material.h"
#include "core/config/engine.h"
+#include "core/version.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -268,7 +269,7 @@ void ShaderMaterial::_bind_methods() {
void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\"";
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
#else
const String quote_style = "\"";
#endif
@@ -279,7 +280,7 @@ void ShaderMaterial::get_argument_options(const StringName &p_function, int p_id
List<PropertyInfo> pl;
shader->get_param_list(&pl);
for (const PropertyInfo &E : pl) {
- r_options->push_back(quote_style + E.name.replace_first("shader_param/", "") + quote_style);
+ r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style));
}
}
}
@@ -469,7 +470,12 @@ void BaseMaterial3D::_update_shader() {
//must create a shader!
- String code = "shader_type spatial;\nrender_mode ";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = vformat(
+ "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s %s.\n\n",
+ orm ? "ORMMaterial3D" : "StandardMaterial3D");
+
+ code += "shader_type spatial;\nrender_mode ";
switch (blend_mode) {
case BLEND_MODE_MIX:
code += "blend_mix";
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 2f92872ad5..ad589a605e 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -653,6 +653,7 @@ enum OldArrayFormat {
};
+#ifndef DISABLE_DEPRECATED
static Array _convert_old_array(const Array &p_old) {
Array new_array;
new_array.resize(Mesh::ARRAY_MAX);
@@ -677,6 +678,7 @@ static Mesh::PrimitiveType _old_primitives[7] = {
Mesh::PRIMITIVE_TRIANGLE_STRIP,
Mesh::PRIMITIVE_TRIANGLE_STRIP
};
+#endif // DISABLE_DEPRECATED
void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) {
uint32_t dst_vertex_stride;
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 3fb4f8f211..04b2437ae8 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -107,9 +107,9 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
bo = arrays[Mesh::ARRAY_BONES].operator Vector<int>().ptr();
}
- const real_t *we = nullptr;
+ const float *we = nullptr;
if (arrays[Mesh::ARRAY_WEIGHTS].get_type() != Variant::NIL) {
- we = arrays[Mesh::ARRAY_WEIGHTS].operator Vector<real_t>().ptr();
+ we = arrays[Mesh::ARRAY_WEIGHTS].operator Vector<float>().ptr();
}
vertices.resize(vcount);
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index ddc50c0490..00cee9269b 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -64,22 +64,22 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
}
}
-void NavigationMesh::set_sample_partition_type(int p_value) {
- ERR_FAIL_COND(p_value >= SAMPLE_PARTITION_MAX);
- partition_type = static_cast<SamplePartitionType>(p_value);
+void NavigationMesh::set_sample_partition_type(SamplePartitionType p_value) {
+ ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX);
+ partition_type = p_value;
}
-int NavigationMesh::get_sample_partition_type() const {
- return static_cast<int>(partition_type);
+NavigationMesh::SamplePartitionType NavigationMesh::get_sample_partition_type() const {
+ return partition_type;
}
-void NavigationMesh::set_parsed_geometry_type(int p_value) {
- ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX);
- parsed_geometry_type = static_cast<ParsedGeometryType>(p_value);
+void NavigationMesh::set_parsed_geometry_type(ParsedGeometryType p_value) {
+ ERR_FAIL_INDEX(p_value, PARSED_GEOMETRY_MAX);
+ parsed_geometry_type = p_value;
notify_property_list_changed();
}
-int NavigationMesh::get_parsed_geometry_type() const {
+NavigationMesh::ParsedGeometryType NavigationMesh::get_parsed_geometry_type() const {
return parsed_geometry_type;
}
@@ -91,29 +91,31 @@ uint32_t NavigationMesh::get_collision_mask() const {
return collision_mask;
}
-void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) {
- ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive.");
+void NavigationMesh::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
uint32_t mask = get_collision_mask();
if (p_value) {
- mask |= 1 << p_bit;
+ mask |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ mask &= ~(1 << (p_layer_number - 1));
}
set_collision_mask(mask);
}
-bool NavigationMesh::get_collision_mask_bit(int p_bit) const {
- ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive.");
- return get_collision_mask() & (1 << p_bit);
+bool NavigationMesh::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
}
-void NavigationMesh::set_source_geometry_mode(int p_geometry_mode) {
+void NavigationMesh::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) {
ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX);
- source_geometry_mode = static_cast<SourceGeometryMode>(p_geometry_mode);
+ source_geometry_mode = p_geometry_mode;
notify_property_list_changed();
}
-int NavigationMesh::get_source_geometry_mode() const {
+NavigationMesh::SourceGeometryMode NavigationMesh::get_source_geometry_mode() const {
return source_geometry_mode;
}
@@ -126,6 +128,7 @@ StringName NavigationMesh::get_source_group_name() const {
}
void NavigationMesh::set_cell_size(float p_value) {
+ ERR_FAIL_COND(p_value <= 0);
cell_size = p_value;
}
@@ -134,6 +137,7 @@ float NavigationMesh::get_cell_size() const {
}
void NavigationMesh::set_cell_height(float p_value) {
+ ERR_FAIL_COND(p_value <= 0);
cell_height = p_value;
}
@@ -142,6 +146,7 @@ float NavigationMesh::get_cell_height() const {
}
void NavigationMesh::set_agent_height(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_height = p_value;
}
@@ -150,6 +155,7 @@ float NavigationMesh::get_agent_height() const {
}
void NavigationMesh::set_agent_radius(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_radius = p_value;
}
@@ -158,6 +164,7 @@ float NavigationMesh::get_agent_radius() {
}
void NavigationMesh::set_agent_max_climb(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
agent_max_climb = p_value;
}
@@ -166,6 +173,7 @@ float NavigationMesh::get_agent_max_climb() const {
}
void NavigationMesh::set_agent_max_slope(float p_value) {
+ ERR_FAIL_COND(p_value < 0 || p_value > 90);
agent_max_slope = p_value;
}
@@ -174,6 +182,7 @@ float NavigationMesh::get_agent_max_slope() const {
}
void NavigationMesh::set_region_min_size(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
region_min_size = p_value;
}
@@ -182,6 +191,7 @@ float NavigationMesh::get_region_min_size() const {
}
void NavigationMesh::set_region_merge_size(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
region_merge_size = p_value;
}
@@ -190,6 +200,7 @@ float NavigationMesh::get_region_merge_size() const {
}
void NavigationMesh::set_edge_max_length(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
edge_max_length = p_value;
}
@@ -198,6 +209,7 @@ float NavigationMesh::get_edge_max_length() const {
}
void NavigationMesh::set_edge_max_error(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
edge_max_error = p_value;
}
@@ -206,6 +218,7 @@ float NavigationMesh::get_edge_max_error() const {
}
void NavigationMesh::set_verts_per_poly(float p_value) {
+ ERR_FAIL_COND(p_value < 3);
verts_per_poly = p_value;
}
@@ -214,6 +227,7 @@ float NavigationMesh::get_verts_per_poly() const {
}
void NavigationMesh::set_detail_sample_distance(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
detail_sample_distance = p_value;
}
@@ -222,6 +236,7 @@ float NavigationMesh::get_detail_sample_distance() const {
}
void NavigationMesh::set_detail_sample_max_error(float p_value) {
+ ERR_FAIL_COND(p_value < 0);
detail_sample_max_error = p_value;
}
@@ -390,8 +405,8 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask);
- ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit);
- ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &NavigationMesh::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &NavigationMesh::get_collision_mask_value);
ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode);
ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode);
@@ -460,14 +475,6 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
- BIND_CONSTANT(SAMPLE_PARTITION_WATERSHED);
- BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE);
- BIND_CONSTANT(SAMPLE_PARTITION_LAYERS);
-
- BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
- BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
- BIND_CONSTANT(PARSED_GEOMETRY_BOTH);
-
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
@@ -494,6 +501,21 @@ void NavigationMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans");
+
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_WATERSHED);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MONOTONE);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_LAYERS);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX);
+
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
+ BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX);
+
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_NAVMESH_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT);
+ BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX);
}
void NavigationMesh::_validate_property(PropertyInfo &property) const {
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index 966221c7c6..1cdf7a07ed 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -109,20 +109,20 @@ protected:
public:
// Recast settings
- void set_sample_partition_type(int p_value);
- int get_sample_partition_type() const;
+ void set_sample_partition_type(SamplePartitionType p_value);
+ SamplePartitionType get_sample_partition_type() const;
- void set_parsed_geometry_type(int p_value);
- int get_parsed_geometry_type() const;
+ void set_parsed_geometry_type(ParsedGeometryType p_value);
+ ParsedGeometryType get_parsed_geometry_type() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
- void set_collision_mask_bit(int p_bit, bool p_value);
- bool get_collision_mask_bit(int p_bit) const;
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
- void set_source_geometry_mode(int p_geometry_mode);
- int get_source_geometry_mode() const;
+ void set_source_geometry_mode(SourceGeometryMode p_geometry_mode);
+ SourceGeometryMode get_source_geometry_mode() const;
void set_source_group_name(StringName p_group_name);
StringName get_source_group_name() const;
@@ -190,4 +190,8 @@ public:
NavigationMesh();
};
+VARIANT_ENUM_CAST(NavigationMesh::SamplePartitionType);
+VARIANT_ENUM_CAST(NavigationMesh::ParsedGeometryType);
+VARIANT_ENUM_CAST(NavigationMesh::SourceGeometryMode);
+
#endif // NAVIGATION_MESH_H
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index eddbb9a842..54bfc427c4 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -162,12 +162,14 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data());
if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) {
- if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
- obj = memnew(Node3D);
- } else if (Object::cast_to<Control>(ret_nodes[n.parent])) {
+ if (Object::cast_to<Control>(ret_nodes[n.parent])) {
obj = memnew(Control);
} else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) {
obj = memnew(Node2D);
+#ifndef _3D_DISABLED
+ } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) {
+ obj = memnew(Node3D);
+#endif // _3D_DISABLED
}
}
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 7da9cb96ee..0495a9e92c 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -30,6 +30,8 @@
#include "particles_material.h"
+#include "core/version.h"
+
Mutex ParticlesMaterial::material_mutex;
SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr;
Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map;
@@ -43,31 +45,31 @@ void ParticlesMaterial::init_shaders() {
shader_names->direction = "direction";
shader_names->spread = "spread";
shader_names->flatness = "flatness";
- shader_names->initial_linear_velocity = "initial_linear_velocity";
- shader_names->initial_angle = "initial_angle";
- shader_names->angular_velocity = "angular_velocity";
- shader_names->orbit_velocity = "orbit_velocity";
- shader_names->linear_accel = "linear_accel";
- shader_names->radial_accel = "radial_accel";
- shader_names->tangent_accel = "tangent_accel";
- shader_names->damping = "damping";
- shader_names->scale = "scale";
- shader_names->hue_variation = "hue_variation";
- shader_names->anim_speed = "anim_speed";
- shader_names->anim_offset = "anim_offset";
-
- shader_names->initial_linear_velocity_random = "initial_linear_velocity_random";
- shader_names->initial_angle_random = "initial_angle_random";
- shader_names->angular_velocity_random = "angular_velocity_random";
- shader_names->orbit_velocity_random = "orbit_velocity_random";
- shader_names->linear_accel_random = "linear_accel_random";
- shader_names->radial_accel_random = "radial_accel_random";
- shader_names->tangent_accel_random = "tangent_accel_random";
- shader_names->damping_random = "damping_random";
- shader_names->scale_random = "scale_random";
- shader_names->hue_variation_random = "hue_variation_random";
- shader_names->anim_speed_random = "anim_speed_random";
- shader_names->anim_offset_random = "anim_offset_random";
+ shader_names->initial_linear_velocity_min = "initial_linear_velocity_min";
+ shader_names->initial_angle_min = "initial_angle_min";
+ shader_names->angular_velocity_min = "angular_velocity_min";
+ shader_names->orbit_velocity_min = "orbit_velocity_min";
+ shader_names->linear_accel_min = "linear_accel_min";
+ shader_names->radial_accel_min = "radial_accel_min";
+ shader_names->tangent_accel_min = "tangent_accel_min";
+ shader_names->damping_min = "damping_min";
+ shader_names->scale_min = "scale_min";
+ shader_names->hue_variation_min = "hue_variation_min";
+ shader_names->anim_speed_min = "anim_speed_min";
+ shader_names->anim_offset_min = "anim_offset_min";
+
+ shader_names->initial_linear_velocity_max = "initial_linear_velocity_max";
+ shader_names->initial_angle_max = "initial_angle_max";
+ shader_names->angular_velocity_max = "angular_velocity_max";
+ shader_names->orbit_velocity_max = "orbit_velocity_max";
+ shader_names->linear_accel_max = "linear_accel_max";
+ shader_names->radial_accel_max = "radial_accel_max";
+ shader_names->tangent_accel_max = "tangent_accel_max";
+ shader_names->damping_max = "damping_max";
+ shader_names->scale_max = "scale_max";
+ shader_names->hue_variation_max = "hue_variation_max";
+ shader_names->anim_speed_max = "anim_speed_max";
+ shader_names->anim_offset_max = "anim_offset_max";
shader_names->angle_texture = "angle_texture";
shader_names->angular_velocity_texture = "angular_velocity_texture";
@@ -141,7 +143,10 @@ void ParticlesMaterial::_update_shader() {
//must create a shader!
- String code = "shader_type particles;\n";
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ String code = "// NOTE: Shader automatically converted from " VERSION_NAME " " VERSION_FULL_CONFIG "'s ParticlesMaterial.\n\n";
+
+ code += "shader_type particles;\n";
if (collision_scale) {
code += "render_mode collision_use_scale;\n";
@@ -150,31 +155,31 @@ void ParticlesMaterial::_update_shader() {
code += "uniform vec3 direction;\n";
code += "uniform float spread;\n";
code += "uniform float flatness;\n";
- code += "uniform float initial_linear_velocity;\n";
- code += "uniform float initial_angle;\n";
- code += "uniform float angular_velocity;\n";
- code += "uniform float orbit_velocity;\n";
- code += "uniform float linear_accel;\n";
- code += "uniform float radial_accel;\n";
- code += "uniform float tangent_accel;\n";
- code += "uniform float damping;\n";
- code += "uniform float scale;\n";
- code += "uniform float hue_variation;\n";
- code += "uniform float anim_speed;\n";
- code += "uniform float anim_offset;\n";
-
- code += "uniform float initial_linear_velocity_random;\n";
- code += "uniform float initial_angle_random;\n";
- code += "uniform float angular_velocity_random;\n";
- code += "uniform float orbit_velocity_random;\n";
- code += "uniform float linear_accel_random;\n";
- code += "uniform float radial_accel_random;\n";
- code += "uniform float tangent_accel_random;\n";
- code += "uniform float damping_random;\n";
- code += "uniform float scale_random;\n";
- code += "uniform float hue_variation_random;\n";
- code += "uniform float anim_speed_random;\n";
- code += "uniform float anim_offset_random;\n";
+ code += "uniform float initial_linear_velocity_min;\n";
+ code += "uniform float initial_angle_min;\n";
+ code += "uniform float angular_velocity_min;\n";
+ code += "uniform float orbit_velocity_min;\n";
+ code += "uniform float linear_accel_min;\n";
+ code += "uniform float radial_accel_min;\n";
+ code += "uniform float tangent_accel_min;\n";
+ code += "uniform float damping_min;\n";
+ code += "uniform float scale_min;\n";
+ code += "uniform float hue_variation_min;\n";
+ code += "uniform float anim_speed_min;\n";
+ code += "uniform float anim_offset_min;\n";
+
+ code += "uniform float initial_linear_velocity_max;\n";
+ code += "uniform float initial_angle_max;\n";
+ code += "uniform float angular_velocity_max;\n";
+ code += "uniform float orbit_velocity_max;\n";
+ code += "uniform float linear_accel_max;\n";
+ code += "uniform float radial_accel_max;\n";
+ code += "uniform float tangent_accel_max;\n";
+ code += "uniform float damping_max;\n";
+ code += "uniform float scale_max;\n";
+ code += "uniform float hue_variation_max;\n";
+ code += "uniform float anim_speed_max;\n";
+ code += "uniform float anim_offset_max;\n";
code += "uniform float lifetime_randomness;\n";
switch (emission_shape) {
@@ -324,7 +329,7 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 1.0;\n";
}
code += " float spread_rad = spread * degree_to_rad;\n";
@@ -334,42 +339,46 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 1.0;\n";
}
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
- code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
- code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " {\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " angle1_rad += direction.x != 0.0 ? atan(direction.y, direction.x) : sign(direction.y) * (pi / 2.0);\n";
+ code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n";
+ code += " VELOCITY = rot * mix(initial_linear_velocity_min,initial_linear_velocity_max, rand_from_seed(alt_seed));\n";
+ code += " }\n";
} else {
//initiate velocity spread in 3D
- code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
- code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
- code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
- code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
- code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
- code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
- code += " vec3 direction_nrm = normalize(direction);\n";
- code += " // rotate spread to direction\n";
- code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n";
- code += " if (length(binormal) < 0.0001) {\n";
- code += " // direction is parallel to Y. Choose Z as the binormal.\n";
- code += " binormal = vec3(0.0, 0.0, 1.0);\n";
+ code += " {\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n";
+ code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n";
+ code += " vec3 direction_xz = vec3(sin(angle1_rad), 0.0, cos(angle1_rad));\n";
+ code += " vec3 direction_yz = vec3(0.0, sin(angle2_rad), cos(angle2_rad));\n";
+ code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n";
+ code += " vec3 spread_direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " vec3 direction_nrm = length(direction) > 0.0 ? normalize(direction) : vec3(0.0, 0.0, 1.0);\n";
+ code += " // rotate spread to direction\n";
+ code += " vec3 binormal = cross(vec3(0.0, 1.0, 0.0), direction_nrm);\n";
+ code += " if (length(binormal) < 0.0001) {\n";
+ code += " // direction is parallel to Y. Choose Z as the binormal.\n";
+ code += " binormal = vec3(0.0, 0.0, 1.0);\n";
+ code += " }\n";
+ code += " binormal = normalize(binormal);\n";
+ code += " vec3 normal = cross(binormal, direction_nrm);\n";
+ code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n";
+ code += " VELOCITY = spread_direction * mix(initial_linear_velocity_min, initial_linear_velocity_max,rand_from_seed(alt_seed));\n";
code += " }\n";
- code += " binormal = normalize(binormal);\n";
- code += " vec3 normal = cross(binormal, direction_nrm);\n";
- code += " spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z;\n";
- code += " VELOCITY = spread_direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
}
code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
+ code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, angle_rand);\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
code += " CUSTOM.y = 0.0;\n"; // phase
code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1)
+ code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n"; // animation offset (0-1)
code += " if (RESTART_POSITION) {\n";
@@ -393,16 +402,20 @@ void ParticlesMaterial::_update_shader() {
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " mat2 rotm;";
- code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
- code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
- code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ code += " {\n";
+ code += " mat2 rotm;";
+ code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n";
+ code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n";
+ code += " if (RESTART_VELOCITY) VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ code += " }\n";
} else {
- code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " {\n";
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " if (RESTART_VELOCITY) VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n";
+ code += " }\n";
}
}
} break;
@@ -458,63 +471,63 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_velocity = 0.0;\n";
+ code += " float tex_linear_velocity = 1.0;\n";
}
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_orbit_velocity = 0.0;\n";
+ code += " float tex_orbit_velocity = 1.0;\n";
}
}
if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angular_velocity = 0.0;\n";
+ code += " float tex_angular_velocity = 1.0;\n";
}
if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) {
code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_linear_accel = 0.0;\n";
+ code += " float tex_linear_accel = 1.0;\n";
}
if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) {
code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_radial_accel = 0.0;\n";
+ code += " float tex_radial_accel = 1.0;\n";
}
if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) {
code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_tangent_accel = 0.0;\n";
+ code += " float tex_tangent_accel = 1.0;\n";
}
if (tex_parameters[PARAM_DAMPING].is_valid()) {
code += " float tex_damping = textureLod(damping_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_damping = 0.0;\n";
+ code += " float tex_damping = 1.0;\n";
}
if (tex_parameters[PARAM_ANGLE].is_valid()) {
code += " float tex_angle = textureLod(angle_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_angle = 0.0;\n";
+ code += " float tex_angle = 1.0;\n";
}
if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) {
code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_speed = 0.0;\n";
+ code += " float tex_anim_speed = 1.0;\n";
}
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) {
code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_anim_offset = 0.0;\n";
+ code += " float tex_anim_offset = 1.0;\n";
}
code += " vec3 force = gravity;\n";
@@ -523,18 +536,19 @@ void ParticlesMaterial::_update_shader() {
code += " pos.z = 0.0;\n";
}
code += " // apply linear acceleration\n";
- code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n";
+ code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
code += " // apply radial acceleration\n";
code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
code += " vec3 diff = pos - org;\n";
- code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n";
+ code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n";
code += " // apply tangential acceleration;\n";
+ code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed))\n;";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
+ code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * tangent_accel_val : vec3(0.0);\n";
} else {
code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
- code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
+ code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * tangent_accel_val : vec3(0.0);\n";
}
if (attractor_interaction_enabled) {
code += " force += ATTRACTOR_FORCE;\n\n";
@@ -544,7 +558,7 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY += force * DELTA;\n";
code += " // orbit velocity\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
- code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n";
+ code += " float orbit_amount = tex_orbit_velocity * mix(orbit_velocity_min, orbit_velocity_max, rand_from_seed(alt_seed));\n";
code += " if (orbit_amount != 0.0) {\n";
code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n";
@@ -556,9 +570,10 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n";
}
- code += " if (damping + tex_damping > 0.0) {\n";
+ code += " float dmp = mix(damping_min, damping_max, rand_from_seed(alt_seed));\n";
+ code += " if (dmp * tex_damping > 0.0) {\n";
code += " float v = length(VELOCITY);\n";
- code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n";
+ code += " float damp = tex_damping * dmp;\n";
code += " v -= damp * DELTA;\n";
code += " if (v < 0.0) {\n";
code += " VELOCITY = vec3(0.0);\n";
@@ -566,26 +581,26 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY = normalize(VELOCITY) * v;\n";
code += " }\n";
code += " }\n";
- code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n";
- code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n";
+ code += " float base_angle = (tex_angle) * mix(initial_angle_min, initial_angle_max, rand_from_seed(alt_seed));\n";
+ code += " base_angle += CUSTOM.y * LIFETIME * (tex_angular_velocity) * mix(angular_velocity_min,angular_velocity_max, rand_from_seed(alt_seed));\n";
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
- code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle
+ code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, rand_from_seed(alt_seed)) + CUSTOM.y * tex_anim_speed * mix(anim_speed_min, anim_speed_max, rand_from_seed(alt_seed));\n"; // angle
// apply color
// apply hue rotation
if (tex_parameters[PARAM_SCALE].is_valid()) {
- code += " float tex_scale = textureLod(scale_texture, vec2(tv, 0.0), 0.0).r;\n";
+ code += " vec3 tex_scale = textureLod(scale_texture, vec2(tv, 0.0), 0.0).rgb;\n";
} else {
- code += " float tex_scale = 1.0;\n";
+ code += " vec3 tex_scale = vec3(1.0);\n";
}
if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) {
code += " float tex_hue_variation = textureLod(hue_variation_texture, vec2(tv, 0.0), 0.0).r;\n";
} else {
- code += " float tex_hue_variation = 0.0;\n";
+ code += " float tex_hue_variation = 1.0;\n";
}
- code += " float hue_rot_angle = (hue_variation + tex_hue_variation) * pi * 2.0 * mix(1.0, hue_rot_rand * 2.0 - 1.0, hue_variation_random);\n";
+ code += " float hue_rot_angle = (tex_hue_variation) * pi * 2.0 * mix(hue_variation_min, hue_variation_max, rand_from_seed(alt_seed));\n";
code += " float hue_rot_c = cos(hue_rot_angle);\n";
code += " float hue_rot_s = sin(hue_rot_angle);\n";
code += " mat4 hue_rot_mat = mat4(vec4(0.299, 0.587, 0.114, 0.0),\n";
@@ -647,18 +662,18 @@ void ParticlesMaterial::_update_shader() {
}
// turn particle by rotation in Y
if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) {
+ code += " vec4 origin = TRANSFORM[3];\n";
code += " TRANSFORM = mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += " TRANSFORM[3] = origin;\n";
}
}
//scale by scale
- code += " float base_scale = tex_scale * mix(scale, 1.0, scale_random * scale_rand);\n";
- code += " if (base_scale < 0.000001) {\n";
- code += " base_scale = 0.000001;\n";
- code += " }\n";
+ code += " float base_scale = mix(scale_min, scale_max, scale_rand);\n";
+ code += " base_scale = sign(base_scale) * max(abs(base_scale), 0.001);\n";
- code += " TRANSFORM[0].xyz *= base_scale;\n";
- code += " TRANSFORM[1].xyz *= base_scale;\n";
- code += " TRANSFORM[2].xyz *= base_scale;\n";
+ code += " TRANSFORM[0].xyz *= base_scale * sign(tex_scale.r) * max(abs(tex_scale.r), 0.001);\n";
+ code += " TRANSFORM[1].xyz *= base_scale * sign(tex_scale.g) * max(abs(tex_scale.g), 0.001);\n";
+ code += " TRANSFORM[2].xyz *= base_scale * sign(tex_scale.b) * max(abs(tex_scale.b), 0.001);\n";
if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
@@ -764,110 +779,116 @@ float ParticlesMaterial::get_flatness() const {
return flatness;
}
-void ParticlesMaterial::set_param(Parameter p_param, float p_value) {
+void ParticlesMaterial::set_param_min(Parameter p_param, float p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
- parameters[p_param] = p_value;
+ params_min[p_param] = p_value;
+ if (params_min[p_param] > params_max[p_param]) {
+ set_param_max(p_param, p_value);
+ }
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_min, p_value);
} break;
case PARAM_ANGULAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_min, p_value);
} break;
case PARAM_ORBIT_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_min, p_value);
} break;
case PARAM_LINEAR_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_min, p_value);
} break;
case PARAM_RADIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_min, p_value);
} break;
case PARAM_TANGENTIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_min, p_value);
} break;
case PARAM_DAMPING: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_min, p_value);
} break;
case PARAM_ANGLE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_min, p_value);
} break;
case PARAM_SCALE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_min, p_value);
} break;
case PARAM_HUE_VARIATION: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_min, p_value);
} break;
case PARAM_ANIM_SPEED: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_min, p_value);
} break;
case PARAM_ANIM_OFFSET: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_min, p_value);
} break;
case PARAM_MAX:
break; // Can't happen, but silences warning
}
}
-float ParticlesMaterial::get_param(Parameter p_param) const {
+float ParticlesMaterial::get_param_min(Parameter p_param) const {
ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return parameters[p_param];
+ return params_min[p_param];
}
-void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) {
+void ParticlesMaterial::set_param_max(Parameter p_param, float p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
- randomness[p_param] = p_value;
+ params_max[p_param] = p_value;
+ if (params_min[p_param] > params_max[p_param]) {
+ set_param_min(p_param, p_value);
+ }
switch (p_param) {
case PARAM_INITIAL_LINEAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_max, p_value);
} break;
case PARAM_ANGULAR_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_max, p_value);
} break;
case PARAM_ORBIT_VELOCITY: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_max, p_value);
} break;
case PARAM_LINEAR_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_max, p_value);
} break;
case PARAM_RADIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_max, p_value);
} break;
case PARAM_TANGENTIAL_ACCEL: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_max, p_value);
} break;
case PARAM_DAMPING: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_max, p_value);
} break;
case PARAM_ANGLE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_max, p_value);
} break;
case PARAM_SCALE: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_max, p_value);
} break;
case PARAM_HUE_VARIATION: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_max, p_value);
} break;
case PARAM_ANIM_SPEED: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_max, p_value);
} break;
case PARAM_ANIM_OFFSET: {
- RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value);
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_max, p_value);
} break;
case PARAM_MAX:
break; // Can't happen, but silences warning
}
}
-float ParticlesMaterial::get_param_randomness(Parameter p_param) const {
+float ParticlesMaterial::get_param_max(Parameter p_param) const {
ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return randomness[p_param];
+ return params_max[p_param];
}
static void _adjust_curve_range(const Ref<Texture2D> &p_texture, float p_min, float p_max) {
@@ -988,7 +1009,7 @@ void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) {
_queue_shader_change();
}
-void ParticlesMaterial::set_emission_sphere_radius(float p_radius) {
+void ParticlesMaterial::set_emission_sphere_radius(real_t p_radius) {
emission_sphere_radius = p_radius;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius);
}
@@ -1027,17 +1048,17 @@ void ParticlesMaterial::set_emission_ring_axis(Vector3 p_axis) {
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_axis, p_axis);
}
-void ParticlesMaterial::set_emission_ring_height(float p_height) {
+void ParticlesMaterial::set_emission_ring_height(real_t p_height) {
emission_ring_height = p_height;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_height, p_height);
}
-void ParticlesMaterial::set_emission_ring_radius(float p_radius) {
+void ParticlesMaterial::set_emission_ring_radius(real_t p_radius) {
emission_ring_radius = p_radius;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_radius, p_radius);
}
-void ParticlesMaterial::set_emission_ring_inner_radius(float p_radius) {
+void ParticlesMaterial::set_emission_ring_inner_radius(real_t p_radius) {
emission_ring_inner_radius = p_radius;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_radius);
}
@@ -1046,7 +1067,7 @@ ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const {
return emission_shape;
}
-float ParticlesMaterial::get_emission_sphere_radius() const {
+real_t ParticlesMaterial::get_emission_sphere_radius() const {
return emission_sphere_radius;
}
@@ -1074,15 +1095,15 @@ Vector3 ParticlesMaterial::get_emission_ring_axis() const {
return emission_ring_axis;
}
-float ParticlesMaterial::get_emission_ring_height() const {
+real_t ParticlesMaterial::get_emission_ring_height() const {
return emission_ring_height;
}
-float ParticlesMaterial::get_emission_ring_radius() const {
+real_t ParticlesMaterial::get_emission_ring_radius() const {
return emission_ring_radius;
}
-float ParticlesMaterial::get_emission_ring_inner_radius() const {
+real_t ParticlesMaterial::get_emission_ring_inner_radius() const {
return emission_ring_inner_radius;
}
@@ -1099,12 +1120,12 @@ Vector3 ParticlesMaterial::get_gravity() const {
return gravity;
}
-void ParticlesMaterial::set_lifetime_randomness(float p_lifetime) {
+void ParticlesMaterial::set_lifetime_randomness(double p_lifetime) {
lifetime_randomness = p_lifetime;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->lifetime_randomness, lifetime_randomness);
}
-float ParticlesMaterial::get_lifetime_randomness() const {
+double ParticlesMaterial::get_lifetime_randomness() const {
return lifetime_randomness;
}
@@ -1161,11 +1182,12 @@ ParticlesMaterial::SubEmitterMode ParticlesMaterial::get_sub_emitter_mode() cons
return sub_emitter_mode;
}
-void ParticlesMaterial::set_sub_emitter_frequency(float p_frequency) {
+void ParticlesMaterial::set_sub_emitter_frequency(double p_frequency) {
sub_emitter_frequency = p_frequency;
RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->sub_emitter_frequency, 1.0 / p_frequency); //pass delta instead of frequency, since its easier to compute
}
-float ParticlesMaterial::get_sub_emitter_frequency() const {
+
+double ParticlesMaterial::get_sub_emitter_frequency() const {
return sub_emitter_frequency;
}
@@ -1245,11 +1267,11 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness);
ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness);
- ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticlesMaterial::set_param);
- ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticlesMaterial::get_param);
+ ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticlesMaterial::set_param_min);
+ ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticlesMaterial::get_param_min);
- ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &ParticlesMaterial::set_param_randomness);
- ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &ParticlesMaterial::get_param_randomness);
+ ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &ParticlesMaterial::set_param_max);
+ ClassDB::bind_method(D_METHOD("get_param_max", "param"), &ParticlesMaterial::get_param_max);
ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture);
ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture);
@@ -1355,54 +1377,54 @@ void ParticlesMaterial::_bind_methods() {
ADD_GROUP("Gravity", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
ADD_GROUP("Initial Velocity", "initial_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY);
ADD_GROUP("Orbit Velocity", "orbit_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY);
ADD_GROUP("Linear Accel", "linear_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL);
ADD_GROUP("Radial Accel", "radial_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL);
ADD_GROUP("Tangential Accel", "tangential_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL);
ADD_GROUP("Damping", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING);
ADD_GROUP("Angle", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param", "get_param", PARAM_ANGLE);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE);
ADD_GROUP("Scale", "");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE);
- ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
ADD_GROUP("Sub Emitter", "sub_emitter_");
@@ -1458,18 +1480,30 @@ ParticlesMaterial::ParticlesMaterial() :
set_direction(Vector3(1, 0, 0));
set_spread(45);
set_flatness(0);
- set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0);
- set_param(PARAM_ANGULAR_VELOCITY, 0);
- set_param(PARAM_ORBIT_VELOCITY, 0);
- set_param(PARAM_LINEAR_ACCEL, 0);
- set_param(PARAM_RADIAL_ACCEL, 0);
- set_param(PARAM_TANGENTIAL_ACCEL, 0);
- set_param(PARAM_DAMPING, 0);
- set_param(PARAM_ANGLE, 0);
- set_param(PARAM_SCALE, 1);
- set_param(PARAM_HUE_VARIATION, 0);
- set_param(PARAM_ANIM_SPEED, 0);
- set_param(PARAM_ANIM_OFFSET, 0);
+ set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
+ set_param_min(PARAM_ANGULAR_VELOCITY, 0);
+ set_param_min(PARAM_ORBIT_VELOCITY, 0);
+ set_param_min(PARAM_LINEAR_ACCEL, 0);
+ set_param_min(PARAM_RADIAL_ACCEL, 0);
+ set_param_min(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param_min(PARAM_DAMPING, 0);
+ set_param_min(PARAM_ANGLE, 0);
+ set_param_min(PARAM_SCALE, 1);
+ set_param_min(PARAM_HUE_VARIATION, 0);
+ set_param_min(PARAM_ANIM_SPEED, 0);
+ set_param_min(PARAM_ANIM_OFFSET, 0);
+ set_param_max(PARAM_INITIAL_LINEAR_VELOCITY, 0);
+ set_param_max(PARAM_ANGULAR_VELOCITY, 0);
+ set_param_max(PARAM_ORBIT_VELOCITY, 0);
+ set_param_max(PARAM_LINEAR_ACCEL, 0);
+ set_param_max(PARAM_RADIAL_ACCEL, 0);
+ set_param_max(PARAM_TANGENTIAL_ACCEL, 0);
+ set_param_max(PARAM_DAMPING, 0);
+ set_param_max(PARAM_ANGLE, 0);
+ set_param_max(PARAM_SCALE, 1);
+ set_param_max(PARAM_HUE_VARIATION, 0);
+ set_param_max(PARAM_ANIM_SPEED, 0);
+ set_param_max(PARAM_ANIM_OFFSET, 0);
set_emission_shape(EMISSION_SHAPE_POINT);
set_emission_sphere_radius(1);
set_emission_box_extents(Vector3(1, 1, 1));
@@ -1491,10 +1525,6 @@ ParticlesMaterial::ParticlesMaterial() :
set_collision_friction(0.0);
set_collision_use_scale(false);
- for (int i = 0; i < PARAM_MAX; i++) {
- set_param_randomness(Parameter(i), 0);
- }
-
for (int i = 0; i < PARTICLE_FLAG_MAX; i++) {
particle_flags[i] = false;
}
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 8b0b26a3d1..8ab26aff77 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -61,6 +61,7 @@ public:
PARAM_MAX
};
+ // When extending, make sure not to overflow the size of the MaterialKey below.
enum ParticleFlags {
PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY,
PARTICLE_FLAG_ROTATE_Y,
@@ -68,6 +69,7 @@ public:
PARTICLE_FLAG_MAX
};
+ // When extending, make sure not to overflow the size of the MaterialKey below.
enum EmissionShape {
EMISSION_SHAPE_POINT,
EMISSION_SHAPE_SPHERE,
@@ -78,6 +80,7 @@ public:
EMISSION_SHAPE_MAX
};
+ // When extending, make sure not to overflow the size of the MaterialKey below.
enum SubEmitterMode {
SUB_EMITTER_DISABLED,
SUB_EMITTER_CONSTANT,
@@ -88,11 +91,13 @@ public:
private:
union MaterialKey {
+ // The bit size of the struct must be kept below or equal to 32 bits.
+ // Consider this when extending ParticleFlags, EmissionShape, or SubEmitterMode.
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
uint32_t particle_flags : 4;
- uint32_t emission_shape : 2;
+ uint32_t emission_shape : 3;
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
uint32_t sub_emitter : 2;
@@ -149,31 +154,31 @@ private:
StringName direction;
StringName spread;
StringName flatness;
- StringName initial_linear_velocity;
- StringName initial_angle;
- StringName angular_velocity;
- StringName orbit_velocity;
- StringName linear_accel;
- StringName radial_accel;
- StringName tangent_accel;
- StringName damping;
- StringName scale;
- StringName hue_variation;
- StringName anim_speed;
- StringName anim_offset;
-
- StringName initial_linear_velocity_random;
- StringName initial_angle_random;
- StringName angular_velocity_random;
- StringName orbit_velocity_random;
- StringName linear_accel_random;
- StringName radial_accel_random;
- StringName tangent_accel_random;
- StringName damping_random;
- StringName scale_random;
- StringName hue_variation_random;
- StringName anim_speed_random;
- StringName anim_offset_random;
+ StringName initial_linear_velocity_min;
+ StringName initial_angle_min;
+ StringName angular_velocity_min;
+ StringName orbit_velocity_min;
+ StringName linear_accel_min;
+ StringName radial_accel_min;
+ StringName tangent_accel_min;
+ StringName damping_min;
+ StringName scale_min;
+ StringName hue_variation_min;
+ StringName anim_speed_min;
+ StringName anim_offset_min;
+
+ StringName initial_linear_velocity_max;
+ StringName initial_angle_max;
+ StringName angular_velocity_max;
+ StringName orbit_velocity_max;
+ StringName linear_accel_max;
+ StringName radial_accel_max;
+ StringName tangent_accel_max;
+ StringName damping_max;
+ StringName scale_max;
+ StringName hue_variation_max;
+ StringName anim_speed_max;
+ StringName anim_offset_max;
StringName angle_texture;
StringName angular_velocity_texture;
@@ -225,8 +230,8 @@ private:
float spread;
float flatness;
- float parameters[PARAM_MAX];
- float randomness[PARAM_MAX];
+ float params_min[PARAM_MAX];
+ float params_max[PARAM_MAX];
Ref<Texture2D> tex_parameters[PARAM_MAX];
Color color;
@@ -241,19 +246,19 @@ private:
Ref<Texture2D> emission_normal_texture;
Ref<Texture2D> emission_color_texture;
Vector3 emission_ring_axis;
- float emission_ring_height;
- float emission_ring_radius;
- float emission_ring_inner_radius;
+ real_t emission_ring_height;
+ real_t emission_ring_radius;
+ real_t emission_ring_inner_radius;
int emission_point_count = 1;
bool anim_loop;
Vector3 gravity;
- float lifetime_randomness;
+ double lifetime_randomness;
SubEmitterMode sub_emitter_mode;
- float sub_emitter_frequency;
+ double sub_emitter_frequency;
int sub_emitter_amount_at_end;
bool sub_emitter_keep_velocity;
//do not save emission points here
@@ -278,11 +283,11 @@ public:
void set_flatness(float p_flatness);
float get_flatness() const;
- void set_param(Parameter p_param, float p_value);
- float get_param(Parameter p_param) const;
+ void set_param_min(Parameter p_param, float p_value);
+ float get_param_min(Parameter p_param) const;
- void set_param_randomness(Parameter p_param, float p_value);
- float get_param_randomness(Parameter p_param) const;
+ void set_param_max(Parameter p_param, float p_value);
+ float get_param_max(Parameter p_param) const;
void set_param_texture(Parameter p_param, const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_param_texture(Parameter p_param) const;
@@ -297,34 +302,34 @@ public:
bool get_particle_flag(ParticleFlags p_particle_flag) const;
void set_emission_shape(EmissionShape p_shape);
- void set_emission_sphere_radius(float p_radius);
+ void set_emission_sphere_radius(real_t p_radius);
void set_emission_box_extents(Vector3 p_extents);
void set_emission_point_texture(const Ref<Texture2D> &p_points);
void set_emission_normal_texture(const Ref<Texture2D> &p_normals);
void set_emission_color_texture(const Ref<Texture2D> &p_colors);
void set_emission_ring_axis(Vector3 p_axis);
- void set_emission_ring_height(float p_height);
- void set_emission_ring_radius(float p_radius);
- void set_emission_ring_inner_radius(float p_radius);
+ void set_emission_ring_height(real_t p_height);
+ void set_emission_ring_radius(real_t p_radius);
+ void set_emission_ring_inner_radius(real_t p_radius);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
- float get_emission_sphere_radius() const;
+ real_t get_emission_sphere_radius() const;
Vector3 get_emission_box_extents() const;
Ref<Texture2D> get_emission_point_texture() const;
Ref<Texture2D> get_emission_normal_texture() const;
Ref<Texture2D> get_emission_color_texture() const;
Vector3 get_emission_ring_axis() const;
- float get_emission_ring_height() const;
- float get_emission_ring_radius() const;
- float get_emission_ring_inner_radius() const;
+ real_t get_emission_ring_height() const;
+ real_t get_emission_ring_radius() const;
+ real_t get_emission_ring_inner_radius() const;
int get_emission_point_count() const;
void set_gravity(const Vector3 &p_gravity);
Vector3 get_gravity() const;
- void set_lifetime_randomness(float p_lifetime);
- float get_lifetime_randomness() const;
+ void set_lifetime_randomness(double p_lifetime);
+ double get_lifetime_randomness() const;
void set_attractor_interaction_enabled(bool p_enable);
bool is_attractor_interaction_enabled() const;
@@ -348,8 +353,8 @@ public:
void set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode);
SubEmitterMode get_sub_emitter_mode() const;
- void set_sub_emitter_frequency(float p_frequency);
- float get_sub_emitter_frequency() const;
+ void set_sub_emitter_frequency(double p_frequency);
+ double get_sub_emitter_frequency() const;
void set_sub_emitter_amount_at_end(int p_amount);
int get_sub_emitter_amount_at_end() const;
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index a08684a506..4dd3c874cb 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -417,9 +417,9 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) {
}
if (p_data.has("penalties")) {
- Vector<float> penalties = p_data["penalties"];
+ Vector<real_t> penalties = p_data["penalties"];
if (penalties.size() == pc) {
- const float *pr2 = penalties.ptr();
+ const real_t *pr2 = penalties.ptr();
for (int i = 0; i < pc; i++) {
points.write[i].penalty = pr2[i];
}
@@ -445,11 +445,11 @@ Dictionary PolygonPathFinder::_get_data() const {
p.resize(MAX(0, points.size() - 2));
connections.resize(MAX(0, points.size() - 2));
ind.resize(edges.size() * 2);
- Vector<float> penalties;
+ Vector<real_t> penalties;
penalties.resize(MAX(0, points.size() - 2));
{
Vector2 *wp = p.ptrw();
- float *pw = penalties.ptrw();
+ real_t *pw = penalties.ptrw();
for (int i = 0; i < points.size() - 2; i++) {
wp[i] = points[i].pos;
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index dfa45cc810..ba85ea4a6c 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -300,7 +300,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
z = cos(u * Math_TAU);
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
- points.push_back(p + Vector3(0.0, 0.5 * mid_height, 0.0));
+ points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0));
normals.push_back(p.normalized());
ADD_TANGENT(z, 0.0, x, 1.0)
uvs.push_back(Vector2(u, v * onethird));
@@ -328,8 +328,8 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
v = j;
v /= (rings + 1);
- y = mid_height * v;
- y = (mid_height * 0.5) - y;
+ y = (height - 2.0 * radius) * v;
+ y = (0.5 * height - radius) - y;
for (i = 0; i <= radial_segments; i++) {
u = i;
@@ -379,7 +379,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
z = cos(u2 * Math_TAU);
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
- points.push_back(p + Vector3(0.0, -0.5 * mid_height, 0.0));
+ points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p.normalized());
ADD_TANGENT(z, 0.0, x, 1.0)
uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird)));
@@ -410,8 +410,8 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
void CapsuleMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CapsuleMesh::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &CapsuleMesh::get_radius);
- ClassDB::bind_method(D_METHOD("set_mid_height", "mid_height"), &CapsuleMesh::set_mid_height);
- ClassDB::bind_method(D_METHOD("get_mid_height"), &CapsuleMesh::get_mid_height);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleMesh::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CapsuleMesh::get_height);
ClassDB::bind_method(D_METHOD("set_radial_segments", "segments"), &CapsuleMesh::set_radial_segments);
ClassDB::bind_method(D_METHOD("get_radial_segments"), &CapsuleMesh::get_radial_segments);
@@ -419,13 +419,16 @@ void CapsuleMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mid_height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_mid_height", "get_mid_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1,or_greater"), "set_rings", "get_rings");
}
void CapsuleMesh::set_radius(const float p_radius) {
radius = p_radius;
+ if (radius > height * 0.5) {
+ radius = height * 0.5;
+ }
_request_update();
}
@@ -433,13 +436,16 @@ float CapsuleMesh::get_radius() const {
return radius;
}
-void CapsuleMesh::set_mid_height(const float p_mid_height) {
- mid_height = p_mid_height;
+void CapsuleMesh::set_height(const float p_height) {
+ height = p_height;
+ if (radius > height * 0.5) {
+ height = radius * 2;
+ }
_request_update();
}
-float CapsuleMesh::get_mid_height() const {
- return mid_height;
+float CapsuleMesh::get_height() const {
+ return height;
}
void CapsuleMesh::set_radial_segments(const int p_segments) {
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index a3de34d3e3..7915cb0028 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -108,7 +108,7 @@ class CapsuleMesh : public PrimitiveMesh {
private:
float radius = 1.0;
- float mid_height = 1.0;
+ float height = 3.0;
int radial_segments = 64;
int rings = 8;
@@ -120,8 +120,8 @@ public:
void set_radius(const float p_radius);
float get_radius() const;
- void set_mid_height(const float p_mid_height);
- float get_mid_height() const;
+ void set_height(const float p_height);
+ float get_height() const;
void set_radial_segments(const int p_segments);
int get_radial_segments() const;
diff --git a/scene/resources/ray_shape_2d.cpp b/scene/resources/ray_shape_2d.cpp
deleted file mode 100644
index fb8f4b9985..0000000000
--- a/scene/resources/ray_shape_2d.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*************************************************************************/
-/* ray_shape_2d.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "ray_shape_2d.h"
-
-#include "servers/physics_server_2d.h"
-#include "servers/rendering_server.h"
-
-void RayShape2D::_update_shape() {
- Dictionary d;
- d["length"] = length;
- d["slips_on_slope"] = slips_on_slope;
- PhysicsServer2D::get_singleton()->shape_set_data(get_rid(), d);
- emit_changed();
-}
-
-void RayShape2D::draw(const RID &p_to_rid, const Color &p_color) {
- const Vector2 target_position = Vector2(0, get_length());
-
- const float max_arrow_size = 6;
- const float line_width = 1.4;
- bool no_line = target_position.length() < line_width;
- float arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size);
-
- if (no_line) {
- arrow_size = target_position.length();
- } else {
- RS::get_singleton()->canvas_item_add_line(p_to_rid, Vector2(), target_position - target_position.normalized() * arrow_size, p_color, line_width);
- }
-
- Transform2D xf;
- xf.rotate(target_position.angle());
- xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0));
-
- Vector<Vector2> pts;
- pts.push_back(xf.xform(Vector2(arrow_size, 0)));
- pts.push_back(xf.xform(Vector2(0, 0.5 * arrow_size)));
- pts.push_back(xf.xform(Vector2(0, -0.5 * arrow_size)));
-
- Vector<Color> cols;
- for (int i = 0; i < 3; i++) {
- cols.push_back(p_color);
- }
-
- RS::get_singleton()->canvas_item_add_primitive(p_to_rid, pts, cols, Vector<Point2>(), RID());
-}
-
-Rect2 RayShape2D::get_rect() const {
- Rect2 rect;
- rect.position = Vector2();
- rect.expand_to(Vector2(0, length));
- rect = rect.grow(Math_SQRT12 * 4);
- return rect;
-}
-
-real_t RayShape2D::get_enclosing_radius() const {
- return length;
-}
-
-void RayShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length);
- ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length);
-
- ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope);
- ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope);
-
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
-}
-
-void RayShape2D::set_length(real_t p_length) {
- length = p_length;
- _update_shape();
-}
-
-real_t RayShape2D::get_length() const {
- return length;
-}
-
-void RayShape2D::set_slips_on_slope(bool p_active) {
- slips_on_slope = p_active;
- _update_shape();
-}
-
-bool RayShape2D::get_slips_on_slope() const {
- return slips_on_slope;
-}
-
-RayShape2D::RayShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->ray_shape_create()) {
- _update_shape();
-}
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 2b93414906..dbe118a262 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -558,6 +558,12 @@ Error ResourceLoaderText::load() {
resource_current++;
+ int_resources[id] = res; //always assign int resources
+ if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ res->set_scene_unique_id(id);
+ }
+
while (true) {
String assign;
Variant value;
@@ -585,12 +591,6 @@ Error ResourceLoaderText::load() {
}
}
- int_resources[id] = res; //always assign int resources
- if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
- res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
- res->set_scene_unique_id(id);
- }
-
if (progress && resources_total > 0) {
*progress = resource_current / float(resources_total);
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 424a54f344..44d524f142 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -76,8 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
if (default_textures.has(pi.name)) { //do not show default textures
continue;
}
+ String original_name = pi.name;
pi.name = "shader_param/" + pi.name;
- params_cache[pi.name] = pi.name;
+ params_cache[pi.name] = original_name;
if (p_params) {
//small little hack
if (pi.type == Variant::RID) {
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index b52a60006a..e533fb054a 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -44,7 +44,7 @@
///////////////////////////////////////
void SkeletonModification2D::_execute(float p_delta) {
- call("_execute", p_delta);
+ GDVIRTUAL_CALL(_execute, p_delta);
if (!enabled) {
return;
@@ -59,11 +59,11 @@ void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_
WARN_PRINT("Could not setup modification with name " + get_name());
}
- call("_setup_modification", p_stack);
+ GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack2D>(p_stack));
}
void SkeletonModification2D::_draw_editor_gizmo() {
- call("_draw_editor_gizmo");
+ GDVIRTUAL_CALL(_draw_editor_gizmo);
}
void SkeletonModification2D::set_enabled(bool p_enabled) {
@@ -166,13 +166,13 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
if (operation_bone_parent_bone) {
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()),
+ stack->skeleton->to_local(p_operation_bone->get_global_position()),
operation_bone_parent_bone->get_global_rotation() - stack->skeleton->get_global_rotation());
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
}
if (p_constraint_inverted) {
@@ -186,7 +186,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_max), Math::sin(arc_angle_max)) * p_operation_bone->get_length(), bone_ik_color, 1.0);
} else {
- stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position()));
+ stack->skeleton->draw_set_transform(stack->skeleton->to_local(p_operation_bone->get_global_position()));
stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), 0, Math_PI * 2, 32, bone_ik_color, 1.0);
stack->skeleton->draw_line(Vector2(0, 0), Vector2(1, 0) * p_operation_bone->get_length(), bone_ik_color, 1.0);
}
@@ -228,9 +228,9 @@ bool SkeletonModification2D::get_editor_draw_gizmo() const {
}
void SkeletonModification2D::_bind_methods() {
- BIND_VMETHOD(MethodInfo("_execute", PropertyInfo(Variant::FLOAT, "delta")));
- BIND_VMETHOD(MethodInfo("_setup_modification", PropertyInfo(Variant::OBJECT, "modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D")));
- BIND_VMETHOD(MethodInfo("_draw_editor_gizmo"));
+ GDVIRTUAL_BIND(_execute, "delta");
+ GDVIRTUAL_BIND(_setup_modification, "modification_stack")
+ GDVIRTUAL_BIND(_draw_editor_gizmo)
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled);
ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled);
diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h
index 18633e55cb..aaddb9136e 100644
--- a/scene/resources/skeleton_modification_2d.h
+++ b/scene/resources/skeleton_modification_2d.h
@@ -57,6 +57,10 @@ protected:
bool _print_execution_error(bool p_condition, String p_message);
+ GDVIRTUAL1(_execute, double)
+ GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack2D>)
+ GDVIRTUAL0(_draw_editor_gizmo)
+
public:
virtual void _execute(float _delta);
virtual void _setup_modification(SkeletonModificationStack2D *p_stack);
diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp
index 7ea60e584e..6eab8d4afe 100644
--- a/scene/resources/skeleton_modification_2d_ccdik.cpp
+++ b/scene/resources/skeleton_modification_2d_ccdik.cpp
@@ -201,18 +201,18 @@ void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *
if (ccdik_data.rotate_from_joint) {
// To rotate from the joint, simply look at the target!
operation_transform.set_rotation(
- operation_transform.looking_at(p_target->get_global_transform().get_origin()).get_rotation() - operation_bone->get_bone_angle());
+ operation_transform.looking_at(p_target->get_global_position()).get_rotation() - operation_bone->get_bone_angle());
} else {
// How to rotate from the tip: get the difference of rotation needed from the tip to the target, from the perspective of the joint.
// Because we are only using the offset, we do not need to account for the bone angle of the Bone2D node.
- float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_transform().get_origin());
- float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_transform().get_origin());
+ float joint_to_tip = operation_transform.get_origin().angle_to_point(p_tip->get_global_position());
+ float joint_to_target = operation_transform.get_origin().angle_to_point(p_target->get_global_position());
operation_transform.set_rotation(
operation_transform.get_rotation() + (joint_to_target - joint_to_tip));
}
// Reset scale
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Apply constraints in globalspace:
if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) {
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index aef852f7e4..6e9429034f 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -149,37 +149,28 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
return;
}
fabrik_transform_chain.write[i] = joint_bone2d_node->get_global_transform();
-
- // Apply magnet positions
- if (i == 0) {
- continue; // The origin cannot use a magnet position!
- } else {
- Transform2D joint_trans = fabrik_transform_chain[i];
- joint_trans.set_origin(joint_trans.get_origin() + fabrik_data_chain[i].magnet_position);
- fabrik_transform_chain.write[i] = joint_trans;
- }
}
Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[fabrik_data_chain.size() - 1].bone2d_node_cache));
- float final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ float final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
Vector2 final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
float final_bone2d_length = final_bone2d_node->get_length() * MIN(final_bone2d_node->get_global_scale().x, final_bone2d_node->get_global_scale().y);
- float target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ float target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations = 0;
while (target_distance > chain_tolarance) {
chain_backwards();
chain_forwards();
- final_bone2d_angle = final_bone2d_node->get_global_transform().get_rotation();
+ final_bone2d_angle = final_bone2d_node->get_global_rotation();
if (fabrik_data_chain[fabrik_data_chain.size() - 1].use_target_rotation) {
final_bone2d_angle = target_global_pose.get_rotation();
}
final_bone2d_direction = Vector2(Math::cos(final_bone2d_angle), Math::sin(final_bone2d_angle));
- target_distance = (final_bone2d_node->get_global_transform().get_origin() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_transform().get_origin());
+ target_distance = (final_bone2d_node->get_global_position() + (final_bone2d_direction * final_bone2d_length)).distance_to(target->get_global_position());
chain_iterations += 1;
if (chain_iterations >= chain_max_iterations) {
@@ -210,7 +201,7 @@ void SkeletonModification2DFABRIK::_execute(float p_delta) {
chain_trans.set_rotation(chain_trans.get_rotation() - joint_bone2d_node->get_bone_angle());
// Reset scale
- chain_trans.set_scale(joint_bone2d_node->get_global_transform().get_scale());
+ chain_trans.set_scale(joint_bone2d_node->get_global_scale());
// Apply to the bone, and to the override
joint_bone2d_node->set_global_transform(chain_trans);
@@ -223,6 +214,11 @@ void SkeletonModification2DFABRIK::chain_backwards() {
Bone2D *final_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[final_joint_index].bone2d_node_cache));
Transform2D final_bone2d_trans = fabrik_transform_chain[final_joint_index];
+ // Apply magnet position
+ if (final_joint_index != 0) {
+ final_bone2d_trans.set_origin(final_bone2d_trans.get_origin() + fabrik_data_chain[final_joint_index].magnet_position);
+ }
+
// Set the rotation of the tip bone
final_bone2d_trans = final_bone2d_trans.looking_at(target_global_pose.get_origin());
@@ -245,6 +241,11 @@ void SkeletonModification2DFABRIK::chain_backwards() {
Bone2D *current_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache));
Transform2D current_pose = fabrik_transform_chain[i];
+ // Apply magnet position
+ if (i != 0) {
+ current_pose.set_origin(current_pose.get_origin() + fabrik_data_chain[i].magnet_position);
+ }
+
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length();
Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length);
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 2547083336..84abc9d020 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -171,7 +171,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
}
Transform2D operation_bone_trans = operation_bone->get_global_transform();
- Vector2 target_position = p_target->get_global_transform().get_origin();
+ Vector2 target_position = p_target->get_global_position();
jiggle_data_chain.write[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
@@ -215,7 +215,7 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
operation_bone_trans.set_rotation(operation_bone_trans.get_rotation() - operation_bone->get_bone_angle());
// Reset scale
- operation_bone_trans.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_bone_trans.set_scale(operation_bone->get_global_scale());
operation_bone->set_global_transform(operation_bone_trans);
stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, operation_bone->get_transform(), stack->strength, true);
@@ -244,7 +244,7 @@ void SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack
int bone_idx = jiggle_data_chain[i].bone_idx;
if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx);
- jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_transform().get_origin();
+ jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_position();
}
}
}
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
index fd5c8c7cc2..2da770f012 100644
--- a/scene/resources/skeleton_modification_2d_lookat.cpp
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -147,7 +147,7 @@ void SkeletonModification2DLookAt::_execute(float p_delta) {
// Look at the target!
operation_transform = operation_transform.looking_at(target_trans.get_origin());
// Apply whatever scale it had prior to looking_at
- operation_transform.set_scale(operation_bone->get_global_transform().get_scale());
+ operation_transform.set_scale(operation_bone->get_global_scale());
// Account for the direction the bone faces in:
operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle());
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index 0a91290015..88d80a501f 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -142,7 +142,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
// http://theorangeduck.com/page/simple-two-joint
// https://www.alanzucconi.com/2018/05/02/ik-2d-2/
// With modifications by TwistedTwigleg
- Vector2 target_difference = target->get_global_transform().get_origin() - joint_one_bone->get_global_transform().get_origin();
+ Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position();
float joint_one_to_target = target_difference.length();
float angle_atan = Math::atan2(target_difference.y, target_difference.x);
@@ -206,7 +206,7 @@ void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() {
return;
}
stack->skeleton->draw_set_transform(
- stack->skeleton->get_global_transform().affine_inverse().xform(operation_bone_one->get_global_position()),
+ stack->skeleton->to_local(operation_bone_one->get_global_position()),
operation_bone_one->get_global_rotation() - stack->skeleton->get_global_rotation());
Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4);
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
new file mode 100644
index 0000000000..ee02ede2d5
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* skeleton_modification_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "skeleton_modification_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+void SkeletonModification3D::_execute(real_t p_delta) {
+ GDVIRTUAL_CALL(_execute, p_delta);
+
+ if (!enabled)
+ return;
+}
+
+void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack) {
+ is_setup = true;
+ } else {
+ WARN_PRINT("Could not setup modification with name " + this->get_name());
+ }
+
+ GDVIRTUAL_CALL(_setup_modification, Ref<SkeletonModificationStack3D>(p_stack));
+}
+
+void SkeletonModification3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+}
+
+bool SkeletonModification3D::get_enabled() {
+ return enabled;
+}
+
+// Helper function. Needed for CCDIK.
+real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert) {
+ // Map to the 0 to 360 range (in radians though) instead of the -180 to 180 range.
+ if (p_angle < 0) {
+ p_angle = Math_TAU + p_angle;
+ }
+
+ // Make min and max in the range of 0 to 360 (in radians), and make sure they are in the right order
+ if (p_min_bound < 0) {
+ p_min_bound = Math_TAU + p_min_bound;
+ }
+ if (p_max_bound < 0) {
+ p_max_bound = Math_TAU + p_max_bound;
+ }
+ if (p_min_bound > p_max_bound) {
+ real_t tmp = p_min_bound;
+ p_min_bound = p_max_bound;
+ p_max_bound = tmp;
+ }
+
+ // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
+ if (p_invert == false) {
+ if (p_angle < p_min_bound || p_angle > p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ } else {
+ if (p_angle > p_min_bound && p_angle < p_max_bound) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
+ }
+ }
+ }
+ return p_angle;
+}
+
+bool SkeletonModification3D::_print_execution_error(bool p_condition, String p_message) {
+ // If the modification is not setup, don't bother printing the error
+ if (!is_setup) {
+ return p_condition;
+ }
+
+ if (p_condition && !execution_error_found) {
+ ERR_PRINT(p_message);
+ execution_error_found = true;
+ }
+ return p_condition;
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3D::get_modification_stack() {
+ return stack;
+}
+
+void SkeletonModification3D::set_is_setup(bool p_is_setup) {
+ is_setup = p_is_setup;
+}
+
+bool SkeletonModification3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModification3D::set_execution_mode(int p_mode) {
+ execution_mode = p_mode;
+}
+
+int SkeletonModification3D::get_execution_mode() const {
+ return execution_mode;
+}
+
+void SkeletonModification3D::_bind_methods() {
+ GDVIRTUAL_BIND(_execute, "delta");
+ GDVIRTUAL_BIND(_setup_modification, "modification_stack")
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification3D::get_enabled);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification3D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification3D::set_is_setup);
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification3D::get_is_setup);
+ ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification3D::set_execution_mode);
+ ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification3D::get_execution_mode);
+ ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification3D::clamp_angle);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "execution_mode", PROPERTY_HINT_ENUM, "process, physics_process"), "set_execution_mode", "get_execution_mode");
+}
+
+SkeletonModification3D::SkeletonModification3D() {
+ stack = nullptr;
+ is_setup = false;
+}
diff --git a/scene/resources/ray_shape_3d.h b/scene/resources/skeleton_modification_3d.h
index 2da6311321..fb1f3d33d1 100644
--- a/scene/resources/ray_shape_3d.h
+++ b/scene/resources/skeleton_modification_3d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* ray_shape_3d.h */
+/* skeleton_modification_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,52 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RAY_SHAPE_H
-#define RAY_SHAPE_H
-#include "scene/resources/shape_3d.h"
+#ifndef SKELETONMODIFICATION3D_H
+#define SKELETONMODIFICATION3D_H
-class RayShape3D : public Shape3D {
- GDCLASS(RayShape3D, Shape3D);
- float length = 1.0;
- bool slips_on_slope = false;
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_stack_3d.h"
+
+class SkeletonModificationStack3D;
+
+class SkeletonModification3D : public Resource {
+ GDCLASS(SkeletonModification3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModificationStack3D;
protected:
static void _bind_methods();
- virtual void _update_shape() override;
+
+ SkeletonModificationStack3D *stack;
+ int execution_mode = 0; // 0 = process
+
+ bool enabled = true;
+ bool is_setup = false;
+ bool execution_error_found = false;
+
+ bool _print_execution_error(bool p_condition, String p_message);
+
+ GDVIRTUAL1(_execute, double)
+ GDVIRTUAL1(_setup_modification, Ref<SkeletonModificationStack3D>)
public:
- void set_length(float p_length);
- float get_length() const;
+ virtual void _execute(real_t p_delta);
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack);
+
+ real_t clamp_angle(real_t p_angle, real_t p_min_bound, real_t p_max_bound, bool p_invert);
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled();
- void set_slips_on_slope(bool p_active);
- bool get_slips_on_slope() const;
+ void set_execution_mode(int p_mode);
+ int get_execution_mode() const;
- virtual Vector<Vector3> get_debug_mesh_lines() const override;
- virtual real_t get_enclosing_radius() const override;
+ Ref<SkeletonModificationStack3D> get_modification_stack();
- RayShape3D();
+ void set_is_setup(bool p_setup);
+ bool get_is_setup() const;
+
+ SkeletonModification3D();
};
-#endif // RAY_SHAPE_H
+
+#endif // SKELETONMODIFICATION3D_H
diff --git a/scene/resources/skeleton_modification_3d_ccdik.cpp b/scene/resources/skeleton_modification_3d_ccdik.cpp
new file mode 100644
index 0000000000..6409022563
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.cpp
@@ -0,0 +1,474 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_ccdik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DCCDIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ set_ccdik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_ccdik_joint_bone_index(which, p_value);
+ } else if (what == "ccdik_axis") {
+ set_ccdik_joint_ccdik_axis(which, p_value);
+ } else if (what == "enable_joint_constraint") {
+ set_ccdik_joint_enable_constraint(which, p_value);
+ } else if (what == "joint_constraint_angle_min") {
+ set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angle_max") {
+ set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(real_t(p_value)));
+ } else if (what == "joint_constraint_angles_invert") {
+ set_ccdik_joint_constraint_invert(which, p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DCCDIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int ccdik_data_size = ccdik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, ccdik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_ccdik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_ccdik_joint_bone_index(which);
+ } else if (what == "ccdik_axis") {
+ r_ret = get_ccdik_joint_ccdik_axis(which);
+ } else if (what == "enable_joint_constraint") {
+ r_ret = get_ccdik_joint_enable_constraint(which);
+ } else if (what == "joint_constraint_angle_min") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which));
+ } else if (what == "joint_constraint_angle_max") {
+ r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which));
+ } else if (what == "joint_constraint_angles_invert") {
+ r_ret = get_ccdik_joint_constraint_invert(which);
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "ccdik_axis",
+ PROPERTY_HINT_ENUM, "X Axis, Y Axis, Z Axis", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_joint_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (ccdik_data_chain[i].enable_constraint) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_min", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "joint_constraint_angle_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "joint_constraint_angles_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update");
+ update_target_cache();
+ return;
+ }
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update");
+ update_tip_cache();
+ return;
+ }
+
+ // Reset the local bone overrides for CCDIK affected nodes
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ stack->skeleton->set_bone_local_pose_override(ccdik_data_chain[i].bone_idx,
+ stack->skeleton->get_bone_local_pose_override(ccdik_data_chain[i].bone_idx),
+ 0.0, false);
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ Node3D *node_tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(!node_tip || !node_tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ if (use_high_quality_solve) {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ for (uint32_t j = i; j < ccdik_data_chain.size(); j++) {
+ _execute_ccdik_joint(j, node_target, node_tip);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < ccdik_data_chain.size(); i++) {
+ _execute_ccdik_joint(i, node_target, node_tip);
+ }
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip) {
+ CCDIK_Joint_Data ccdik_data = ccdik_data_chain[p_joint_idx];
+
+ if (_print_execution_error(ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count(),
+ "CCDIK joint: bone index for joint" + itos(p_joint_idx) + " not found. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D bone_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->get_bone_global_pose(ccdik_data.bone_idx));
+ Transform3D tip_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_tip->get_global_transform()));
+ Transform3D target_trans = stack->skeleton->global_pose_to_local_pose(ccdik_data.bone_idx, stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()));
+
+ if (tip_trans.origin.distance_to(target_trans.origin) <= 0.01) {
+ return;
+ }
+
+ // Inspired (and very loosely based on) by the CCDIK algorithm made by Zalo on GitHub (https://github.com/zalo/MathUtilities)
+ // Convert the 3D position to a 2D position so we can use Atan2 (via the angle function)
+ // to know how much rotation we need on the given axis to place the tip at the target.
+ Vector2 tip_pos_2d;
+ Vector2 target_pos_2d;
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ tip_pos_2d = Vector2(tip_trans.origin.y, tip_trans.origin.z);
+ target_pos_2d = Vector2(target_trans.origin.y, target_trans.origin.z);
+ bone_trans.basis.rotate_local(Vector3(1, 0, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ tip_pos_2d = Vector2(tip_trans.origin.z, tip_trans.origin.x);
+ target_pos_2d = Vector2(target_trans.origin.z, target_trans.origin.x);
+ bone_trans.basis.rotate_local(Vector3(0, 1, 0), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ tip_pos_2d = Vector2(tip_trans.origin.x, tip_trans.origin.y);
+ target_pos_2d = Vector2(target_trans.origin.x, target_trans.origin.y);
+ bone_trans.basis.rotate_local(Vector3(0, 0, 1), target_pos_2d.angle() - tip_pos_2d.angle());
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+
+ if (ccdik_data.enable_constraint) {
+ Vector3 rotation_axis;
+ real_t rotation_angle;
+ bone_trans.basis.get_axis_angle(rotation_axis, rotation_angle);
+
+ // Note: When the axis has a negative direction, the angle is OVER 180 degrees and therefore we need to account for this
+ // when constraining.
+ if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_X) {
+ if (rotation_axis.x < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(1, 0, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Y) {
+ if (rotation_axis.y < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 1, 0);
+ }
+ } else if (ccdik_data.ccdik_axis == CCDIK_Axes::AXIS_Z) {
+ if (rotation_axis.z < 0) {
+ rotation_angle += Math_PI;
+ rotation_axis = Vector3(0, 0, 1);
+ }
+ } else {
+ // Should never happen, but...
+ ERR_FAIL_MSG("CCDIK joint: Unknown axis vector passed for joint" + itos(p_joint_idx) + ". Cannot execute modification!");
+ }
+ rotation_angle = clamp_angle(rotation_angle, ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angles_invert);
+
+ bone_trans.basis.set_axis_angle(rotation_axis, rotation_angle);
+ }
+
+ stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(ccdik_data.bone_idx);
+}
+
+void SkeletonModification3DCCDIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+ update_tip_cache();
+ }
+}
+
+void SkeletonModification3DCCDIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::update_tip_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: node is not in scene tree!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DCCDIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DCCDIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_tip_cache();
+}
+
+NodePath SkeletonModification3DCCDIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DCCDIK::set_use_high_quality_solve(bool p_high_quality) {
+ use_high_quality_solve = p_high_quality;
+}
+
+bool SkeletonModification3DCCDIK::get_use_high_quality_solve() const {
+ return use_high_quality_solve;
+}
+
+// CCDIK joint data functions
+String SkeletonModification3DCCDIK::get_ccdik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return ccdik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ ccdik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ ccdik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return ccdik_data_chain[p_joint_idx].ccdik_axis;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_axis < 0, "CCDIK axis is out of range: The axis mode is too low!");
+ ccdik_data_chain[p_joint_idx].ccdik_axis = p_axis;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].enable_constraint;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].enable_constraint = p_enable;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_min;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_min = p_angle_min;
+}
+
+real_t SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angle_max;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angle_max = p_angle_max;
+}
+
+bool SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert(int p_joint_idx) const {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return ccdik_data_chain[p_joint_idx].constraint_angles_invert;
+}
+
+void SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert) {
+ const int bone_chain_size = ccdik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ccdik_data_chain[p_joint_idx].constraint_angles_invert = p_invert;
+}
+
+int SkeletonModification3DCCDIK::get_ccdik_data_chain_length() {
+ return ccdik_data_chain.size();
+}
+void SkeletonModification3DCCDIK::set_ccdik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ ccdik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DCCDIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DCCDIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DCCDIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DCCDIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DCCDIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_high_quality_solve", "high_quality_solve"), &SkeletonModification3DCCDIK::set_use_high_quality_solve);
+ ClassDB::bind_method(D_METHOD("get_use_high_quality_solve"), &SkeletonModification3DCCDIK::get_use_high_quality_solve);
+
+ // CCDIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_name", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DCCDIK::set_ccdik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_ccdik_axis", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_ccdik_axis", "joint_idx", "axis"), &SkeletonModification3DCCDIK::set_ccdik_joint_ccdik_axis);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_joint_constraint", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_joint_constraint", "joint_idx", "enable"), &SkeletonModification3DCCDIK::set_ccdik_joint_enable_constraint);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "min_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_min);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "max_angle"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_angle_max);
+ ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_invert", "joint_idx"), &SkeletonModification3DCCDIK::get_ccdik_joint_constraint_invert);
+ ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_invert", "joint_idx", "invert"), &SkeletonModification3DCCDIK::set_ccdik_joint_constraint_invert);
+
+ ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification3DCCDIK::set_ccdik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification3DCCDIK::get_ccdik_data_chain_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_tip_node", "get_tip_node");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "high_quality_solve", PROPERTY_HINT_NONE, ""), "set_use_high_quality_solve", "get_use_high_quality_solve");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ccdik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_ccdik_data_chain_length", "get_ccdik_data_chain_length");
+}
+
+SkeletonModification3DCCDIK::SkeletonModification3DCCDIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DCCDIK::~SkeletonModification3DCCDIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h
new file mode 100644
index 0000000000..e7537cc5b0
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_ccdik.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* skeleton_modification_3d_ccdik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DCCDIK_H
+#define SKELETONMODIFICATION3DCCDIK_H
+
+class SkeletonModification3DCCDIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D);
+
+private:
+ enum CCDIK_Axes {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z
+ };
+
+ struct CCDIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ int ccdik_axis = 0;
+
+ bool enable_constraint = false;
+ real_t constraint_angle_min = 0;
+ real_t constraint_angle_max = (2.0 * Math_PI);
+ bool constraint_angles_invert = false;
+ };
+
+ LocalVector<CCDIK_Joint_Data> ccdik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_high_quality_solve = true;
+
+ void update_target_cache();
+ void update_tip_cache();
+
+ void _execute_ccdik_joint(int p_joint_idx, Node3D *p_target, Node3D *p_tip);
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_high_quality_solve(bool p_solve);
+ bool get_use_high_quality_solve() const;
+
+ String get_ccdik_joint_bone_name(int p_joint_idx) const;
+ void set_ccdik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_ccdik_joint_bone_index(int p_joint_idx) const;
+ void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ int get_ccdik_joint_ccdik_axis(int p_joint_idx) const;
+ void set_ccdik_joint_ccdik_axis(int p_joint_idx, int p_axis);
+ bool get_ccdik_joint_enable_constraint(int p_joint_idx) const;
+ void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_enable);
+ real_t get_ccdik_joint_constraint_angle_min(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_min(int p_joint_idx, real_t p_angle_min);
+ real_t get_ccdik_joint_constraint_angle_max(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_angle_max(int p_joint_idx, real_t p_angle_max);
+ bool get_ccdik_joint_constraint_invert(int p_joint_idx) const;
+ void set_ccdik_joint_constraint_invert(int p_joint_idx, bool p_invert);
+
+ int get_ccdik_data_chain_length();
+ void set_ccdik_data_chain_length(int p_new_length);
+
+ SkeletonModification3DCCDIK();
+ ~SkeletonModification3DCCDIK();
+};
+
+#endif //SKELETONMODIFICATION3DCCDIK_H
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
new file mode 100644
index 0000000000..69f75eb7b5
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -0,0 +1,628 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_fabrik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DFABRIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ set_fabrik_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_fabrik_joint_bone_index(which, p_value);
+ } else if (what == "length") {
+ set_fabrik_joint_length(which, p_value);
+ } else if (what == "magnet_position") {
+ set_fabrik_joint_magnet(which, p_value);
+ } else if (what == "auto_calculate_length") {
+ set_fabrik_joint_auto_calculate_length(which, p_value);
+ } else if (what == "use_tip_node") {
+ set_fabrik_joint_use_tip_node(which, p_value);
+ } else if (what == "tip_node") {
+ set_fabrik_joint_tip_node(which, p_value);
+ } else if (what == "use_target_basis") {
+ set_fabrik_joint_use_target_basis(which, p_value);
+ } else if (what == "roll") {
+ set_fabrik_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DFABRIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int fabrik_data_size = fabrik_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, fabrik_data_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_fabrik_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_fabrik_joint_bone_index(which);
+ } else if (what == "length") {
+ r_ret = get_fabrik_joint_length(which);
+ } else if (what == "magnet_position") {
+ r_ret = get_fabrik_joint_magnet(which);
+ } else if (what == "auto_calculate_length") {
+ r_ret = get_fabrik_joint_auto_calculate_length(which);
+ } else if (what == "use_tip_node") {
+ r_ret = get_fabrik_joint_use_tip_node(which);
+ } else if (what == "tip_node") {
+ r_ret = get_fabrik_joint_tip_node(which);
+ } else if (what == "use_target_basis") {
+ r_ret = get_fabrik_joint_use_target_basis(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_fabrik_joint_roll(which));
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "auto_calculate_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (!fabrik_data_chain[i].auto_calculate_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (fabrik_data_chain[i].use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+
+ // Cannot apply magnet to the origin of the chain, as it will not do anything.
+ if (i > 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ // Only give the override basis option on the last bone in the chain, so only include it for the last bone.
+ if (i == fabrik_data_chain.size() - 1) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_target_cache();
+ return;
+ }
+
+ if (_print_execution_error(fabrik_data_chain.size() <= 1, "FABRIK requires at least two joints to operate. Cannot execute modification!")) {
+ return;
+ }
+
+ Node3D *node_target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!node_target || !node_target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+
+ // Verify that all joints have a valid bone ID, and that all bone lengths are zero or more
+ // Also, while we are here, apply magnet positions.
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ if (_print_execution_error(fabrik_data_chain[i].bone_idx < 0, "FABRIK Joint " + itos(i) + " has an invalid bone ID. Cannot execute!")) {
+ return;
+ }
+
+ if (fabrik_data_chain[i].length < 0 && fabrik_data_chain[i].auto_calculate_length) {
+ fabrik_joint_auto_calculate_length(i);
+ }
+ if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) {
+ return;
+ }
+
+ Transform3D local_pose_override = stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[i].bone_idx);
+
+ // Apply magnet positions:
+ if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
+ int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx));
+ local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
+ } else {
+ local_pose_override.origin += fabrik_data_chain[i].magnet_position;
+ }
+
+ stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, local_pose_override, stack->strength, true);
+ }
+
+ target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform());
+ origin_global_pose = stack->skeleton->local_pose_to_global_pose(
+ fabrik_data_chain[0].bone_idx, stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[0].bone_idx));
+
+ final_joint_idx = fabrik_data_chain.size() - 1;
+ real_t target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+ chain_iterations = 0;
+
+ while (target_distance > chain_tolerance) {
+ chain_backwards();
+ chain_forwards();
+
+ // update the target distance
+ target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+
+ // update chain iterations
+ chain_iterations += 1;
+ if (chain_iterations >= chain_max_iterations) {
+ break;
+ }
+ }
+ chain_apply();
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DFABRIK::chain_backwards() {
+ int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx;
+ Transform3D final_joint_trans = stack->skeleton->local_pose_to_global_pose(final_bone_idx, stack->skeleton->get_bone_local_pose_override(final_bone_idx));
+
+ // Get the direction the final bone is facing in.
+ stack->skeleton->update_bone_rest_forward_vector(final_bone_idx);
+ Transform3D final_bone_direction_trans = final_joint_trans.looking_at(target_global_pose.origin, Vector3(0, 1, 0));
+ final_bone_direction_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(final_bone_idx, final_bone_direction_trans.basis);
+ Vector3 direction = final_bone_direction_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+
+ // If set to override, then use the target's Basis rather than the bone's
+ if (fabrik_data_chain[final_joint_idx].use_target_basis) {
+ direction = target_global_pose.basis.xform(stack->skeleton->get_bone_axis_forward_vector(final_bone_idx)).normalized();
+ }
+
+ // set the position of the final joint to the target position
+ final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length);
+ final_joint_trans = stack->skeleton->global_pose_to_local_pose(final_bone_idx, final_joint_trans);
+ stack->skeleton->set_bone_local_pose_override(final_bone_idx, final_joint_trans, stack->strength, true);
+
+ // for all other joints, move them towards the target
+ int i = final_joint_idx;
+ while (i >= 1) {
+ int next_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ i -= 1;
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length();
+ current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_forwards() {
+ // Set root at the initial position.
+ int origin_bone_idx = fabrik_data_chain[0].bone_idx;
+ Transform3D root_transform = stack->skeleton->local_pose_to_global_pose(origin_bone_idx, stack->skeleton->get_bone_local_pose_override(origin_bone_idx));
+ root_transform.origin = origin_global_pose.origin;
+ stack->skeleton->set_bone_local_pose_override(origin_bone_idx, stack->skeleton->global_pose_to_local_pose(origin_bone_idx, root_transform), stack->strength, true);
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length();
+ next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
+
+ // Apply it back to the skeleton
+ stack->skeleton->set_bone_local_pose_override(next_bone_idx, stack->skeleton->global_pose_to_local_pose(next_bone_idx, next_bone_trans), stack->strength, true);
+ }
+}
+
+void SkeletonModification3DFABRIK::chain_apply() {
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ int current_bone_idx = fabrik_data_chain[i].bone_idx;
+ Transform3D current_trans = stack->skeleton->get_bone_local_pose_override(current_bone_idx);
+ current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, current_trans);
+
+ // If this is the last bone in the chain...
+ if (i == fabrik_data_chain.size() - 1) {
+ if (fabrik_data_chain[i].use_target_basis == false) { // Point to target...
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the target:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(target_global_pose.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ } else { // Use the target's Basis...
+ current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale());
+ }
+ } else { // every other bone in the chain...
+ int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
+ Transform3D next_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(current_bone_idx);
+ // Rotate the bone towards the next bone in the chain:
+ current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin));
+ current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
+ }
+ current_trans = stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans);
+ current_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, current_trans, stack->strength, true);
+ }
+
+ // Update all the bones so the next modification has up-to-date data.
+ stack->skeleton->force_update_all_bone_transforms();
+}
+
+void SkeletonModification3DFABRIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_target_cache();
+
+ for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
+ update_joint_tip_cache(i);
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_target_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::update_joint_tip_cache(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_MSG(p_joint_idx, bone_chain_size, "FABRIK joint not found");
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+ fabrik_data_chain[p_joint_idx].tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && fabrik_data_chain[p_joint_idx].tip_node.is_empty() == false) {
+ if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].tip_node)) {
+ Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache for joint " + itos(p_joint_idx) + ": node is not in scene tree!");
+ fabrik_data_chain[p_joint_idx].tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DFABRIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_target_cache();
+}
+
+NodePath SkeletonModification3DFABRIK::get_target_node() const {
+ return target_node;
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() {
+ return fabrik_data_chain.size();
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ fabrik_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_chain_tolerance() {
+ return chain_tolerance;
+}
+
+void SkeletonModification3DFABRIK::set_chain_tolerance(real_t p_tolerance) {
+ ERR_FAIL_COND_MSG(p_tolerance <= 0, "FABRIK chain tolerance must be more than zero!");
+ chain_tolerance = p_tolerance;
+}
+
+int SkeletonModification3DFABRIK::get_chain_max_iterations() {
+ return chain_max_iterations;
+}
+void SkeletonModification3DFABRIK::set_chain_max_iterations(int p_iterations) {
+ ERR_FAIL_COND_MSG(p_iterations <= 0, "FABRIK chain iterations must be at least one. Set enabled to false to disable the FABRIK chain.");
+ chain_max_iterations = p_iterations;
+}
+
+// FABRIK joint data functions
+String SkeletonModification3DFABRIK::get_fabrik_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, String());
+ return fabrik_data_chain[p_joint_idx].bone_name;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].bone_name = p_bone_name;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ fabrik_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ fabrik_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return fabrik_data_chain[p_joint_idx].length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_length < 0, "FABRIK joint length cannot be less than zero!");
+
+ if (!is_setup) {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ return;
+ }
+
+ if (fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ WARN_PRINT("FABRIK Length not set: auto calculate length is enabled for this joint!");
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ } else {
+ fabrik_data_chain[p_joint_idx].length = p_bone_length;
+ }
+
+ execution_error_found = false;
+}
+
+Vector3 SkeletonModification3DFABRIK::get_fabrik_joint_magnet(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3());
+ return fabrik_data_chain[p_joint_idx].magnet_position;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].magnet_position = p_magnet;
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].auto_calculate_length;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].auto_calculate_length = p_auto_calculate;
+ fabrik_joint_auto_calculate_length(p_joint_idx);
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_joint_idx) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ if (!fabrik_data_chain[p_joint_idx].auto_calculate_length) {
+ return;
+ }
+
+ if (!stack || !stack->skeleton || !is_setup) {
+ _print_execution_error(true, "Cannot auto calculate joint length: modification is not properly setup!");
+ return;
+ }
+ ERR_FAIL_INDEX_MSG(fabrik_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_count(),
+ "Bone for joint " + itos(p_joint_idx) + " is not set or points to an unknown bone!");
+
+ if (fabrik_data_chain[p_joint_idx].use_tip_node) { // Use the tip node to update joint length.
+
+ update_joint_tip_cache(p_joint_idx);
+
+ Node3D *tip_node = Object::cast_to<Node3D>(ObjectDB::get_instance(fabrik_data_chain[p_joint_idx].tip_node_cache));
+ ERR_FAIL_COND_MSG(!tip_node, "Tip node for joint " + itos(p_joint_idx) + "is not a Node3D-based node. Cannot calculate length...");
+ ERR_FAIL_COND_MSG(!tip_node->is_inside_tree(), "Tip node for joint " + itos(p_joint_idx) + "is not in the scene tree. Cannot calculate length...");
+
+ Transform3D node_trans = tip_node->get_global_transform();
+ node_trans = stack->skeleton->world_transform_to_global_pose(node_trans);
+ node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
+ fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
+ } else { // Use child bone(s) to update joint length, if possible
+ Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx);
+ if (bone_children.size() <= 0) {
+ ERR_FAIL_MSG("Cannot calculate length for joint " + itos(p_joint_idx) + "joint uses leaf bone. \nPlease manually set the bone length or use a tip node!");
+ return;
+ }
+
+ real_t final_length = 0;
+ for (int i = 0; i < bone_children.size(); i++) {
+ Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]);
+ final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
+ }
+ fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size();
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+NodePath SkeletonModification3DFABRIK::get_fabrik_joint_tip_node(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, NodePath());
+ return fabrik_data_chain[p_joint_idx].tip_node;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].tip_node = p_tip_node;
+ update_joint_tip_cache(p_joint_idx);
+}
+
+bool SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return fabrik_data_chain[p_joint_idx].use_target_basis;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_target_basis) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].use_target_basis = p_use_target_basis;
+}
+
+real_t SkeletonModification3DFABRIK::get_fabrik_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return fabrik_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DFABRIK::set_fabrik_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = fabrik_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ fabrik_data_chain[p_joint_idx].roll = p_roll;
+}
+
+void SkeletonModification3DFABRIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DFABRIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DFABRIK::get_target_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification3DFABRIK::set_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification3DFABRIK::get_fabrik_data_chain_length);
+ ClassDB::bind_method(D_METHOD("set_chain_tolerance", "tolerance"), &SkeletonModification3DFABRIK::set_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("get_chain_tolerance"), &SkeletonModification3DFABRIK::get_chain_tolerance);
+ ClassDB::bind_method(D_METHOD("set_chain_max_iterations", "max_iterations"), &SkeletonModification3DFABRIK::set_chain_max_iterations);
+ ClassDB::bind_method(D_METHOD("get_chain_max_iterations"), &SkeletonModification3DFABRIK::get_chain_max_iterations);
+
+ // FABRIK joint data functions
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_name", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_name", "joint_idx", "bone_name"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_index"), &SkeletonModification3DFABRIK::set_fabrik_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_length", "joint_idx", "length"), &SkeletonModification3DFABRIK::set_fabrik_joint_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet", "joint_idx", "magnet_position"), &SkeletonModification3DFABRIK::set_fabrik_joint_magnet);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_auto_calculate_length", "joint_idx", "auto_calculate_length"), &SkeletonModification3DFABRIK::set_fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("fabrik_joint_auto_calculate_length", "joint_idx"), &SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_tip_node", "joint_idx", "use_tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_tip_node", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_tip_node", "joint_idx", "tip_node"), &SkeletonModification3DFABRIK::set_fabrik_joint_tip_node);
+ ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_basis", "joint_idx"), &SkeletonModification3DFABRIK::get_fabrik_joint_use_target_basis);
+ ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_basis", "joint_idx", "use_target_basis"), &SkeletonModification3DFABRIK::set_fabrik_joint_use_target_basis);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fabrik_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_fabrik_data_chain_length", "get_fabrik_data_chain_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "chain_tolerance", PROPERTY_HINT_RANGE, "0,100,0.001"), "set_chain_tolerance", "get_chain_tolerance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "chain_max_iterations", PROPERTY_HINT_RANGE, "1,50,1"), "set_chain_max_iterations", "get_chain_max_iterations");
+}
+
+SkeletonModification3DFABRIK::SkeletonModification3DFABRIK() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DFABRIK::~SkeletonModification3DFABRIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
new file mode 100644
index 0000000000..9b5da883d4
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* skeleton_modification_3d_fabrik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DFABRIK_H
+#define SKELETONMODIFICATION3DFABRIK_H
+
+class SkeletonModification3DFABRIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D);
+
+private:
+ struct FABRIK_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+ real_t length = -1;
+ Vector3 magnet_position = Vector3(0, 0, 0);
+
+ bool auto_calculate_length = true;
+ bool use_tip_node = false;
+ NodePath tip_node = NodePath();
+ ObjectID tip_node_cache;
+
+ bool use_target_basis = false;
+ real_t roll = 0;
+ };
+
+ LocalVector<FABRIK_Joint_Data> fabrik_data_chain;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ real_t chain_tolerance = 0.01;
+ int chain_max_iterations = 10;
+ int chain_iterations = 0;
+
+ void update_target_cache();
+ void update_joint_tip_cache(int p_joint_idx);
+
+ int final_joint_idx = 0;
+ Transform3D target_global_pose = Transform3D();
+ Transform3D origin_global_pose = Transform3D();
+
+ void chain_backwards();
+ void chain_forwards();
+ void chain_apply();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ int get_fabrik_data_chain_length();
+ void set_fabrik_data_chain_length(int p_new_length);
+
+ real_t get_chain_tolerance();
+ void set_chain_tolerance(real_t p_tolerance);
+
+ int get_chain_max_iterations();
+ void set_chain_max_iterations(int p_iterations);
+
+ String get_fabrik_joint_bone_name(int p_joint_idx) const;
+ void set_fabrik_joint_bone_name(int p_joint_idx, String p_bone_name);
+ int get_fabrik_joint_bone_index(int p_joint_idx) const;
+ void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx);
+ real_t get_fabrik_joint_length(int p_joint_idx) const;
+ void set_fabrik_joint_length(int p_joint_idx, real_t p_bone_length);
+ Vector3 get_fabrik_joint_magnet(int p_joint_idx) const;
+ void set_fabrik_joint_magnet(int p_joint_idx, Vector3 p_magnet);
+ bool get_fabrik_joint_auto_calculate_length(int p_joint_idx) const;
+ void set_fabrik_joint_auto_calculate_length(int p_joint_idx, bool p_auto_calculate);
+ void fabrik_joint_auto_calculate_length(int p_joint_idx);
+ bool get_fabrik_joint_use_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_use_tip_node(int p_joint_idx, bool p_use_tip_node);
+ NodePath get_fabrik_joint_tip_node(int p_joint_idx) const;
+ void set_fabrik_joint_tip_node(int p_joint_idx, NodePath p_tip_node);
+ bool get_fabrik_joint_use_target_basis(int p_joint_idx) const;
+ void set_fabrik_joint_use_target_basis(int p_joint_idx, bool p_use_basis);
+ real_t get_fabrik_joint_roll(int p_joint_idx) const;
+ void set_fabrik_joint_roll(int p_joint_idx, real_t p_roll);
+
+ SkeletonModification3DFABRIK();
+ ~SkeletonModification3DFABRIK();
+};
+
+#endif //SKELETONMODIFICATION3DFABRIK_H
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
new file mode 100644
index 0000000000..1fb7dad2ad
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -0,0 +1,573 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_jiggle.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DJiggle::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ set_jiggle_joint_bone_name(which, p_value);
+ } else if (what == "bone_index") {
+ set_jiggle_joint_bone_index(which, p_value);
+ } else if (what == "override_defaults") {
+ set_jiggle_joint_override(which, p_value);
+ } else if (what == "stiffness") {
+ set_jiggle_joint_stiffness(which, p_value);
+ } else if (what == "mass") {
+ set_jiggle_joint_mass(which, p_value);
+ } else if (what == "damping") {
+ set_jiggle_joint_damping(which, p_value);
+ } else if (what == "use_gravity") {
+ set_jiggle_joint_use_gravity(which, p_value);
+ } else if (what == "gravity") {
+ set_jiggle_joint_gravity(which, p_value);
+ } else if (what == "roll") {
+ set_jiggle_joint_roll(which, Math::deg2rad(real_t(p_value)));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ set_use_colliders(p_value);
+ } else if (path == "collision_mask") {
+ set_collision_mask(p_value);
+ }
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModification3DJiggle::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("joint_data/")) {
+ const int jiggle_size = jiggle_data_chain.size();
+ int which = path.get_slicec('/', 1).to_int();
+ String what = path.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(which, jiggle_size, false);
+
+ if (what == "bone_name") {
+ r_ret = get_jiggle_joint_bone_name(which);
+ } else if (what == "bone_index") {
+ r_ret = get_jiggle_joint_bone_index(which);
+ } else if (what == "override_defaults") {
+ r_ret = get_jiggle_joint_override(which);
+ } else if (what == "stiffness") {
+ r_ret = get_jiggle_joint_stiffness(which);
+ } else if (what == "mass") {
+ r_ret = get_jiggle_joint_mass(which);
+ } else if (what == "damping") {
+ r_ret = get_jiggle_joint_damping(which);
+ } else if (what == "use_gravity") {
+ r_ret = get_jiggle_joint_use_gravity(which);
+ } else if (what == "gravity") {
+ r_ret = get_jiggle_joint_gravity(which);
+ } else if (what == "roll") {
+ r_ret = Math::rad2deg(get_jiggle_joint_roll(which));
+ }
+ return true;
+ } else {
+ if (path == "use_colliders") {
+ r_ret = get_use_colliders();
+ } else if (path == "collision_mask") {
+ r_ret = get_collision_mask();
+ }
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModification3DJiggle::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_colliders", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_colliders) {
+ p_list->push_back(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS, "", PROPERTY_USAGE_DEFAULT));
+ }
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ String base_string = "joint_data/" + itos(i) + "/";
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, base_string + "bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "roll", PROPERTY_HINT_RANGE, "-360,360,0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "override_defaults", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+
+ if (jiggle_data_chain[i].override_defaults) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "stiffness", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "mass", PROPERTY_HINT_RANGE, "0, 1000, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (jiggle_data_chain[i].use_gravity) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ _print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!");
+
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ _execute_jiggle_joint(i, target, p_delta);
+ }
+
+ execution_error_found = false;
+}
+
+void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta) {
+ // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone
+ // With modifications by TwistedTwigleg.
+
+ if (jiggle_data_chain[p_joint_idx].bone_idx <= -2) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(jiggle_data_chain[p_joint_idx].bone_name);
+ }
+ if (_print_execution_error(
+ jiggle_data_chain[p_joint_idx].bone_idx < 0 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count(),
+ "Jiggle joint " + itos(p_joint_idx) + " bone index is invald. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx));
+ Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin;
+
+ jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
+
+ if (jiggle_data_chain[p_joint_idx].use_gravity) {
+ Vector3 gravity_to_apply = new_bone_trans.basis.inverse().xform(jiggle_data_chain[p_joint_idx].gravity);
+ jiggle_data_chain[p_joint_idx].force += gravity_to_apply * p_delta;
+ }
+
+ jiggle_data_chain[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass;
+ jiggle_data_chain[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping);
+
+ jiggle_data_chain[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force;
+ jiggle_data_chain[p_joint_idx].dynamic_position += new_bone_trans.origin - jiggle_data_chain[p_joint_idx].last_position;
+ jiggle_data_chain[p_joint_idx].last_position = new_bone_trans.origin;
+
+ // Collision detection/response
+ if (use_colliders) {
+ if (execution_mode == SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process) {
+ Ref<World3D> world_3d = stack->skeleton->get_world_3d();
+ ERR_FAIL_COND(world_3d.is_null());
+ PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space());
+ PhysicsDirectSpaceState3D::RayResult ray_result;
+
+ // Convert to world transforms, which is what the physics server needs
+ Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
+ Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(),
+ ray_result, Set<RID>(), collision_mask);
+
+ if (ray_hit) {
+ jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
+ jiggle_data_chain[p_joint_idx].acceleration = Vector3(0, 0, 0);
+ jiggle_data_chain[p_joint_idx].velocity = Vector3(0, 0, 0);
+ } else {
+ jiggle_data_chain[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position;
+ }
+
+ } else {
+ WARN_PRINT_ONCE("Jiggle modifier: You cannot detect colliders without the stack mode being set to _physics_process!");
+ }
+ }
+
+ // Get the forward direction that the basis is facing in right now.
+ stack->skeleton->update_bone_rest_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(jiggle_data_chain[p_joint_idx].bone_idx);
+
+ // Rotate the bone using the dynamic position!
+ new_bone_trans.basis.rotate_to_align(forward_vector, new_bone_trans.origin.direction_to(jiggle_data_chain[p_joint_idx].dynamic_position));
+
+ // Roll
+ new_bone_trans.basis.rotate_local(forward_vector, jiggle_data_chain[p_joint_idx].roll);
+
+ new_bone_trans = stack->skeleton->global_pose_to_local_pose(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans);
+ stack->skeleton->set_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(jiggle_data_chain[p_joint_idx].bone_idx);
+}
+
+void SkeletonModification3DJiggle::_update_jiggle_joint_data() {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ if (!jiggle_data_chain[i].override_defaults) {
+ set_jiggle_joint_stiffness(i, stiffness);
+ set_jiggle_joint_mass(i, mass);
+ set_jiggle_joint_damping(i, damping);
+ set_jiggle_joint_use_gravity(i, use_gravity);
+ set_jiggle_joint_gravity(i, gravity);
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack) {
+ is_setup = true;
+ execution_error_found = false;
+
+ if (stack->skeleton) {
+ for (uint32_t i = 0; i < jiggle_data_chain.size(); i++) {
+ int bone_idx = jiggle_data_chain[i].bone_idx;
+ if (bone_idx > 0 && bone_idx < stack->skeleton->get_bone_count()) {
+ jiggle_data_chain[i].dynamic_position = stack->skeleton->local_pose_to_global_pose(bone_idx, stack->skeleton->get_bone_local_pose_override(bone_idx)).origin;
+ }
+ }
+ }
+
+ update_cache();
+ }
+}
+
+void SkeletonModification3DJiggle::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DJiggle::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DJiggle::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DJiggle::set_stiffness(real_t p_stiffness) {
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ stiffness = p_stiffness;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_stiffness() const {
+ return stiffness;
+}
+
+void SkeletonModification3DJiggle::set_mass(real_t p_mass) {
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ mass = p_mass;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_mass() const {
+ return mass;
+}
+
+void SkeletonModification3DJiggle::set_damping(real_t p_damping) {
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_COND_MSG(p_damping > 1, "Damping cannot be more than one!");
+ damping = p_damping;
+ _update_jiggle_joint_data();
+}
+
+real_t SkeletonModification3DJiggle::get_damping() const {
+ return damping;
+}
+
+void SkeletonModification3DJiggle::set_use_gravity(bool p_use_gravity) {
+ use_gravity = p_use_gravity;
+ _update_jiggle_joint_data();
+}
+
+bool SkeletonModification3DJiggle::get_use_gravity() const {
+ return use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_gravity(Vector3 p_gravity) {
+ gravity = p_gravity;
+ _update_jiggle_joint_data();
+}
+
+Vector3 SkeletonModification3DJiggle::get_gravity() const {
+ return gravity;
+}
+
+void SkeletonModification3DJiggle::set_use_colliders(bool p_use_collider) {
+ use_colliders = p_use_collider;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_use_colliders() const {
+ return use_colliders;
+}
+
+void SkeletonModification3DJiggle::set_collision_mask(int p_mask) {
+ collision_mask = p_mask;
+}
+
+int SkeletonModification3DJiggle::get_collision_mask() const {
+ return collision_mask;
+}
+
+// Jiggle joint data functions
+int SkeletonModification3DJiggle::get_jiggle_data_chain_length() {
+ return jiggle_data_chain.size();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_data_chain_length(int p_length) {
+ ERR_FAIL_COND(p_length < 0);
+ jiggle_data_chain.resize(p_length);
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_name(int p_joint_idx, String p_name) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+
+ jiggle_data_chain[p_joint_idx].bone_name = p_name;
+ if (stack && stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_idx = stack->skeleton->find_bone(p_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DJiggle::get_jiggle_joint_bone_name(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, "");
+ return jiggle_data_chain[p_joint_idx].bone_name;
+}
+
+int SkeletonModification3DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].bone_idx;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ jiggle_data_chain[p_joint_idx].bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ jiggle_data_chain[p_joint_idx].bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].override_defaults = p_override;
+ _update_jiggle_joint_data();
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_override(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].override_defaults;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].stiffness = p_stiffness;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].stiffness;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_mass(int p_joint_idx, real_t p_mass) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].mass = p_mass;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_mass(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].mass;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_damping(int p_joint_idx, real_t p_damping) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!");
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].damping = p_damping;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_damping(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, -1);
+ return jiggle_data_chain[p_joint_idx].damping;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].use_gravity = p_use_gravity;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, false);
+ return jiggle_data_chain[p_joint_idx].use_gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].gravity = p_gravity;
+}
+
+Vector3 SkeletonModification3DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, Vector3(0, 0, 0));
+ return jiggle_data_chain[p_joint_idx].gravity;
+}
+
+void SkeletonModification3DJiggle::set_jiggle_joint_roll(int p_joint_idx, real_t p_roll) {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX(p_joint_idx, bone_chain_size);
+ jiggle_data_chain[p_joint_idx].roll = p_roll;
+}
+
+real_t SkeletonModification3DJiggle::get_jiggle_joint_roll(int p_joint_idx) const {
+ const int bone_chain_size = jiggle_data_chain.size();
+ ERR_FAIL_INDEX_V(p_joint_idx, bone_chain_size, 0.0);
+ return jiggle_data_chain[p_joint_idx].roll;
+}
+
+void SkeletonModification3DJiggle::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DJiggle::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DJiggle::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification3DJiggle::set_jiggle_data_chain_length);
+ ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification3DJiggle::get_jiggle_data_chain_length);
+
+ ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification3DJiggle::set_stiffness);
+ ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification3DJiggle::get_stiffness);
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification3DJiggle::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification3DJiggle::get_mass);
+ ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification3DJiggle::set_damping);
+ ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification3DJiggle::get_damping);
+ ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification3DJiggle::set_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification3DJiggle::get_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification3DJiggle::set_gravity);
+ ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification3DJiggle::get_gravity);
+
+ ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification3DJiggle::set_use_colliders);
+ ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification3DJiggle::get_use_colliders);
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &SkeletonModification3DJiggle::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification3DJiggle::get_collision_mask);
+
+ // Jiggle joint data functions
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_name", "joint_idx", "name"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_name", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_name);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification3DJiggle::set_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_bone_index);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification3DJiggle::set_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_override);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification3DJiggle::set_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_stiffness);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification3DJiggle::set_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_mass);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification3DJiggle::set_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_damping);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_use_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification3DJiggle::set_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_gravity);
+ ClassDB::bind_method(D_METHOD("set_jiggle_joint_roll", "joint_idx", "roll"), &SkeletonModification3DJiggle::set_jiggle_joint_roll);
+ ClassDB::bind_method(D_METHOD("get_jiggle_joint_roll", "joint_idx"), &SkeletonModification3DJiggle::get_jiggle_joint_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "jiggle_data_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_jiggle_data_chain_length", "get_jiggle_data_chain_length");
+ ADD_GROUP("Default Joint Settings", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stiffness"), "set_stiffness", "get_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0, 1, 0.01"), "set_damping", "get_damping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_gravity"), "set_use_gravity", "get_use_gravity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DJiggle::SkeletonModification3DJiggle() {
+ stack = nullptr;
+ is_setup = false;
+ jiggle_data_chain = Vector<Jiggle_Joint_Data>();
+ stiffness = 3;
+ mass = 0.75;
+ damping = 0.75;
+ use_gravity = false;
+ gravity = Vector3(0, -6.0, 0);
+ enabled = true;
+}
+
+SkeletonModification3DJiggle::~SkeletonModification3DJiggle() {
+}
diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h
new file mode 100644
index 0000000000..c210c8fa73
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_jiggle.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* skeleton_modification_3d_jiggle.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DJIGGLE_H
+#define SKELETONMODIFICATION3DJIGGLE_H
+
+class SkeletonModification3DJiggle : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D);
+
+private:
+ struct Jiggle_Joint_Data {
+ String bone_name = "";
+ int bone_idx = -1;
+
+ bool override_defaults = false;
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+ real_t roll = 0;
+
+ Vector3 cached_rotation = Vector3(0, 0, 0);
+ Vector3 force = Vector3(0, 0, 0);
+ Vector3 acceleration = Vector3(0, 0, 0);
+ Vector3 velocity = Vector3(0, 0, 0);
+ Vector3 last_position = Vector3(0, 0, 0);
+ Vector3 dynamic_position = Vector3(0, 0, 0);
+
+ Vector3 last_noncollision_position = Vector3(0, 0, 0);
+ };
+
+ NodePath target_node;
+ ObjectID target_node_cache;
+ LocalVector<Jiggle_Joint_Data> jiggle_data_chain;
+
+ real_t stiffness = 3;
+ real_t mass = 0.75;
+ real_t damping = 0.75;
+ bool use_gravity = false;
+ Vector3 gravity = Vector3(0, -6.0, 0);
+
+ bool use_colliders = false;
+ uint32_t collision_mask = 1;
+
+ void update_cache();
+ void _execute_jiggle_joint(int p_joint_idx, Node3D *p_target, real_t p_delta);
+ void _update_jiggle_joint_data();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_stiffness(real_t p_stiffness);
+ real_t get_stiffness() const;
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+ void set_damping(real_t p_damping);
+ real_t get_damping() const;
+
+ void set_use_gravity(bool p_use_gravity);
+ bool get_use_gravity() const;
+ void set_gravity(Vector3 p_gravity);
+ Vector3 get_gravity() const;
+
+ void set_use_colliders(bool p_use_colliders);
+ bool get_use_colliders() const;
+ void set_collision_mask(int p_mask);
+ int get_collision_mask() const;
+
+ int get_jiggle_data_chain_length();
+ void set_jiggle_data_chain_length(int p_new_length);
+
+ void set_jiggle_joint_bone_name(int p_joint_idx, String p_name);
+ String get_jiggle_joint_bone_name(int p_joint_idx) const;
+ void set_jiggle_joint_bone_index(int p_joint_idx, int p_idx);
+ int get_jiggle_joint_bone_index(int p_joint_idx) const;
+
+ void set_jiggle_joint_override(int p_joint_idx, bool p_override);
+ bool get_jiggle_joint_override(int p_joint_idx) const;
+ void set_jiggle_joint_stiffness(int p_joint_idx, real_t p_stiffness);
+ real_t get_jiggle_joint_stiffness(int p_joint_idx) const;
+ void set_jiggle_joint_mass(int p_joint_idx, real_t p_mass);
+ real_t get_jiggle_joint_mass(int p_joint_idx) const;
+ void set_jiggle_joint_damping(int p_joint_idx, real_t p_damping);
+ real_t get_jiggle_joint_damping(int p_joint_idx) const;
+ void set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity);
+ bool get_jiggle_joint_use_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_gravity(int p_joint_idx, Vector3 p_gravity);
+ Vector3 get_jiggle_joint_gravity(int p_joint_idx) const;
+ void set_jiggle_joint_roll(int p_joint_idx, real_t p_roll);
+ real_t get_jiggle_joint_roll(int p_joint_idx) const;
+
+ SkeletonModification3DJiggle();
+ ~SkeletonModification3DJiggle();
+};
+
+#endif //SKELETONMODIFICATION3DJIGGLE_H
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
new file mode 100644
index 0000000000..afdb077e71
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_lookat.cpp
@@ -0,0 +1,265 @@
+/*************************************************************************/
+/* skeleton_modification_3d_lookat.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_lookat.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DLookAt::_set(const StringName &p_path, const Variant &p_value) {
+ if (p_path == "lock_rotation_to_plane") {
+ set_lock_rotation_to_plane(p_value);
+ } else if (p_path == "lock_rotation_plane") {
+ set_lock_rotation_plane(p_value);
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = p_value;
+ tmp.x = Math::deg2rad(tmp.x);
+ tmp.y = Math::deg2rad(tmp.y);
+ tmp.z = Math::deg2rad(tmp.z);
+ set_additional_rotation(tmp);
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DLookAt::_get(const StringName &p_path, Variant &r_ret) const {
+ if (p_path == "lock_rotation_to_plane") {
+ r_ret = get_lock_rotation_to_plane();
+ } else if (p_path == "lock_rotation_plane") {
+ r_ret = get_lock_rotation_plane();
+ } else if (p_path == "additional_rotation") {
+ Vector3 tmp = get_additional_rotation();
+ tmp.x = Math::rad2deg(tmp.x);
+ tmp.y = Math::rad2deg(tmp.y);
+ tmp.z = Math::rad2deg(tmp.z);
+ r_ret = tmp;
+ }
+
+ return true;
+}
+
+void SkeletonModification3DLookAt::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "lock_rotation_to_plane", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (lock_rotation_to_plane) {
+ p_list->push_back(PropertyInfo(Variant::INT, "lock_rotation_plane", PROPERTY_HINT_ENUM, "X plane, Y plane, Z plane", PROPERTY_USAGE_DEFAULT));
+ }
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DLookAt::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+ if (!enabled) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache();
+ return;
+ }
+
+ if (bone_idx <= -2) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) {
+ return;
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx);
+ Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin;
+
+ // Lock the rotation to a plane relative to the bone by changing the target position
+ if (lock_rotation_to_plane) {
+ if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_X) {
+ target_pos.x = new_bone_trans.origin.x;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Y) {
+ target_pos.y = new_bone_trans.origin.y;
+ } else if (lock_rotation_plane == ROTATION_PLANE::ROTATION_PLANE_Z) {
+ target_pos.z = new_bone_trans.origin.z;
+ }
+ }
+
+ // Look at the target!
+ new_bone_trans = new_bone_trans.looking_at(target_pos, Vector3(0, 1, 0));
+ // Convert from Z-forward to whatever direction the bone faces.
+ stack->skeleton->update_bone_rest_forward_vector(bone_idx);
+ new_bone_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(bone_idx, new_bone_trans.basis);
+
+ // Apply additional rotation
+ new_bone_trans.basis.rotate_local(Vector3(1, 0, 0), additional_rotation.x);
+ new_bone_trans.basis.rotate_local(Vector3(0, 1, 0), additional_rotation.y);
+ new_bone_trans.basis.rotate_local(Vector3(0, 0, 1), additional_rotation.z);
+
+ stack->skeleton->set_bone_local_pose_override(bone_idx, new_bone_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(bone_idx);
+
+ // If we completed it successfully, then we can set execution_error_found to false
+ execution_error_found = false;
+}
+
+void SkeletonModification3DLookAt::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache();
+ }
+}
+
+void SkeletonModification3DLookAt::set_bone_name(String p_name) {
+ bone_name = p_name;
+ if (stack) {
+ if (stack->skeleton) {
+ bone_idx = stack->skeleton->find_bone(bone_name);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DLookAt::get_bone_name() const {
+ return bone_name;
+}
+
+int SkeletonModification3DLookAt::get_bone_index() const {
+ return bone_idx;
+}
+
+void SkeletonModification3DLookAt::set_bone_index(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+ bone_idx = p_bone_idx;
+
+ if (stack) {
+ if (stack->skeleton) {
+ bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+void SkeletonModification3DLookAt::update_cache() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Node is not in the scene tree!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DLookAt::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache();
+}
+
+NodePath SkeletonModification3DLookAt::get_target_node() const {
+ return target_node;
+}
+
+Vector3 SkeletonModification3DLookAt::get_additional_rotation() const {
+ return additional_rotation;
+}
+
+void SkeletonModification3DLookAt::set_additional_rotation(Vector3 p_offset) {
+ additional_rotation = p_offset;
+}
+
+bool SkeletonModification3DLookAt::get_lock_rotation_to_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_to_plane(bool p_lock_rotation) {
+ lock_rotation_to_plane = p_lock_rotation;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DLookAt::get_lock_rotation_plane() const {
+ return lock_rotation_plane;
+}
+
+void SkeletonModification3DLookAt::set_lock_rotation_plane(int p_plane) {
+ lock_rotation_plane = p_plane;
+}
+
+void SkeletonModification3DLookAt::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_bone_name", "name"), &SkeletonModification3DLookAt::set_bone_name);
+ ClassDB::bind_method(D_METHOD("get_bone_name"), &SkeletonModification3DLookAt::get_bone_name);
+
+ ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification3DLookAt::set_bone_index);
+ ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification3DLookAt::get_bone_index);
+
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DLookAt::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DLookAt::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_additional_rotation", "additional_rotation"), &SkeletonModification3DLookAt::set_additional_rotation);
+ ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification3DLookAt::get_additional_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_to_plane", "lock_to_plane"), &SkeletonModification3DLookAt::set_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_to_plane"), &SkeletonModification3DLookAt::get_lock_rotation_to_plane);
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_plane", "plane"), &SkeletonModification3DLookAt::set_lock_rotation_plane);
+ ClassDB::bind_method(D_METHOD("get_lock_rotation_plane"), &SkeletonModification3DLookAt::get_lock_rotation_plane);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+}
+
+SkeletonModification3DLookAt::SkeletonModification3DLookAt() {
+ stack = nullptr;
+ is_setup = false;
+ bone_name = "";
+ bone_idx = -2;
+ additional_rotation = Vector3();
+ lock_rotation_to_plane = false;
+ enabled = true;
+}
+
+SkeletonModification3DLookAt::~SkeletonModification3DLookAt() {
+}
diff --git a/scene/resources/ray_shape_3d.cpp b/scene/resources/skeleton_modification_3d_lookat.h
index 5446b4daab..5971e3f647 100644
--- a/scene/resources/ray_shape_3d.cpp
+++ b/scene/resources/skeleton_modification_3d_lookat.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* ray_shape_3d.cpp */
+/* skeleton_modification_3d_lookat.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,64 +28,62 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "ray_shape_3d.h"
-
-#include "servers/physics_server_3d.h"
-
-Vector<Vector3> RayShape3D::get_debug_mesh_lines() const {
- Vector<Vector3> points;
- points.push_back(Vector3());
- points.push_back(Vector3(0, 0, get_length()));
-
- return points;
-}
-
-real_t RayShape3D::get_enclosing_radius() const {
- return length;
-}
-
-void RayShape3D::_update_shape() {
- Dictionary d;
- d["length"] = length;
- d["slips_on_slope"] = slips_on_slope;
- PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), d);
- Shape3D::_update_shape();
-}
-
-void RayShape3D::set_length(float p_length) {
- length = p_length;
- _update_shape();
- notify_change_to_owners();
-}
-
-float RayShape3D::get_length() const {
- return length;
-}
-
-void RayShape3D::set_slips_on_slope(bool p_active) {
- slips_on_slope = p_active;
- _update_shape();
- notify_change_to_owners();
-}
-
-bool RayShape3D::get_slips_on_slope() const {
- return slips_on_slope;
-}
-
-void RayShape3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape3D::set_length);
- ClassDB::bind_method(D_METHOD("get_length"), &RayShape3D::get_length);
-
- ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape3D::set_slips_on_slope);
- ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape3D::get_slips_on_slope);
-
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope");
-}
-
-RayShape3D::RayShape3D() :
- Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_RAY)) {
- /* Code copied from setters to prevent the use of uninitialized variables */
- _update_shape();
- notify_change_to_owners();
-}
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DLOOKAT_H
+#define SKELETONMODIFICATION3DLOOKAT_H
+
+class SkeletonModification3DLookAt : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D);
+
+private:
+ String bone_name = "";
+ int bone_idx = -1;
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ Vector3 additional_rotation = Vector3(1, 0, 0);
+ bool lock_rotation_to_plane = false;
+ int lock_rotation_plane = ROTATION_PLANE_X;
+
+ void update_cache();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ enum ROTATION_PLANE {
+ ROTATION_PLANE_X,
+ ROTATION_PLANE_Y,
+ ROTATION_PLANE_Z
+ };
+
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_bone_name(String p_name);
+ String get_bone_name() const;
+
+ void set_bone_index(int p_idx);
+ int get_bone_index() const;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_additional_rotation(Vector3 p_offset);
+ Vector3 get_additional_rotation() const;
+
+ void set_lock_rotation_to_plane(bool p_lock_to_plane);
+ bool get_lock_rotation_to_plane() const;
+ void set_lock_rotation_plane(int p_plane);
+ int get_lock_rotation_plane() const;
+
+ SkeletonModification3DLookAt();
+ ~SkeletonModification3DLookAt();
+};
+
+#endif //SKELETONMODIFICATION3DLOOKAT_H
diff --git a/scene/resources/skeleton_modification_3d_stackholder.cpp b/scene/resources/skeleton_modification_3d_stackholder.cpp
new file mode 100644
index 0000000000..56035a4def
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_stackholder.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* skeleton_modification_3d_stackholder.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_stackholder.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DStackHolder::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ set_held_modification_stack(p_value);
+ }
+ return true;
+}
+
+bool SkeletonModification3DStackHolder::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "held_modification_stack") {
+ r_ret = get_held_modification_stack();
+ }
+ return true;
+}
+
+void SkeletonModification3DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+}
+
+void SkeletonModification3DStackHolder::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->execute(p_delta, execution_mode);
+ }
+}
+
+void SkeletonModification3DStackHolder::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+
+ if (held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+ }
+}
+
+void SkeletonModification3DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack) {
+ held_modification_stack = p_held_stack;
+
+ if (is_setup && held_modification_stack.is_valid()) {
+ held_modification_stack->set_skeleton(stack->get_skeleton());
+ held_modification_stack->setup();
+ }
+}
+
+Ref<SkeletonModificationStack3D> SkeletonModification3DStackHolder::get_held_modification_stack() const {
+ return held_modification_stack;
+}
+
+void SkeletonModification3DStackHolder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification3DStackHolder::set_held_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification3DStackHolder::get_held_modification_stack);
+}
+
+SkeletonModification3DStackHolder::SkeletonModification3DStackHolder() {
+ stack = nullptr;
+ is_setup = false;
+ enabled = true;
+}
+
+SkeletonModification3DStackHolder::~SkeletonModification3DStackHolder() {
+}
diff --git a/scene/resources/ray_shape_2d.h b/scene/resources/skeleton_modification_3d_stackholder.h
index 56ecfa2722..c765cd8de3 100644
--- a/scene/resources/ray_shape_2d.h
+++ b/scene/resources/skeleton_modification_3d_stackholder.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* ray_shape_2d.h */
+/* skeleton_modification_3d_stackholder.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,34 +28,32 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RAY_SHAPE_2D_H
-#define RAY_SHAPE_2D_H
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
-#include "scene/resources/shape_2d.h"
+#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H
+#define SKELETONMODIFICATION3DSTACKHOLDER_H
-class RayShape2D : public Shape2D {
- GDCLASS(RayShape2D, Shape2D);
-
- real_t length = 20.0;
- bool slips_on_slope = false;
-
- void _update_shape();
+class SkeletonModification3DStackHolder : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D);
protected:
static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
public:
- void set_length(real_t p_length);
- real_t get_length() const;
+ Ref<SkeletonModificationStack3D> held_modification_stack;
- void set_slips_on_slope(bool p_active);
- bool get_slips_on_slope() const;
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
- virtual void draw(const RID &p_to_rid, const Color &p_color) override;
- virtual Rect2 get_rect() const override;
- virtual real_t get_enclosing_radius() const override;
+ void set_held_modification_stack(Ref<SkeletonModificationStack3D> p_held_stack);
+ Ref<SkeletonModificationStack3D> get_held_modification_stack() const;
- RayShape2D();
+ SkeletonModification3DStackHolder();
+ ~SkeletonModification3DStackHolder();
};
-#endif // RAY_SHAPE_2D_H
+#endif //SKELETONMODIFICATION3DSTACKHOLDER_H
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
new file mode 100644
index 0000000000..ae7a3bab7e
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -0,0 +1,599 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/resources/skeleton_modification_3d_twoboneik.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+bool SkeletonModification3DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ set_use_tip_node(p_value);
+ } else if (path == "tip_node") {
+ set_tip_node(p_value);
+ } else if (path == "auto_calculate_joint_length") {
+ set_auto_calculate_joint_length(p_value);
+ } else if (path == "use_pole_node") {
+ set_use_pole_node(p_value);
+ } else if (path == "pole_node") {
+ set_pole_node(p_value);
+ } else if (path == "joint_one_length") {
+ set_joint_one_length(p_value);
+ } else if (path == "joint_two_length") {
+ set_joint_two_length(p_value);
+ } else if (path == "joint_one/bone_name") {
+ set_joint_one_bone_name(p_value);
+ } else if (path == "joint_one/bone_idx") {
+ set_joint_one_bone_idx(p_value);
+ } else if (path == "joint_one/roll") {
+ set_joint_one_roll(Math::deg2rad(real_t(p_value)));
+ } else if (path == "joint_two/bone_name") {
+ set_joint_two_bone_name(p_value);
+ } else if (path == "joint_two/bone_idx") {
+ set_joint_two_bone_idx(p_value);
+ } else if (path == "joint_two/roll") {
+ set_joint_two_roll(Math::deg2rad(real_t(p_value)));
+ }
+
+ return true;
+}
+
+bool SkeletonModification3DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path == "use_tip_node") {
+ r_ret = get_use_tip_node();
+ } else if (path == "tip_node") {
+ r_ret = get_tip_node();
+ } else if (path == "auto_calculate_joint_length") {
+ r_ret = get_auto_calculate_joint_length();
+ } else if (path == "use_pole_node") {
+ r_ret = get_use_pole_node();
+ } else if (path == "pole_node") {
+ r_ret = get_pole_node();
+ } else if (path == "joint_one_length") {
+ r_ret = get_joint_one_length();
+ } else if (path == "joint_two_length") {
+ r_ret = get_joint_two_length();
+ } else if (path == "joint_one/bone_name") {
+ r_ret = get_joint_one_bone_name();
+ } else if (path == "joint_one/bone_idx") {
+ r_ret = get_joint_one_bone_idx();
+ } else if (path == "joint_one/roll") {
+ r_ret = Math::rad2deg(get_joint_one_roll());
+ } else if (path == "joint_two/bone_name") {
+ r_ret = get_joint_two_bone_name();
+ } else if (path == "joint_two/bone_idx") {
+ r_ret = get_joint_two_bone_idx();
+ } else if (path == "joint_two/roll") {
+ r_ret = Math::rad2deg(get_joint_two_roll());
+ }
+
+ return true;
+}
+
+void SkeletonModification3DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_tip_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_tip_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tip_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_joint_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (!auto_calculate_joint_length) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two_length", PROPERTY_HINT_RANGE, "-1, 10000, 0.001", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_pole_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (use_pole_node) {
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "pole_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D", PROPERTY_USAGE_DEFAULT));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_one/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_one/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_one/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "joint_two/bone_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "joint_two/bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_two/roll", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+}
+
+void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
+ ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot execute!");
+
+ if (!enabled) {
+ return;
+ }
+
+ if (_print_execution_error(joint_one_bone_idx < 0 || joint_two_bone_idx < 0,
+ "One (or more) of the bones in the modification have invalid bone indexes. Cannot execute modification!")) {
+ return;
+ }
+
+ if (target_node_cache.is_null()) {
+ _print_execution_error(true, "Target cache is out of date. Attempting to update...");
+ update_cache_target();
+ return;
+ }
+
+ // Update joint lengths (if needed)
+ if (auto_calculate_joint_length && (joint_one_length < 0 || joint_two_length < 0)) {
+ calculate_joint_lengths();
+ }
+
+ // Adopted from the links below:
+ // http://theorangeduck.com/page/simple-two-joint
+ // https://www.alanzucconi.com/2018/05/02/ik-2d-2/
+ // With modifications by TwistedTwigleg
+ Node3D *target = Object::cast_to<Node3D>(ObjectDB::get_instance(target_node_cache));
+ if (_print_execution_error(!target || !target->is_inside_tree(), "Target node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D target_trans = stack->skeleton->world_transform_to_global_pose(target->get_global_transform());
+
+ Transform3D bone_one_trans;
+ Transform3D bone_two_trans;
+
+ // Make the first joint look at the pole, and the second look at the target. That way, the
+ // TwoBoneIK solver has to really only handle extension/contraction, which should make it align with the pole.
+ if (use_pole_node) {
+ if (pole_node_cache.is_null()) {
+ _print_execution_error(true, "Pole cache is out of date. Attempting to update...");
+ update_cache_pole();
+ return;
+ }
+
+ Node3D *pole = Object::cast_to<Node3D>(ObjectDB::get_instance(pole_node_cache));
+ if (_print_execution_error(!pole || !pole->is_inside_tree(), "Pole node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform());
+
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0));
+ bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0));
+ bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis);
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ }
+
+ Transform3D bone_two_tip_trans;
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ _print_execution_error(true, "Tip cache is out of date. Attempting to update...");
+ update_cache_tip();
+ return;
+ }
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (_print_execution_error(!tip || !tip->is_inside_tree(), "Tip node is not in the scene tree. Cannot execute modification!")) {
+ return;
+ }
+ bone_two_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ } else {
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_tip_trans = bone_two_trans;
+ bone_two_tip_trans.origin += bone_two_trans.basis.xform(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx)).normalized() * joint_two_length;
+ }
+
+ real_t joint_one_to_target_length = bone_one_trans.origin.distance_to(target_trans.origin);
+ if (joint_one_length + joint_two_length < joint_one_to_target_length) {
+ // Set the target *just* out of reach to straighten the bones
+ joint_one_to_target_length = joint_one_length + joint_two_length + 0.01;
+ } else if (joint_one_to_target_length < joint_one_length) {
+ // Place the target in reach so the solver doesn't do crazy things
+ joint_one_to_target_length = joint_one_length;
+ }
+
+ // Get the square lengths for all three sides of the triangle we'll use to calculate the angles
+ real_t sqr_one_length = joint_one_length * joint_one_length;
+ real_t sqr_two_length = joint_two_length * joint_two_length;
+ real_t sqr_three_length = joint_one_to_target_length * joint_one_to_target_length;
+
+ // Calculate the angles for the first joint using the law of cosigns
+ real_t ac_ab_0 = Math::acos(CLAMP(bone_two_tip_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_one_trans.origin)), -1, 1));
+ real_t ac_at_0 = Math::acos(CLAMP(bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).dot(bone_one_trans.origin.direction_to(target_trans.origin)), -1, 1));
+ real_t ac_ab_1 = Math::acos(CLAMP((sqr_two_length - sqr_one_length - sqr_three_length) / (-2.0 * joint_one_length * joint_one_to_target_length), -1, 1));
+
+ // Calculate the angles of rotation. Angle 0 is the extension/contraction axis, while angle 1 is the rotation axis to align the triangle to the target
+ Vector3 axis_0 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(bone_two_trans.origin));
+ Vector3 axis_1 = bone_one_trans.origin.direction_to(bone_two_tip_trans.origin).cross(bone_one_trans.origin.direction_to(target_trans.origin));
+
+ // Make a quaternion with the delta rotation needed to rotate the first joint into alignment and apply it to the transform.
+ Quaternion bone_one_quat = bone_one_trans.basis.get_rotation_quaternion();
+ Quaternion rot_0 = Quaternion(bone_one_quat.inverse().xform(axis_0).normalized(), (ac_ab_1 - ac_ab_0));
+ Quaternion rot_2 = Quaternion(bone_one_quat.inverse().xform(axis_1).normalized(), ac_at_0);
+ bone_one_trans.basis.set_quaternion(bone_one_quat * (rot_0 * rot_2));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
+ bone_one_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_one_bone_idx), joint_one_roll);
+
+ // Apply the rotation to the first joint
+ bone_one_trans = stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans);
+ bone_one_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, bone_one_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
+
+ if (use_pole_node) {
+ // Update bone_two_trans so its at the latest position, with the rotation of bone_one_trans taken into account, then look at the target.
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ Vector3 forward_vector = stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_to_align(forward_vector, bone_two_trans.origin.direction_to(target_trans.origin));
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ } else {
+ // Calculate the angles for the second joint using the law of cosigns, make a quaternion with the delta rotation needed to rotate the joint into
+ // alignment, and then apply it to the second joint.
+ real_t ba_bc_0 = Math::acos(CLAMP(bone_two_trans.origin.direction_to(bone_one_trans.origin).dot(bone_two_trans.origin.direction_to(bone_two_tip_trans.origin)), -1, 1));
+ real_t ba_bc_1 = Math::acos(CLAMP((sqr_three_length - sqr_one_length - sqr_two_length) / (-2.0 * joint_one_length * joint_two_length), -1, 1));
+ Quaternion bone_two_quat = bone_two_trans.basis.get_rotation_quaternion();
+ Quaternion rot_1 = Quaternion(bone_two_quat.inverse().xform(axis_0).normalized(), (ba_bc_1 - ba_bc_0));
+ bone_two_trans.basis.set_quaternion(bone_two_quat * rot_1);
+
+ stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
+ bone_two_trans.basis.rotate_local(stack->skeleton->get_bone_axis_forward_vector(joint_two_bone_idx), joint_two_roll);
+
+ bone_two_trans = stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans);
+ bone_two_trans.origin = Vector3(0, 0, 0);
+ stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, bone_two_trans, stack->strength, true);
+ stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::_setup_modification(SkeletonModificationStack3D *p_stack) {
+ stack = p_stack;
+
+ if (stack != nullptr) {
+ is_setup = true;
+ execution_error_found = false;
+ update_cache_target();
+ update_cache_tip();
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_target() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update target cache: modification is not properly setup!");
+ return;
+ }
+
+ target_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree() && target_node.is_empty() == false) {
+ if (stack->skeleton->has_node(target_node)) {
+ Node *node = stack->skeleton->get_node(target_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update target cache: Target node is this modification's skeleton or cannot be found. Cannot execute modification");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update target cache: Target node is not in the scene tree. Cannot execute modification!");
+ target_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_tip() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update tip cache: modification is not properly setup!");
+ return;
+ }
+
+ tip_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(tip_node)) {
+ Node *node = stack->skeleton->get_node(tip_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update tip cache: Tip node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update tip cache: Tip node is not in the scene tree. Cannot execute modification!");
+ tip_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::update_cache_pole() {
+ if (!is_setup || !stack) {
+ _print_execution_error(true, "Cannot update pole cache: modification is not properly setup!");
+ return;
+ }
+
+ pole_node_cache = ObjectID();
+ if (stack->skeleton) {
+ if (stack->skeleton->is_inside_tree()) {
+ if (stack->skeleton->has_node(pole_node)) {
+ Node *node = stack->skeleton->get_node(pole_node);
+ ERR_FAIL_COND_MSG(!node || stack->skeleton == node,
+ "Cannot update pole cache: Pole node is this modification's skeleton or cannot be found!");
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(),
+ "Cannot update pole cache: Pole node is not in the scene tree. Cannot execute modification!");
+ pole_node_cache = node->get_instance_id();
+
+ execution_error_found = false;
+ }
+ }
+ }
+}
+
+void SkeletonModification3DTwoBoneIK::set_target_node(const NodePath &p_target_node) {
+ target_node = p_target_node;
+ update_cache_target();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_target_node() const {
+ return target_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_tip_node(const bool p_use_tip_node) {
+ use_tip_node = p_use_tip_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_tip_node() const {
+ return use_tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_tip_node(const NodePath &p_tip_node) {
+ tip_node = p_tip_node;
+ update_cache_tip();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_tip_node() const {
+ return tip_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_use_pole_node(const bool p_use_pole_node) {
+ use_pole_node = p_use_pole_node;
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_use_pole_node() const {
+ return use_pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_pole_node(const NodePath &p_pole_node) {
+ pole_node = p_pole_node;
+ update_cache_pole();
+}
+
+NodePath SkeletonModification3DTwoBoneIK::get_pole_node() const {
+ return pole_node;
+}
+
+void SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length(bool p_calculate) {
+ auto_calculate_joint_length = p_calculate;
+ if (p_calculate) {
+ calculate_joint_lengths();
+ }
+ notify_property_list_changed();
+}
+
+bool SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length() const {
+ return auto_calculate_joint_length;
+}
+
+void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
+ if (!is_setup) {
+ return; // fail silently, as we likely just loaded the scene.
+ }
+ ERR_FAIL_COND_MSG(!stack || stack->skeleton == nullptr,
+ "Modification is not setup and therefore cannot calculate joint lengths!");
+ ERR_FAIL_COND_MSG(joint_one_bone_idx <= -1 || joint_two_bone_idx <= -1,
+ "One of the bones in the TwoBoneIK modification are not set! Cannot calculate joint lengths!");
+
+ Transform3D bone_one_rest_trans = stack->skeleton->get_bone_global_pose(joint_one_bone_idx);
+ Transform3D bone_two_rest_trans = stack->skeleton->get_bone_global_pose(joint_two_bone_idx);
+
+ joint_one_length = bone_one_rest_trans.origin.distance_to(bone_two_rest_trans.origin);
+
+ if (use_tip_node) {
+ if (tip_node_cache.is_null()) {
+ update_cache_tip();
+ WARN_PRINT("Tip cache is out of date. Updating...");
+ }
+
+ Node3D *tip = Object::cast_to<Node3D>(ObjectDB::get_instance(tip_node_cache));
+ if (tip) {
+ Transform3D bone_tip_trans = stack->skeleton->world_transform_to_global_pose(tip->get_global_transform());
+ joint_two_length = bone_two_rest_trans.origin.distance_to(bone_tip_trans.origin);
+ }
+ } else {
+ // Attempt to use children bones to get the length
+ Vector<int> bone_two_children = stack->skeleton->get_bone_children(joint_two_bone_idx);
+ if (bone_two_children.size() > 0) {
+ joint_two_length = 0;
+ for (int i = 0; i < bone_two_children.size(); i++) {
+ joint_two_length += bone_two_rest_trans.origin.distance_to(
+ stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin);
+ }
+ joint_two_length = joint_two_length / bone_two_children.size();
+ } else {
+ WARN_PRINT("TwoBoneIK modification: Cannot auto calculate length for joint 2! Auto setting the length to 1...");
+ joint_two_length = 1.0;
+ }
+ }
+ execution_error_found = false;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_name(String p_bone_name) {
+ joint_one_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_one_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_one_bone_name() const {
+ return joint_one_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) {
+ joint_one_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_one_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx() const {
+ return joint_one_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_length(real_t p_length) {
+ joint_one_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_length() const {
+ return joint_one_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_name(String p_bone_name) {
+ joint_two_bone_name = p_bone_name;
+ if (stack && stack->skeleton) {
+ joint_two_bone_idx = stack->skeleton->find_bone(p_bone_name);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+String SkeletonModification3DTwoBoneIK::get_joint_two_bone_name() const {
+ return joint_two_bone_name;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) {
+ joint_two_bone_idx = p_bone_idx;
+ if (stack && stack->skeleton) {
+ joint_two_bone_name = stack->skeleton->get_bone_name(p_bone_idx);
+ }
+ execution_error_found = false;
+ notify_property_list_changed();
+}
+
+int SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx() const {
+ return joint_two_bone_idx;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_length(real_t p_length) {
+ joint_two_length = p_length;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_length() const {
+ return joint_two_length;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_one_roll(real_t p_roll) {
+ joint_one_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_one_roll() const {
+ return joint_one_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::set_joint_two_roll(real_t p_roll) {
+ joint_two_roll = p_roll;
+}
+
+real_t SkeletonModification3DTwoBoneIK::get_joint_two_roll() const {
+ return joint_two_roll;
+}
+
+void SkeletonModification3DTwoBoneIK::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification3DTwoBoneIK::set_target_node);
+ ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification3DTwoBoneIK::get_target_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_pole_node", "use_pole_node"), &SkeletonModification3DTwoBoneIK::set_use_pole_node);
+ ClassDB::bind_method(D_METHOD("get_use_pole_node"), &SkeletonModification3DTwoBoneIK::get_use_pole_node);
+ ClassDB::bind_method(D_METHOD("set_pole_node", "pole_nodepath"), &SkeletonModification3DTwoBoneIK::set_pole_node);
+ ClassDB::bind_method(D_METHOD("get_pole_node"), &SkeletonModification3DTwoBoneIK::get_pole_node);
+
+ ClassDB::bind_method(D_METHOD("set_use_tip_node", "use_tip_node"), &SkeletonModification3DTwoBoneIK::set_use_tip_node);
+ ClassDB::bind_method(D_METHOD("get_use_tip_node"), &SkeletonModification3DTwoBoneIK::get_use_tip_node);
+ ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification3DTwoBoneIK::set_tip_node);
+ ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification3DTwoBoneIK::get_tip_node);
+
+ ClassDB::bind_method(D_METHOD("set_auto_calculate_joint_length", "auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::set_auto_calculate_joint_length);
+ ClassDB::bind_method(D_METHOD("get_auto_calculate_joint_length"), &SkeletonModification3DTwoBoneIK::get_auto_calculate_joint_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_one_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_one_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_one_length);
+ ClassDB::bind_method(D_METHOD("get_joint_one_length"), &SkeletonModification3DTwoBoneIK::get_joint_one_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_name", "bone_name"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_name"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_name);
+ ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification3DTwoBoneIK::set_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification3DTwoBoneIK::get_joint_two_bone_idx);
+ ClassDB::bind_method(D_METHOD("set_joint_two_length", "bone_length"), &SkeletonModification3DTwoBoneIK::set_joint_two_length);
+ ClassDB::bind_method(D_METHOD("get_joint_two_length"), &SkeletonModification3DTwoBoneIK::get_joint_two_length);
+
+ ClassDB::bind_method(D_METHOD("set_joint_one_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_one_roll"), &SkeletonModification3DTwoBoneIK::get_joint_one_roll);
+ ClassDB::bind_method(D_METHOD("set_joint_two_roll", "roll"), &SkeletonModification3DTwoBoneIK::set_joint_two_roll);
+ ClassDB::bind_method(D_METHOD("get_joint_two_roll"), &SkeletonModification3DTwoBoneIK::get_joint_two_roll);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_target_node", "get_target_node");
+ ADD_GROUP("", "");
+}
+
+SkeletonModification3DTwoBoneIK::SkeletonModification3DTwoBoneIK() {
+ stack = nullptr;
+ is_setup = false;
+}
+
+SkeletonModification3DTwoBoneIK::~SkeletonModification3DTwoBoneIK() {
+}
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h
new file mode 100644
index 0000000000..e62d6cc497
--- /dev/null
+++ b/scene/resources/skeleton_modification_3d_twoboneik.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* skeleton_modification_3d_twoboneik.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/skeleton_modification_3d.h"
+
+#ifndef SKELETONMODIFICATION3DTWOBONEIK_H
+#define SKELETONMODIFICATION3DTWOBONEIK_H
+
+class SkeletonModification3DTwoBoneIK : public SkeletonModification3D {
+ GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D);
+
+private:
+ NodePath target_node;
+ ObjectID target_node_cache;
+
+ bool use_tip_node = false;
+ NodePath tip_node;
+ ObjectID tip_node_cache;
+
+ bool use_pole_node = false;
+ NodePath pole_node;
+ ObjectID pole_node_cache;
+
+ String joint_one_bone_name = "";
+ int joint_one_bone_idx = -1;
+ String joint_two_bone_name = "";
+ int joint_two_bone_idx = -1;
+
+ bool auto_calculate_joint_length = false;
+ real_t joint_one_length = -1;
+ real_t joint_two_length = -1;
+
+ real_t joint_one_roll = 0;
+ real_t joint_two_roll = 0;
+
+ void update_cache_target();
+ void update_cache_tip();
+ void update_cache_pole();
+
+protected:
+ static void _bind_methods();
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ virtual void _execute(real_t p_delta) override;
+ virtual void _setup_modification(SkeletonModificationStack3D *p_stack) override;
+
+ void set_target_node(const NodePath &p_target_node);
+ NodePath get_target_node() const;
+
+ void set_use_tip_node(const bool p_use_tip_node);
+ bool get_use_tip_node() const;
+ void set_tip_node(const NodePath &p_tip_node);
+ NodePath get_tip_node() const;
+
+ void set_use_pole_node(const bool p_use_pole_node);
+ bool get_use_pole_node() const;
+ void set_pole_node(const NodePath &p_pole_node);
+ NodePath get_pole_node() const;
+
+ void set_auto_calculate_joint_length(bool p_calculate);
+ bool get_auto_calculate_joint_length() const;
+ void calculate_joint_lengths();
+
+ void set_joint_one_bone_name(String p_bone_name);
+ String get_joint_one_bone_name() const;
+ void set_joint_one_bone_idx(int p_bone_idx);
+ int get_joint_one_bone_idx() const;
+ void set_joint_one_length(real_t p_length);
+ real_t get_joint_one_length() const;
+
+ void set_joint_two_bone_name(String p_bone_name);
+ String get_joint_two_bone_name() const;
+ void set_joint_two_bone_idx(int p_bone_idx);
+ int get_joint_two_bone_idx() const;
+ void set_joint_two_length(real_t p_length);
+ real_t get_joint_two_length() const;
+
+ void set_joint_one_roll(real_t p_roll);
+ real_t get_joint_one_roll() const;
+ void set_joint_two_roll(real_t p_roll);
+ real_t get_joint_two_roll() const;
+
+ SkeletonModification3DTwoBoneIK();
+ ~SkeletonModification3DTwoBoneIK();
+};
+
+#endif //SKELETONMODIFICATION3DTWOBONEIK_H
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
new file mode 100644
index 0000000000..3fce0e5dbd
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -0,0 +1,222 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "skeleton_modification_stack_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+///////////////////////////////////////
+// ModificationStack3D
+///////////////////////////////////////
+
+void SkeletonModificationStack3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModification3D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ }
+}
+
+bool SkeletonModificationStack3D::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ set_modification(mod_idx, p_value);
+ return true;
+ }
+ return true;
+}
+
+bool SkeletonModificationStack3D::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("modifications/")) {
+ int mod_idx = path.get_slicec('/', 1).to_int();
+ r_ret = get_modification(mod_idx);
+ return true;
+ }
+ return true;
+}
+
+void SkeletonModificationStack3D::setup() {
+ if (is_setup) {
+ return;
+ }
+
+ if (skeleton != nullptr) {
+ is_setup = true;
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->_setup_modification(this);
+ }
+ } else {
+ WARN_PRINT("Cannot setup SkeletonModificationStack3D: no skeleton set!");
+ }
+}
+
+void SkeletonModificationStack3D::execute(real_t p_delta, int p_execution_mode) {
+ ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(),
+ "Modification stack is not properly setup and therefore cannot execute!");
+
+ if (!skeleton->is_inside_tree()) {
+ ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!");
+ return;
+ }
+
+ if (!enabled) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+
+ if (modifications[i]->get_execution_mode() == p_execution_mode) {
+ modifications[i]->_execute(p_delta);
+ }
+ }
+}
+
+void SkeletonModificationStack3D::enable_all_modifications(bool p_enabled) {
+ for (uint32_t i = 0; i < modifications.size(); i++) {
+ if (!modifications[i].is_valid()) {
+ continue;
+ }
+ modifications[i]->set_enabled(p_enabled);
+ }
+}
+
+Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_mod_idx) const {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX_V(p_mod_idx, modifications_size, nullptr);
+ return modifications[p_mod_idx];
+}
+
+void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
+ p_mod->_setup_modification(this);
+ modifications.push_back(p_mod);
+}
+
+void SkeletonModificationStack3D::delete_modification(int p_mod_idx) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+ modifications.remove(p_mod_idx);
+}
+
+void SkeletonModificationStack3D::set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod) {
+ const int modifications_size = modifications.size();
+ ERR_FAIL_INDEX(p_mod_idx, modifications_size);
+
+ if (p_mod == nullptr) {
+ modifications.remove(p_mod_idx);
+ } else {
+ p_mod->_setup_modification(this);
+ modifications[p_mod_idx] = p_mod;
+ }
+}
+
+void SkeletonModificationStack3D::set_modification_count(int p_count) {
+ modifications.resize(p_count);
+ notify_property_list_changed();
+}
+
+int SkeletonModificationStack3D::get_modification_count() const {
+ return modifications.size();
+}
+
+void SkeletonModificationStack3D::set_skeleton(Skeleton3D *p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+Skeleton3D *SkeletonModificationStack3D::get_skeleton() const {
+ return skeleton;
+}
+
+bool SkeletonModificationStack3D::get_is_setup() const {
+ return is_setup;
+}
+
+void SkeletonModificationStack3D::set_enabled(bool p_enabled) {
+ enabled = p_enabled;
+
+ if (!enabled && is_setup && skeleton != nullptr) {
+ skeleton->clear_bones_local_pose_override();
+ }
+}
+
+bool SkeletonModificationStack3D::get_enabled() const {
+ return enabled;
+}
+
+void SkeletonModificationStack3D::set_strength(real_t p_strength) {
+ ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!");
+ ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!");
+ strength = p_strength;
+}
+
+real_t SkeletonModificationStack3D::get_strength() const {
+ return strength;
+}
+
+void SkeletonModificationStack3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack3D::setup);
+ ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack3D::execute);
+
+ ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack3D::enable_all_modifications);
+ ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack3D::get_modification);
+ ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack3D::add_modification);
+ ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
+ ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
+
+ ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
+
+ ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
+
+ ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack3D::set_enabled);
+ ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack3D::get_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack3D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack3D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack3D::get_skeleton);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count");
+}
+
+SkeletonModificationStack3D::SkeletonModificationStack3D() {
+}
diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h
new file mode 100644
index 0000000000..cbc8d4e0b9
--- /dev/null
+++ b/scene/resources/skeleton_modification_stack_3d.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* skeleton_modification_stack_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SKELETONMODIFICATIONSTACK3D_H
+#define SKELETONMODIFICATIONSTACK3D_H
+
+#include "core/templates/local_vector.h"
+#include "scene/3d/skeleton_3d.h"
+
+class Skeleton3D;
+class SkeletonModification3D;
+
+class SkeletonModificationStack3D : public Resource {
+ GDCLASS(SkeletonModificationStack3D, Resource);
+ friend class Skeleton3D;
+ friend class SkeletonModification3D;
+
+protected:
+ static void _bind_methods();
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual bool _set(const StringName &p_path, const Variant &p_value);
+ virtual bool _get(const StringName &p_path, Variant &r_ret) const;
+
+public:
+ Skeleton3D *skeleton = nullptr;
+ bool is_setup = false;
+ bool enabled = false;
+ real_t strength = 1.0;
+
+ enum EXECUTION_MODE {
+ execution_mode_process,
+ execution_mode_physics_process,
+ };
+
+ LocalVector<Ref<SkeletonModification3D>> modifications = LocalVector<Ref<SkeletonModification3D>>();
+ int modifications_count = 0;
+
+ virtual void setup();
+ virtual void execute(real_t p_delta, int p_execution_mode);
+
+ void enable_all_modifications(bool p_enable);
+ Ref<SkeletonModification3D> get_modification(int p_mod_idx) const;
+ void add_modification(Ref<SkeletonModification3D> p_mod);
+ void delete_modification(int p_mod_idx);
+ void set_modification(int p_mod_idx, Ref<SkeletonModification3D> p_mod);
+
+ void set_modification_count(int p_count);
+ int get_modification_count() const;
+
+ void set_skeleton(Skeleton3D *p_skeleton);
+ Skeleton3D *get_skeleton() const;
+
+ bool get_is_setup() const;
+
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const;
+
+ void set_strength(real_t p_strength);
+ real_t get_strength() const;
+
+ SkeletonModificationStack3D();
+};
+
+#endif // SKELETONMODIFICATIONSTACK3D_H
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index ec00f9d7b7..39082b6f7a 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -30,6 +30,8 @@
#include "sky_material.h"
+#include "core/version.h"
+
Mutex ProceduralSkyMaterial::shader_mutex;
RID ProceduralSkyMaterial::shader;
@@ -204,7 +206,10 @@ void ProceduralSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s ProceduralSkyMaterial.
+
shader_type sky;
uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);
@@ -350,10 +355,13 @@ void PanoramaSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial.
+
shader_type sky;
-uniform sampler2D source_panorama : filter_linear;
+uniform sampler2D source_panorama : filter_linear, hint_albedo;
void sky() {
COLOR = texture(source_panorama, SKY_COORDS).rgb;
@@ -561,7 +569,10 @@ void PhysicalSkyMaterial::_update_shader() {
if (shader.is_null()) {
shader = RS::get_singleton()->shader_create();
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PhysicalSkyMaterial.
+
shader_type sky;
uniform float rayleigh : hint_range(0, 64) = 2.0;
@@ -576,7 +587,7 @@ uniform vec4 ground_color : hint_color = vec4(1.0);
uniform float exposure : hint_range(0, 128) = 0.1;
uniform float dither_strength : hint_range(0, 10) = 1.0;
-uniform sampler2D night_sky : hint_black;
+uniform sampler2D night_sky : hint_black_albedo;
const vec3 UP = vec3( 0.0, 1.0, 0.0 );
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index e9adf67559..140c6f821f 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -122,14 +122,14 @@ Vector<String> SpriteFrames::get_animation_names() const {
return names;
}
-void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) {
+void SpriteFrames::set_animation_speed(const StringName &p_anim, double p_fps) {
ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ").");
Map<StringName, Anim>::Element *E = animations.find(p_anim);
ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().speed = p_fps;
}
-float SpriteFrames::get_animation_speed(const StringName &p_anim) const {
+double SpriteFrames::get_animation_speed(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().speed;
diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h
index 282c5f20ab..fdfd6af5ab 100644
--- a/scene/resources/sprite_frames.h
+++ b/scene/resources/sprite_frames.h
@@ -37,7 +37,7 @@ class SpriteFrames : public Resource {
GDCLASS(SpriteFrames, Resource);
struct Anim {
- float speed = 5.0;
+ double speed = 5.0;
bool loop = true;
Vector<Ref<Texture2D>> frames;
};
@@ -64,8 +64,8 @@ public:
void get_animation_list(List<StringName> *r_animations) const;
Vector<String> get_animation_names() const;
- void set_animation_speed(const StringName &p_anim, float p_fps);
- float get_animation_speed(const StringName &p_anim) const;
+ void set_animation_speed(const StringName &p_anim, double p_fps);
+ double get_animation_speed(const StringName &p_anim) const;
void set_animation_loop(const StringName &p_anim, bool p_loop);
bool get_animation_loop(const StringName &p_anim) const;
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index 87371224e0..3381043d29 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -87,9 +87,6 @@ void StyleBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_margin", "margin", "offset"), &StyleBox::set_default_margin);
ClassDB::bind_method(D_METHOD("get_default_margin", "margin"), &StyleBox::get_default_margin);
- //ClassDB::bind_method(D_METHOD("set_default_margin"),&StyleBox::set_default_margin);
- //ClassDB::bind_method(D_METHOD("get_default_margin"),&StyleBox::get_default_margin);
-
ClassDB::bind_method(D_METHOD("get_margin", "margin"), &StyleBox::get_margin);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &StyleBox::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_center_size"), &StyleBox::get_center_size);
@@ -464,12 +461,12 @@ bool StyleBoxFlat::is_anti_aliased() const {
return anti_aliased;
}
-void StyleBoxFlat::set_aa_size(const int &p_aa_size) {
- aa_size = CLAMP(p_aa_size, 1, 5);
+void StyleBoxFlat::set_aa_size(const real_t p_aa_size) {
+ aa_size = CLAMP(p_aa_size, 0.01, 10);
emit_changed();
}
-int StyleBoxFlat::get_aa_size() const {
+real_t StyleBoxFlat::get_aa_size() const {
return aa_size;
}
@@ -486,31 +483,32 @@ Size2 StyleBoxFlat::get_center_size() const {
return Size2();
}
-inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const int corner_radius[4], int *inner_corner_radius) {
- int border_left = inner_rect.position.x - style_rect.position.x;
- int border_top = inner_rect.position.y - style_rect.position.y;
- int border_right = style_rect.size.width - inner_rect.size.width - border_left;
- int border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
+inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) {
+ real_t border_left = inner_rect.position.x - style_rect.position.x;
+ real_t border_top = inner_rect.position.y - style_rect.position.y;
+ real_t border_right = style_rect.size.width - inner_rect.size.width - border_left;
+ real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
+
+ real_t rad;
- int rad;
- //tl
+ // Top left.
rad = MIN(border_top, border_left);
inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
- //tr
+ // Top right;
rad = MIN(border_top, border_right);
inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
- //br
+ // Bottom right.
rad = MIN(border_bottom, border_right);
inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0);
- //bl
+ // Bottom left.
rad = MIN(border_bottom, border_left);
inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0);
}
-inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const int corner_radius[4],
+inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) {
int vert_offset = verts.size();
if (!vert_offset) {
@@ -519,17 +517,17 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
- int ring_corner_radius[4];
+ real_t ring_corner_radius[4];
set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
- //corner radius center points
+ // Corner radius center points.
Vector<Point2> outer_points;
outer_points.push_back(ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0])); //tl
outer_points.push_back(Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1])); //tr
outer_points.push_back(ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2])); //br
outer_points.push_back(Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3])); //bl
- int inner_corner_radius[4];
+ real_t inner_corner_radius[4];
set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
Vector<Point2> inner_points;
@@ -538,11 +536,11 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
inner_points.push_back(inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2])); //br
inner_points.push_back(Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3])); //bl
- //calculate the vert array
+ // Calculate the vertices.
for (int corner_index = 0; corner_index < 4; corner_index++) {
for (int detail = 0; detail <= adapted_corner_detail; detail++) {
for (int inner_outer = 0; inner_outer < 2; inner_outer++) {
- float radius;
+ real_t radius;
Color color;
Point2 corner_point;
if (inner_outer == 0) {
@@ -564,7 +562,7 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
int ring_vert_count = verts.size() - vert_offset;
- //fill the indices and the colors for the border
+ // Fill the indices and the colors for the border.
for (int i = 0; i < ring_vert_count; i++) {
indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
@@ -572,14 +570,14 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
if (fill_center) {
- //fill the indices and the colors for the center
+ //Fill the indices and the colors for the center.
for (int index = 0; index < ring_vert_count / 2; index += 2) {
int i = index;
- //poly 1
+ // Polygon 1.
indices.push_back(vert_offset + i);
indices.push_back(vert_offset + ring_vert_count - 4 - i);
indices.push_back(vert_offset + i + 2);
- //poly 2
+ // Polygon 2.
indices.push_back(vert_offset + i);
indices.push_back(vert_offset + ring_vert_count - 2 - i);
indices.push_back(vert_offset + ring_vert_count - 4 - i);
@@ -587,20 +585,20 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color
}
}
-inline void adapt_values(int p_index_a, int p_index_b, int *adapted_values, const int *p_values, const real_t p_width, const int p_max_a, const int p_max_b) {
+inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) {
if (p_values[p_index_a] + p_values[p_index_b] > p_width) {
- float factor;
- int newValue;
+ real_t factor;
+ real_t new_value;
- factor = (float)p_width / (float)(p_values[p_index_a] + p_values[p_index_b]);
+ factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]);
- newValue = (int)(p_values[p_index_a] * factor);
- if (newValue < adapted_values[p_index_a]) {
- adapted_values[p_index_a] = newValue;
+ new_value = (p_values[p_index_a] * factor);
+ if (new_value < adapted_values[p_index_a]) {
+ adapted_values[p_index_a] = new_value;
}
- newValue = (int)(p_values[p_index_b] * factor);
- if (newValue < adapted_values[p_index_b]) {
- adapted_values[p_index_b] = newValue;
+ new_value = (p_values[p_index_b] * factor);
+ if (new_value < adapted_values[p_index_b]) {
+ adapted_values[p_index_b] = new_value;
}
} else {
adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]);
@@ -623,7 +621,6 @@ Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
}
void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- //PREPARATIONS
bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0);
bool draw_shadow = (shadow_size > 0);
if (!draw_border && !draw_center && !draw_shadow) {
@@ -637,7 +634,6 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
bool aa_on = rounded_corners && anti_aliased;
- float aa_size_grow = 0.5 * ((float)aa_size + 1.0);
bool blend_on = blend_border && draw_border;
@@ -645,15 +641,15 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Color border_color_blend = (draw_center ? bg_color : border_color_alpha);
Color border_color_inner = blend_on ? border_color_blend : border_color;
- //adapt borders (prevent weird overlapping/glitchy drawings)
- int width = MAX(style_rect.size.width, 0);
- int height = MAX(style_rect.size.height, 0);
- int adapted_border[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
+ // Adapt borders (prevent weird overlapping/glitchy drawings).
+ real_t width = MAX(style_rect.size.width, 0);
+ real_t height = MAX(style_rect.size.height, 0);
+ real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height);
adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width);
- //adapt corners (prevent weird overlapping/glitchy drawings)
- int adapted_corner[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
+ // Adapt corners (prevent weird overlapping/glitchy drawings).
+ real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
@@ -665,7 +661,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
if (aa_on) {
for (int i = 0; i < 4; i++) {
if (border_width[i] > 0) {
- border_style_rect = border_style_rect.grow_side((Side)i, -aa_size_grow);
+ border_style_rect = border_style_rect.grow_side((Side)i, -aa_size);
}
}
}
@@ -675,7 +671,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Vector<Color> colors;
Vector<Point2> uvs;
- //DRAW SHADOW
+ // Create shadow
if (draw_shadow) {
Rect2 shadow_inner_rect = style_rect;
shadow_inner_rect.position += shadow_offset;
@@ -694,35 +690,35 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
}
}
- //DRAW border
- if (draw_border) {
+ // Create border (no AA).
+ if (draw_border && !aa_on) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
border_style_rect, infill_rect, border_color_inner, border_color, corner_detail);
}
- //DRAW INFILL
+ // Create infill (no AA).
if (draw_center && (!aa_on || blend_on || !draw_border)) {
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
infill_rect, infill_rect, bg_color, bg_color, corner_detail, true);
}
if (aa_on) {
- int aa_border_width[4];
- int aa_fill_width[4];
+ real_t aa_border_width[4];
+ real_t aa_fill_width[4];
if (draw_border) {
for (int i = 0; i < 4; i++) {
if (border_width[i] > 0) {
- aa_border_width[i] = aa_size_grow;
+ aa_border_width[i] = aa_size;
aa_fill_width[i] = 0;
} else {
aa_border_width[i] = 0;
- aa_fill_width[i] = aa_size_grow;
+ aa_fill_width[i] = aa_size;
}
}
} else {
for (int i = 0; i < 4; i++) {
aa_border_width[i] = 0;
- aa_fill_width[i] = aa_size_grow;
+ aa_fill_width[i] = aa_size;
}
}
@@ -731,45 +727,58 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
if (draw_center) {
if (!blend_on && draw_border) {
- //DRAW INFILL WITHIN BORDER AA
+ Rect2 infill_inner_rect_aa = infill_inner_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ // Create infill within AA border.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_inner_rect, infill_inner_rect, bg_color, bg_color, corner_detail, true);
+ infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, true);
}
if (!blend_on || !draw_border) {
- Rect2 infill_aa_rect = infill_rect.grow_individual(aa_fill_width[SIDE_LEFT], aa_fill_width[SIDE_TOP],
+ Rect2 infill_rect_aa = infill_rect.grow_individual(aa_fill_width[SIDE_LEFT], aa_fill_width[SIDE_TOP],
aa_fill_width[SIDE_RIGHT], aa_fill_width[SIDE_BOTTOM]);
Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
- //INFILL AA
+ // Create infill fake AA gradient.
draw_ring(verts, indices, colors, style_rect, adapted_corner,
- infill_aa_rect, infill_rect, bg_color, alpha_bg, corner_detail);
+ infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail);
}
}
if (draw_border) {
+ Rect2 infill_rect_aa = infill_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ Rect2 style_rect_aa = style_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+ Rect2 border_style_rect_aa = border_style_rect.grow_individual(aa_border_width[SIDE_LEFT], aa_border_width[SIDE_TOP],
+ aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]);
+
+ // Create border.
+ draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
+ border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail);
+
if (!blend_on) {
- //DRAW INNER BORDER AA
+ // Create inner border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect, infill_inner_rect, border_color_blend, border_color, corner_detail);
+ infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail);
}
- //DRAW OUTER BORDER AA
+ // Create outer border fake AA gradient.
draw_ring(verts, indices, colors, border_style_rect, adapted_corner,
- style_rect, border_style_rect, border_color, border_color_alpha, corner_detail);
+ style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail);
}
}
- //COMPUTE UV COORDINATES
- Rect2 uv_rect = style_rect.grow(aa_on ? aa_size_grow : 0);
+ // Compute UV coordinates.
+ Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0);
uvs.resize(verts.size());
for (int i = 0; i < verts.size(); i++) {
uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
}
- //DRAWING
+ // Draw stylebox.
RenderingServer *vs = RenderingServer::get_singleton();
vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs);
}
@@ -869,7 +878,7 @@ void StyleBoxFlat::_bind_methods() {
ADD_GROUP("Anti Aliasing", "anti_aliasing_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "1,5,1"), "set_aa_size", "get_aa_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "set_aa_size", "get_aa_size");
}
StyleBoxFlat::StyleBoxFlat() {}
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index dd5c873a00..a6cd5b7fb7 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -143,9 +143,9 @@ class StyleBoxFlat : public StyleBox {
Color shadow_color = Color(0, 0, 0, 0.6);
Color border_color = Color(0.8, 0.8, 0.8);
- int border_width[4] = {};
- int expand_margin[4] = {};
- int corner_radius[4] = {};
+ real_t border_width[4] = {};
+ real_t expand_margin[4] = {};
+ real_t corner_radius[4] = {};
bool draw_center = true;
bool blend_border = false;
@@ -154,7 +154,7 @@ class StyleBoxFlat : public StyleBox {
int corner_detail = 8;
int shadow_size = 0;
Point2 shadow_offset;
- int aa_size = 1;
+ real_t aa_size = 0.625;
protected:
virtual float get_style_margin(Side p_side) const override;
@@ -162,27 +162,21 @@ protected:
void _validate_property(PropertyInfo &property) const override;
public:
- //Color
void set_bg_color(const Color &p_color);
Color get_bg_color() const;
- //Border Color
void set_border_color(const Color &p_color);
Color get_border_color() const;
- //BORDER
- //width
void set_border_width_all(int p_size);
int get_border_width_min() const;
void set_border_width(Side p_side, int p_width);
int get_border_width(Side p_side) const;
- //blend
void set_border_blend(bool p_blend);
bool get_border_blend() const;
- //CORNER
void set_corner_radius_all(int radius);
void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left);
@@ -192,17 +186,14 @@ public:
void set_corner_detail(const int &p_corner_detail);
int get_corner_detail() const;
- //EXPANDS
void set_expand_margin_size(Side p_expand_side, float p_size);
void set_expand_margin_size_all(float p_expand_margin_size);
void set_expand_margin_size_individual(float p_left, float p_top, float p_right, float p_bottom);
float get_expand_margin_size(Side p_expand_side) const;
- //DRAW CENTER
void set_draw_center(bool p_enabled);
bool is_draw_center_enabled() const;
- //SHADOW
void set_shadow_color(const Color &p_color);
Color get_shadow_color() const;
@@ -212,12 +203,10 @@ public:
void set_shadow_offset(const Point2 &p_offset);
Point2 get_shadow_offset() const;
- //ANTI_ALIASING
void set_anti_aliased(const bool &p_anti_aliased);
bool is_anti_aliased() const;
- //tempAA
- void set_aa_size(const int &p_aa_size);
- int get_aa_size() const;
+ void set_aa_size(const real_t p_aa_size);
+ real_t get_aa_size() const;
virtual Size2 get_center_size() const override;
@@ -228,7 +217,7 @@ public:
~StyleBoxFlat();
};
-// just used to draw lines.
+// Just used to draw lines.
class StyleBoxLine : public StyleBox {
GDCLASS(StyleBoxLine, StyleBox);
Color color;
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 173ce2adce..52a3abf74d 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -43,12 +43,10 @@ Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
return color_map;
}
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_get_line_syntax_highlighting")) {
- color_map = si->call("_get_line_syntax_highlighting", p_line);
- } else {
- color_map = _get_line_syntax_highlighting(p_line);
+ if (!GDVIRTUAL_CALL(_get_line_syntax_highlighting, p_line, color_map)) {
+ color_map = _get_line_syntax_highlighting_impl(p_line);
}
+
highlighting_cache[p_line] = color_map;
return color_map;
}
@@ -69,9 +67,7 @@ void SyntaxHighlighter::_lines_edited_from(int p_from_line, int p_to_line) {
void SyntaxHighlighter::clear_highlighting_cache() {
highlighting_cache.clear();
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_clear_highlighting_cache")) {
- si->call("_clear_highlighting_cache");
+ if (GDVIRTUAL_CALL(_clear_highlighting_cache)) {
return;
}
_clear_highlighting_cache();
@@ -83,9 +79,7 @@ void SyntaxHighlighter::update_cache() {
if (text_edit == nullptr) {
return;
}
- ScriptInstance *si = get_script_instance();
- if (si && si->has_method("_update_cache")) {
- si->call("_update_cache");
+ if (GDVIRTUAL_CALL(_update_cache)) {
return;
}
_update_cache();
@@ -115,9 +109,9 @@ void SyntaxHighlighter::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_highlighting_cache"), &SyntaxHighlighter::clear_highlighting_cache);
ClassDB::bind_method(D_METHOD("get_text_edit"), &SyntaxHighlighter::get_text_edit);
- BIND_VMETHOD(MethodInfo(Variant::DICTIONARY, "_get_line_syntax_highlighting", PropertyInfo(Variant::INT, "line")));
- BIND_VMETHOD(MethodInfo("_clear_highlighting_cache"));
- BIND_VMETHOD(MethodInfo("_update_cache"));
+ GDVIRTUAL_BIND(_get_line_syntax_highlighting, "line")
+ GDVIRTUAL_BIND(_clear_highlighting_cache)
+ GDVIRTUAL_BIND(_update_cache)
}
////////////////////////////////////////////////////////////////////////////////
@@ -130,7 +124,7 @@ static bool _is_hex_symbol(char32_t c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
-Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
+Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
bool prev_is_char = false;
diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h
index f3964b0c8f..0fe39ccff6 100644
--- a/scene/resources/syntax_highlighter.h
+++ b/scene/resources/syntax_highlighter.h
@@ -32,6 +32,8 @@
#define SYNTAX_HIGHLIGHTER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class TextEdit;
@@ -48,9 +50,12 @@ protected:
static void _bind_methods();
+ GDVIRTUAL1RC(Dictionary, _get_line_syntax_highlighting, int)
+ GDVIRTUAL0(_clear_highlighting_cache)
+ GDVIRTUAL0(_update_cache)
public:
Dictionary get_line_syntax_highlighting(int p_line);
- virtual Dictionary _get_line_syntax_highlighting(int p_line) { return Dictionary(); }
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) { return Dictionary(); }
void clear_highlighting_cache();
virtual void _clear_highlighting_cache() {}
@@ -93,7 +98,7 @@ protected:
static void _bind_methods();
public:
- virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
virtual void _clear_highlighting_cache() override;
virtual void _update_cache() override;
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index f1eff6e84f..0807a062f2 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -56,8 +56,8 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override);
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(VALIGN_CENTER));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width);
@@ -76,6 +76,11 @@ void TextLine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab"), "set_flags", "get_flags");
+ ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextLine::set_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextLine::get_text_overrun_behavior);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+
ClassDB::bind_method(D_METHOD("get_objects"), &TextLine::get_objects);
ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect);
@@ -93,6 +98,12 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color"), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextLine::hit_test);
+
+ BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
}
void TextLine::_shape() {
@@ -100,7 +111,38 @@ void TextLine::_shape() {
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(rid, tab_stops);
}
- if (align == HALIGN_FILL) {
+
+ uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ if (overrun_behavior != OVERRUN_NO_TRIMMING) {
+ switch (overrun_behavior) {
+ case OVERRUN_TRIM_WORD_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_WORD:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ break;
+ case OVERRUN_TRIM_CHAR:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ break;
+ case OVERRUN_NO_TRIMMING:
+ break;
+ }
+
+ if (align == HALIGN_FILL) {
+ TS->shaped_text_fit_to_width(rid, width, flags);
+ overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
+ TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
+ } else {
+ TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
+ }
+ } else if (align == HALIGN_FILL) {
TS->shaped_text_fit_to_width(rid, width, flags);
}
dirty = false;
@@ -175,13 +217,13 @@ bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_
return res;
}
-bool TextLine::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
dirty = true;
return res;
}
-bool TextLine::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
const_cast<TextLine *>(this)->_shape();
return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
}
@@ -225,9 +267,20 @@ uint8_t TextLine::get_flags() const {
return flags;
}
+void TextLine::set_text_overrun_behavior(TextLine::OverrunBehavior p_behavior) {
+ if (overrun_behavior != p_behavior) {
+ overrun_behavior = p_behavior;
+ dirty = true;
+ }
+}
+
+TextLine::OverrunBehavior TextLine::get_text_overrun_behavior() const {
+ return overrun_behavior;
+}
+
void TextLine::set_width(float p_width) {
width = p_width;
- if (align == HALIGN_FILL) {
+ if (align == HALIGN_FILL || overrun_behavior != OVERRUN_NO_TRIMMING) {
dirty = true;
}
}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index 1b5c1a3123..9ed9c2f177 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -39,6 +39,16 @@
class TextLine : public RefCounted {
GDCLASS(TextLine, RefCounted);
+public:
+ enum OverrunBehavior {
+ OVERRUN_NO_TRIMMING,
+ OVERRUN_TRIM_CHAR,
+ OVERRUN_TRIM_WORD,
+ OVERRUN_TRIM_ELLIPSIS,
+ OVERRUN_TRIM_WORD_ELLIPSIS,
+ };
+
+private:
RID rid;
int spacing_top = 0;
int spacing_bottom = 0;
@@ -48,6 +58,7 @@ class TextLine : public RefCounted {
float width = -1.0;
uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
HAlign align = HALIGN_LEFT;
+ OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS;
Vector<float> tab_stops;
@@ -76,8 +87,8 @@ public:
bool get_preserve_control() const;
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
- bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1);
- bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER);
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
void set_align(HAlign p_align);
HAlign get_align() const;
@@ -87,6 +98,9 @@ public:
void set_flags(uint8_t p_flags);
uint8_t get_flags() const;
+ void set_text_overrun_behavior(OverrunBehavior p_behavior);
+ OverrunBehavior get_text_overrun_behavior() const;
+
void set_width(float p_width);
float get_width() const;
@@ -113,4 +127,6 @@ public:
~TextLine();
};
+VARIANT_ENUM_CAST(TextLine::OverrunBehavior);
+
#endif // TEXT_LINE_H
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 958c94fe31..763033354a 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -59,8 +59,8 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER));
+ ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGN_CENTER));
ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align);
ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align);
@@ -74,6 +74,11 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab,Break Mandatory,Break Words,Break Graphemes"), "set_flags", "get_flags");
+ ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
@@ -88,6 +93,11 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count);
+ ClassDB::bind_method(D_METHOD("set_max_lines_visible", "max_lines_visible"), &TextParagraph::set_max_lines_visible);
+ ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &TextParagraph::get_max_lines_visible);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible");
+
ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects);
ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
@@ -114,14 +124,20 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test);
+
+ BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
+ BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
}
void TextParagraph::_shape_lines() {
- if (dirty_lines) {
- for (int i = 0; i < lines.size(); i++) {
- TS->free(lines[i]);
+ if (lines_dirty) {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
}
- lines.clear();
+ lines_rid.clear();
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(rid, tab_stops);
@@ -153,13 +169,10 @@ void TextParagraph::_shape_lines() {
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
- if (align == HALIGN_FILL && (line_breaks.size() == 1 || i < line_breaks.size() - 1)) {
- TS->shaped_text_fit_to_width(line, width - h_offset, flags);
- }
dropcap_lines++;
v_offset -= h;
start = line_breaks[i].y;
- lines.push_back(line);
+ lines_rid.push_back(line);
}
}
// Use fixed for the rest of lines.
@@ -169,12 +182,69 @@ void TextParagraph::_shape_lines() {
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
- if (align == HALIGN_FILL && (line_breaks.size() == 1 || i < line_breaks.size() - 1)) {
- TS->shaped_text_fit_to_width(line, width, flags);
+ lines_rid.push_back(line);
+ }
+
+ uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ if (overrun_behavior != OVERRUN_NO_TRIMMING) {
+ switch (overrun_behavior) {
+ case OVERRUN_TRIM_WORD_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_ELLIPSIS:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS;
+ break;
+ case OVERRUN_TRIM_WORD:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY;
+ break;
+ case OVERRUN_TRIM_CHAR:
+ overrun_flags |= TextServer::OVERRUN_TRIM;
+ break;
+ case OVERRUN_NO_TRIMMING:
+ break;
+ }
+ }
+
+ bool autowrap_enabled = ((flags & TextServer::BREAK_WORD_BOUND) == TextServer::BREAK_WORD_BOUND) || ((flags & TextServer::BREAK_GRAPHEME_BOUND) == TextServer::BREAK_GRAPHEME_BOUND);
+
+ // Fill after min_size calculation.
+ if (autowrap_enabled) {
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size();
+ if (lines_hidden) {
+ overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
+ }
+ if (align == HALIGN_FILL) {
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (i < visible_lines - 1 || lines_rid.size() == 1) {
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
+ } else if (i == (visible_lines - 1)) {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+ }
+ }
+
+ } else if (lines_hidden) {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+ }
+
+ } else {
+ // Autowrap disabled.
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (align == HALIGN_FILL) {
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags);
+ overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ TS->shaped_text_fit_to_width(lines_rid[i], width, flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
+ } else {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ }
}
- lines.push_back(line);
}
- dirty_lines = false;
+ lines_dirty = false;
}
}
@@ -184,8 +254,8 @@ RID TextParagraph::get_rid() const {
RID TextParagraph::get_line_rid(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), RID());
- return lines[p_line];
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), RID());
+ return lines_rid[p_line];
}
RID TextParagraph::get_dropcap_rid() const {
@@ -195,10 +265,10 @@ RID TextParagraph::get_dropcap_rid() const {
void TextParagraph::clear() {
spacing_top = 0;
spacing_bottom = 0;
- for (int i = 0; i < lines.size(); i++) {
- TS->free(lines[i]);
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
}
- lines.clear();
+ lines_rid.clear();
TS->shaped_text_clear(rid);
TS->shaped_text_clear(dropcap_rid);
}
@@ -206,7 +276,7 @@ void TextParagraph::clear() {
void TextParagraph::set_preserve_invalid(bool p_enabled) {
TS->shaped_text_set_preserve_invalid(rid, p_enabled);
TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
- dirty_lines = true;
+ lines_dirty = true;
}
bool TextParagraph::get_preserve_invalid() const {
@@ -216,7 +286,7 @@ bool TextParagraph::get_preserve_invalid() const {
void TextParagraph::set_preserve_control(bool p_enabled) {
TS->shaped_text_set_preserve_control(rid, p_enabled);
TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
- dirty_lines = true;
+ lines_dirty = true;
}
bool TextParagraph::get_preserve_control() const {
@@ -226,7 +296,7 @@ bool TextParagraph::get_preserve_control() const {
void TextParagraph::set_direction(TextServer::Direction p_direction) {
TS->shaped_text_set_direction(rid, p_direction);
TS->shaped_text_set_direction(dropcap_rid, p_direction);
- dirty_lines = true;
+ lines_dirty = true;
}
TextServer::Direction TextParagraph::get_direction() const {
@@ -237,7 +307,7 @@ TextServer::Direction TextParagraph::get_direction() const {
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
- dirty_lines = true;
+ lines_dirty = true;
}
TextServer::Orientation TextParagraph::get_orientation() const {
@@ -250,14 +320,14 @@ bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts,
TS->shaped_text_clear(dropcap_rid);
dropcap_margins = p_dropcap_margins;
bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
- dirty_lines = true;
+ lines_dirty = true;
return res;
}
void TextParagraph::clear_dropcap() {
dropcap_margins = Rect2();
TS->shaped_text_clear(dropcap_rid);
- dirty_lines = true;
+ lines_dirty = true;
}
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
@@ -265,7 +335,7 @@ bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, i
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM);
- dirty_lines = true;
+ lines_dirty = true;
return res;
}
@@ -287,18 +357,18 @@ void TextParagraph::_set_bidi_override(const Array &p_override) {
void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) {
TS->shaped_text_set_bidi_override(rid, p_override);
- dirty_lines = true;
+ lines_dirty = true;
}
-bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) {
+bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length);
- dirty_lines = true;
+ lines_dirty = true;
return res;
}
-bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) {
+bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align);
- dirty_lines = true;
+ lines_dirty = true;
return res;
}
@@ -306,7 +376,7 @@ void TextParagraph::set_align(HAlign p_align) {
if (align != p_align) {
if (align == HALIGN_FILL || p_align == HALIGN_FILL) {
align = p_align;
- dirty_lines = true;
+ lines_dirty = true;
} else {
align = p_align;
}
@@ -319,13 +389,13 @@ HAlign TextParagraph::get_align() const {
void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
tab_stops = p_tab_stops;
- dirty_lines = true;
+ lines_dirty = true;
}
void TextParagraph::set_flags(uint8_t p_flags) {
if (flags != p_flags) {
flags = p_flags;
- dirty_lines = true;
+ lines_dirty = true;
}
}
@@ -333,10 +403,21 @@ uint8_t TextParagraph::get_flags() const {
return flags;
}
+void TextParagraph::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) {
+ if (overrun_behavior != p_behavior) {
+ overrun_behavior = p_behavior;
+ lines_dirty = true;
+ }
+}
+
+TextParagraph::OverrunBehavior TextParagraph::get_text_overrun_behavior() const {
+ return overrun_behavior;
+}
+
void TextParagraph::set_width(float p_width) {
if (width != p_width) {
width = p_width;
- dirty_lines = true;
+ lines_dirty = true;
}
}
@@ -356,9 +437,10 @@ Size2 TextParagraph::get_non_wraped_size() const {
Size2 TextParagraph::get_size() const {
const_cast<TextParagraph *>(this)->_shape_lines();
Size2 size;
- for (int i = 0; i < lines.size(); i++) {
- Size2 lsize = TS->shaped_text_get_size(lines[i]);
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ for (int i = 0; i < visible_lines; i++) {
+ Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
size.x = MAX(size.x, lsize.x);
size.y += lsize.y + spacing_top + spacing_bottom;
} else {
@@ -371,22 +453,33 @@ Size2 TextParagraph::get_size() const {
int TextParagraph::get_line_count() const {
const_cast<TextParagraph *>(this)->_shape_lines();
- return lines.size();
+ return lines_rid.size();
+}
+
+void TextParagraph::set_max_lines_visible(int p_lines) {
+ if (p_lines != max_lines_visible) {
+ max_lines_visible = p_lines;
+ lines_dirty = true;
+ }
+}
+
+int TextParagraph::get_max_lines_visible() const {
+ return max_lines_visible;
}
Array TextParagraph::get_line_objects(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Array());
- return TS->shaped_text_get_objects(lines[p_line]);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Array());
+ return TS->shaped_text_get_objects(lines_rid[p_line]);
}
Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Rect2());
- Rect2 xrect = TS->shaped_text_get_object_rect(lines[p_line], p_key);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Rect2());
+ Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
for (int i = 0; i < p_line; i++) {
- Size2 lsize = TS->shaped_text_get_size(lines[i]);
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
xrect.position.y += lsize.y + spacing_top + spacing_bottom;
} else {
xrect.position.x += lsize.x + spacing_top + spacing_bottom;
@@ -397,48 +490,48 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
Size2 TextParagraph::get_line_size(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Size2());
- if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2(TS->shaped_text_get_size(lines[p_line]).x, TS->shaped_text_get_size(lines[p_line]).y + spacing_top + spacing_bottom);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Size2());
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom);
} else {
- return Size2(TS->shaped_text_get_size(lines[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines[p_line]).y);
+ return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines_rid[p_line]).y);
}
}
Vector2i TextParagraph::get_line_range(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), Vector2i());
- return TS->shaped_text_get_range(lines[p_line]);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Vector2i());
+ return TS->shaped_text_get_range(lines_rid[p_line]);
}
float TextParagraph::get_line_ascent(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
- return TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
}
float TextParagraph::get_line_descent(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
- return TS->shaped_text_get_descent(lines[p_line]) + spacing_bottom;
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom;
}
float TextParagraph::get_line_width(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
- return TS->shaped_text_get_width(lines[p_line]);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_width(lines_rid[p_line]);
}
float TextParagraph::get_line_underline_position(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
- return TS->shaped_text_get_underline_position(lines[p_line]);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_underline_position(lines_rid[p_line]);
}
float TextParagraph::get_line_underline_thickness(int p_line) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND_V(p_line < 0 || p_line >= lines.size(), 0.f);
- return TS->shaped_text_get_underline_thickness(lines[p_line]);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f);
+ return TS->shaped_text_get_underline_thickness(lines_rid[p_line]);
}
Size2 TextParagraph::get_dropcap_size() const {
@@ -472,11 +565,13 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color);
}
- for (int i = 0; i < lines.size(); i++) {
+ int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+
+ for (int i = 0; i < lines_visible; i++) {
float l_width = width;
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
@@ -485,7 +580,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
}
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
@@ -493,41 +588,49 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
l_width -= h_offset;
}
}
- float length = TS->shaped_text_get_width(lines[i]);
+ float line_width = TS->shaped_text_get_width(lines_rid[i]);
if (width > 0) {
switch (align) {
case HALIGN_FILL:
+ if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - line_width;
+ } else {
+ ofs.y += l_width - line_width;
+ }
+ }
+ break;
case HALIGN_LEFT:
break;
case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((l_width - length) / 2.0);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - line_width) / 2.0);
} else {
- ofs.y += Math::floor((l_width - length) / 2.0);
+ ofs.y += Math::floor((l_width - line_width) / 2.0);
}
} break;
case HALIGN_RIGHT: {
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += l_width - length;
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - line_width;
} else {
- ofs.y += l_width - length;
+ ofs.y += l_width - line_width;
}
} break;
}
}
float clip_l;
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
clip_l = MAX(0, p_pos.x - ofs.x);
} else {
clip_l = MAX(0, p_pos.y - ofs.y);
}
- TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
}
}
}
@@ -556,11 +659,11 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color);
}
- for (int i = 0; i < lines.size(); i++) {
+ for (int i = 0; i < lines_rid.size(); i++) {
float l_width = width;
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
@@ -569,7 +672,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
}
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
@@ -577,21 +680,29 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
l_width -= h_offset;
}
}
- float length = TS->shaped_text_get_width(lines[i]);
+ float length = TS->shaped_text_get_width(lines_rid[i]);
if (width > 0) {
switch (align) {
case HALIGN_FILL:
+ if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ }
+ break;
case HALIGN_LEFT:
break;
case HALIGN_CENTER: {
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += Math::floor((l_width - length) / 2.0);
} else {
ofs.y += Math::floor((l_width - length) / 2.0);
}
} break;
case HALIGN_RIGHT: {
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += l_width - length;
} else {
ofs.y += l_width - length;
@@ -600,18 +711,18 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
}
}
float clip_l;
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
clip_l = MAX(0, p_pos.x - ofs.x);
} else {
clip_l = MAX(0, p_pos.y - ofs.y);
}
- TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom;
}
}
}
@@ -628,17 +739,17 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
return 0;
}
}
- for (int i = 0; i < lines.size(); i++) {
- if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines[i]).y)) {
- return TS->shaped_text_hit_test_position(lines[i], p_coords.x);
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) {
+ return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x);
}
- ofs.y += TS->shaped_text_get_size(lines[i]).y + spacing_bottom + spacing_top;
+ ofs.y += TS->shaped_text_get_size(lines_rid[i]).y + spacing_bottom + spacing_top;
} else {
- if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines[i]).x)) {
- return TS->shaped_text_hit_test_position(lines[i], p_coords.y);
+ if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) {
+ return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y);
}
- ofs.y += TS->shaped_text_get_size(lines[i]).x + spacing_bottom + spacing_top;
+ ofs.y += TS->shaped_text_get_size(lines_rid[i]).x + spacing_bottom + spacing_top;
}
}
return TS->shaped_text_get_range(rid).y;
@@ -690,29 +801,29 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int
void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND(p_line < 0 || p_line >= lines.size());
+ ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
Vector2 ofs = p_pos;
- if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
} else {
- ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
}
- return TS->shaped_text_draw(lines[p_line], p_canvas, ofs, -1, -1, p_color);
+ return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color);
}
void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const {
const_cast<TextParagraph *>(this)->_shape_lines();
- ERR_FAIL_COND(p_line < 0 || p_line >= lines.size());
+ ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size());
Vector2 ofs = p_pos;
- if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
+ if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
} else {
- ofs.x += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top;
}
- return TS->shaped_text_draw_outline(lines[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
+ return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color);
}
TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
@@ -729,10 +840,10 @@ TextParagraph::TextParagraph() {
}
TextParagraph::~TextParagraph() {
- for (int i = 0; i < lines.size(); i++) {
- TS->free(lines[i]);
+ for (int i = 0; i < lines_rid.size(); i++) {
+ TS->free(lines_rid[i]);
}
- lines.clear();
+ lines_rid.clear();
TS->free(rid);
TS->free(dropcap_rid);
}
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index a34e745090..ee7bbab9c5 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -39,19 +39,33 @@
class TextParagraph : public RefCounted {
GDCLASS(TextParagraph, RefCounted);
+public:
+ enum OverrunBehavior {
+ OVERRUN_NO_TRIMMING,
+ OVERRUN_TRIM_CHAR,
+ OVERRUN_TRIM_WORD,
+ OVERRUN_TRIM_ELLIPSIS,
+ OVERRUN_TRIM_WORD_ELLIPSIS,
+ };
+
+private:
RID dropcap_rid;
int dropcap_lines = 0;
Rect2 dropcap_margins;
RID rid;
- Vector<RID> lines;
+ Vector<RID> lines_rid;
int spacing_top = 0;
int spacing_bottom = 0;
- bool dirty_lines = true;
+ bool lines_dirty = true;
float width = -1.0;
+ int max_lines_visible = -1;
+
uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING;
+
HAlign align = HALIGN_LEFT;
Vector<float> tab_stops;
@@ -86,8 +100,8 @@ public:
void clear_dropcap();
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
- bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1);
- bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER);
+ bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1);
+ bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER);
void set_align(HAlign p_align);
HAlign get_align() const;
@@ -97,9 +111,15 @@ public:
void set_flags(uint8_t p_flags);
uint8_t get_flags() const;
+ void set_text_overrun_behavior(OverrunBehavior p_behavior);
+ OverrunBehavior get_text_overrun_behavior() const;
+
void set_width(float p_width);
float get_width() const;
+ void set_max_lines_visible(int p_lines);
+ int get_max_lines_visible() const;
+
Size2 get_non_wraped_size() const;
Size2 get_size() const;
@@ -140,4 +160,6 @@ public:
~TextParagraph();
};
+VARIANT_ENUM_CAST(TextParagraph::OverrunBehavior);
+
#endif // TEXT_PARAGRAPH_H
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 2ea55843ad..4ad5f2a506 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -2587,7 +2587,10 @@ RID CameraTexture::get_rid() const {
if (feed.is_valid()) {
return feed->get_texture(which_feed);
} else {
- return RID();
+ if (_texture.is_null()) {
+ _texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return _texture;
}
}
@@ -2643,5 +2646,7 @@ bool CameraTexture::get_camera_active() const {
CameraTexture::CameraTexture() {}
CameraTexture::~CameraTexture() {
- // nothing to do here yet
+ if (_texture.is_valid()) {
+ RenderingServer::get_singleton()->free(_texture);
+ }
}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 98aa61138d..2e97c2deb1 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -812,6 +812,7 @@ class CameraTexture : public Texture2D {
GDCLASS(CameraTexture, Texture2D);
private:
+ mutable RID _texture;
int camera_feed_id = 0;
CameraServer::FeedImage which_feed = CameraServer::FEED_RGBA_IMAGE;
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index e4a731c7f7..e49d883ba4 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -1350,40 +1350,36 @@ void Theme::clear() {
_emit_theme_changed();
}
-void Theme::copy_default_theme() {
- Ref<Theme> default_theme2 = get_default();
- copy_theme(default_theme2);
-}
-
-void Theme::copy_theme(const Ref<Theme> &p_other) {
+void Theme::merge_with(const Ref<Theme> &p_other) {
if (p_other.is_null()) {
- clear();
return;
}
_freeze_change_propagation();
- // These items need reconnecting, so add them normally.
+ // Colors.
{
const StringName *K = nullptr;
- while ((K = p_other->icon_map.next(K))) {
+ while ((K = p_other->color_map.next(K))) {
const StringName *L = nullptr;
- while ((L = p_other->icon_map[*K].next(L))) {
- set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ while ((L = p_other->color_map[*K].next(L))) {
+ set_color(*L, *K, p_other->color_map[*K][*L]);
}
}
}
+ // Constants.
{
const StringName *K = nullptr;
- while ((K = p_other->style_map.next(K))) {
+ while ((K = p_other->constant_map.next(K))) {
const StringName *L = nullptr;
- while ((L = p_other->style_map[*K].next(L))) {
- set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ while ((L = p_other->constant_map[*K].next(L))) {
+ set_constant(*L, *K, p_other->constant_map[*K][*L]);
}
}
}
+ // Fonts.
{
const StringName *K = nullptr;
while ((K = p_other->font_map.next(K))) {
@@ -1394,13 +1390,46 @@ void Theme::copy_theme(const Ref<Theme> &p_other) {
}
}
- // These items can be simply copied.
- font_size_map = p_other->font_size_map;
- color_map = p_other->color_map;
- constant_map = p_other->constant_map;
+ // Font sizes.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->font_size_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->font_size_map[*K].next(L))) {
+ set_font_size(*L, *K, p_other->font_size_map[*K][*L]);
+ }
+ }
+ }
- variation_map = p_other->variation_map;
- variation_base_map = p_other->variation_base_map;
+ // Icons.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->icon_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->icon_map[*K].next(L))) {
+ set_icon(*L, *K, p_other->icon_map[*K][*L]);
+ }
+ }
+ }
+
+ // Styleboxes.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->style_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = p_other->style_map[*K].next(L))) {
+ set_stylebox(*L, *K, p_other->style_map[*K][*L]);
+ }
+ }
+ }
+
+ // Type variations.
+ {
+ const StringName *K = nullptr;
+ while ((K = p_other->variation_map.next(K))) {
+ set_type_variation(*K, p_other->variation_map[*K]);
+ }
+ }
_unfreeze_and_propagate_changes();
}
@@ -1534,8 +1563,6 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
- ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
-
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
@@ -1558,8 +1585,8 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
- ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme);
- ClassDB::bind_method(D_METHOD("copy_theme", "other"), &Theme::copy_theme);
+ ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
+ ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 8a8fc28be1..15f21b91b8 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -210,8 +210,7 @@ public:
void get_type_list(List<StringName> *p_list) const;
void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, List<StringName> *p_list);
- void copy_default_theme();
- void copy_theme(const Ref<Theme> &p_other);
+ void merge_with(const Ref<Theme> &p_other);
void clear();
Theme();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index a23152a6d1..a7f99a2113 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -261,54 +261,47 @@ VisualShaderNode::VisualShaderNode() {
/////////////////////////////////////////////////////////
void VisualShaderNodeCustom::update_ports() {
- ERR_FAIL_COND(!get_script_instance());
+ {
+ input_ports.clear();
+ int input_port_count;
+ if (GDVIRTUAL_CALL(_get_input_port_count, input_port_count)) {
+ for (int i = 0; i < input_port_count; i++) {
+ Port port;
+ if (!GDVIRTUAL_CALL(_get_input_port_name, i, port.name)) {
+ port.name = "in" + itos(i);
+ }
+ if (!GDVIRTUAL_CALL(_get_input_port_type, i, port.type)) {
+ port.type = (int)PortType::PORT_TYPE_SCALAR;
+ }
- input_ports.clear();
- if (get_script_instance()->has_method("_get_input_port_count")) {
- int input_port_count = (int)get_script_instance()->call("_get_input_port_count");
- bool has_name = get_script_instance()->has_method("_get_input_port_name");
- bool has_type = get_script_instance()->has_method("_get_input_port_type");
- for (int i = 0; i < input_port_count; i++) {
- Port port;
- if (has_name) {
- port.name = (String)get_script_instance()->call("_get_input_port_name", i);
- } else {
- port.name = "in" + itos(i);
- }
- if (has_type) {
- port.type = (int)get_script_instance()->call("_get_input_port_type", i);
- } else {
- port.type = (int)PortType::PORT_TYPE_SCALAR;
+ input_ports.push_back(port);
}
- input_ports.push_back(port);
}
}
- output_ports.clear();
- if (get_script_instance()->has_method("_get_output_port_count")) {
- int output_port_count = (int)get_script_instance()->call("_get_output_port_count");
- bool has_name = get_script_instance()->has_method("_get_output_port_name");
- bool has_type = get_script_instance()->has_method("_get_output_port_type");
- for (int i = 0; i < output_port_count; i++) {
- Port port;
- if (has_name) {
- port.name = (String)get_script_instance()->call("_get_output_port_name", i);
- } else {
- port.name = "out" + itos(i);
- }
- if (has_type) {
- port.type = (int)get_script_instance()->call("_get_output_port_type", i);
- } else {
- port.type = (int)PortType::PORT_TYPE_SCALAR;
+
+ {
+ output_ports.clear();
+ int output_port_count;
+ if (GDVIRTUAL_CALL(_get_output_port_count, output_port_count)) {
+ for (int i = 0; i < output_port_count; i++) {
+ Port port;
+ if (!GDVIRTUAL_CALL(_get_output_port_name, i, port.name)) {
+ port.name = "out" + itos(i);
+ }
+ if (!GDVIRTUAL_CALL(_get_output_port_type, i, port.type)) {
+ port.type = (int)PortType::PORT_TYPE_SCALAR;
+ }
+
+ output_ports.push_back(port);
}
- output_ports.push_back(port);
}
}
}
String VisualShaderNodeCustom::get_caption() const {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- if (get_script_instance()->has_method("_get_name")) {
- return (String)get_script_instance()->call("_get_name");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_name, ret)) {
+ return ret;
}
return "Unnamed";
}
@@ -342,9 +335,8 @@ String VisualShaderNodeCustom::get_output_port_name(int p_port) const {
}
String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- ERR_FAIL_COND_V(!get_script_instance()->has_method("_get_code"), "");
- Array input_vars;
+ ERR_FAIL_COND_V(!GDVIRTUAL_IS_OVERRIDEN(_get_code), "");
+ Vector<String> input_vars;
for (int i = 0; i < get_input_port_count(); i++) {
input_vars.push_back(p_input_vars[i]);
}
@@ -353,7 +345,8 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
output_vars.push_back(p_output_vars[i]);
}
String code = " {\n";
- String _code = (String)get_script_instance()->call("_get_code", input_vars, output_vars, (int)p_mode, (int)p_type);
+ String _code;
+ GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code);
bool nend = _code.ends_with("\n");
_code = _code.insert(0, " ");
_code = _code.replace("\n", "\n ");
@@ -369,10 +362,10 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
}
String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
- ERR_FAIL_COND_V(!get_script_instance(), "");
- if (get_script_instance()->has_method("_get_global_code")) {
+ String ret;
+ if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) {
String code = "// " + get_caption() + "\n";
- code += (String)get_script_instance()->call("_get_global_code", (int)p_mode);
+ code += ret;
code += "\n";
return code;
}
@@ -416,19 +409,19 @@ void VisualShaderNodeCustom::_set_initialized(bool p_enabled) {
}
void VisualShaderNodeCustom::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_description"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_return_icon_type"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_port_type", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_input_port_name", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_port_type", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING_NAME, "_get_output_port_name", PropertyInfo(Variant::INT, "port")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_code", PropertyInfo(Variant::ARRAY, "input_vars"), PropertyInfo(Variant::ARRAY, "output_vars"), PropertyInfo(Variant::INT, "mode"), PropertyInfo(Variant::INT, "type")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_global_code", PropertyInfo(Variant::INT, "mode")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_highend"));
+ GDVIRTUAL_BIND(_get_name);
+ GDVIRTUAL_BIND(_get_description);
+ GDVIRTUAL_BIND(_get_category);
+ GDVIRTUAL_BIND(_get_return_icon_type);
+ GDVIRTUAL_BIND(_get_input_port_count);
+ GDVIRTUAL_BIND(_get_input_port_type, "port");
+ GDVIRTUAL_BIND(_get_input_port_name, "port");
+ GDVIRTUAL_BIND(_get_output_port_count);
+ GDVIRTUAL_BIND(_get_output_port_type, "port");
+ GDVIRTUAL_BIND(_get_output_port_name, "port");
+ GDVIRTUAL_BIND(_get_code, "input_vars", "output_vars", "mode", "type");
+ GDVIRTUAL_BIND(_get_global_code, "mode");
+ GDVIRTUAL_BIND(_is_highend);
ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized);
ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized);
@@ -1648,19 +1641,10 @@ void VisualShader::_update_shader() const {
{
//fill render mode enums
int idx = 0;
- bool specular = false;
while (render_mode_enums[idx].string) {
if (shader_mode == render_mode_enums[idx].mode) {
- if (shader_mode == Shader::MODE_SPATIAL) {
- if (String(render_mode_enums[idx].string) == "specular") {
- specular = true;
- }
- }
- if (modes.has(render_mode_enums[idx].string) || specular) {
- int which = 0;
- if (modes.has(render_mode_enums[idx].string)) {
- which = modes[render_mode_enums[idx].string];
- }
+ if (modes.has(render_mode_enums[idx].string)) {
+ int which = modes[render_mode_enums[idx].string];
int count = 0;
for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) {
String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i];
@@ -3041,6 +3025,10 @@ String VisualShaderNodeUniform::get_uniform_name() const {
}
void VisualShaderNodeUniform::set_qualifier(VisualShaderNodeUniform::Qualifier p_qual) {
+ ERR_FAIL_INDEX(int(p_qual), int(QUAL_MAX));
+ if (qualifier == p_qual) {
+ return;
+ }
qualifier = p_qual;
emit_changed();
}
@@ -3070,6 +3058,7 @@ void VisualShaderNodeUniform::_bind_methods() {
BIND_ENUM_CONSTANT(QUAL_NONE);
BIND_ENUM_CONSTANT(QUAL_GLOBAL);
BIND_ENUM_CONSTANT(QUAL_INSTANCE);
+ BIND_ENUM_CONSTANT(QUAL_MAX);
}
String VisualShaderNodeUniform::_get_qual_str() const {
@@ -3081,6 +3070,8 @@ String VisualShaderNodeUniform::_get_qual_str() const {
return "global ";
case QUAL_INSTANCE:
return "instance ";
+ default:
+ break;
}
}
return String();
@@ -3090,10 +3081,86 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
List<String> keyword_list;
ShaderLanguage::get_keyword_list(&keyword_list);
if (keyword_list.find(uniform_name)) {
- return TTR("Uniform name cannot be equal to a shader keyword. Choose another name.");
+ return TTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
}
if (!is_qualifier_supported(qualifier)) {
- return "This uniform type does not support that qualifier.";
+ String qualifier_str;
+ switch (qualifier) {
+ case QUAL_NONE:
+ break;
+ case QUAL_GLOBAL:
+ qualifier_str = "global";
+ break;
+ case QUAL_INSTANCE:
+ qualifier_str = "instance";
+ break;
+ default:
+ break;
+ }
+ return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
+ } else if (qualifier == Qualifier::QUAL_GLOBAL) {
+ RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name);
+ if (gvt == RS::GLOBAL_VAR_TYPE_MAX) {
+ return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
+ }
+ bool incompatible_type = false;
+ switch (gvt) {
+ case RS::GLOBAL_VAR_TYPE_FLOAT: {
+ if (!Object::cast_to<VisualShaderNodeFloatUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_INT: {
+ if (!Object::cast_to<VisualShaderNodeIntUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_BOOL: {
+ if (!Object::cast_to<VisualShaderNodeBooleanUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_COLOR: {
+ if (!Object::cast_to<VisualShaderNodeColorUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_VEC3: {
+ if (!Object::cast_to<VisualShaderNodeVec3Uniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
+ if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
+ if (!Object::cast_to<VisualShaderNodeTextureUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
+ if (!Object::cast_to<VisualShaderNodeTexture3DUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
+ if (!Object::cast_to<VisualShaderNodeTexture2DArrayUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
+ if (!Object::cast_to<VisualShaderNodeCubemapUniform>(this)) {
+ incompatible_type = true;
+ }
+ } break;
+ default:
+ break;
+ }
+ if (incompatible_type) {
+ return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
+ }
}
return String();
@@ -3285,6 +3352,10 @@ bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
}
void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_input_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> inputs_strings = inputs.split(";", false);
int index = 0;
@@ -3357,6 +3428,10 @@ bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
}
void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
+ ERR_FAIL_COND(has_output_port(p_id));
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
+ ERR_FAIL_COND(!is_valid_port_name(p_name));
+
String str = itos(p_id) + "," + itos(p_type) + "," + p_name + ";";
Vector<String> outputs_strings = outputs.split(";", false);
int index = 0;
@@ -3438,7 +3513,7 @@ void VisualShaderNodeGroupBase::clear_output_ports() {
void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_input_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (input_ports[p_id].type == p_type) {
return;
@@ -3510,7 +3585,7 @@ String VisualShaderNodeGroupBase::get_input_port_name(int p_id) const {
void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
ERR_FAIL_COND(!has_output_port(p_id));
- ERR_FAIL_COND(p_type < 0 || p_type >= PORT_TYPE_MAX);
+ ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
if (output_ports[p_id].type == p_type) {
return;
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index abf55185ab..b3efac02aa 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -314,6 +314,20 @@ protected:
virtual void remove_input_port_default_value(int p_port) override;
virtual void clear_default_input_values() override;
+ GDVIRTUAL0RC(String, _get_name)
+ GDVIRTUAL0RC(String, _get_description)
+ GDVIRTUAL0RC(String, _get_category)
+ GDVIRTUAL0RC(int, _get_return_icon_type)
+ GDVIRTUAL0RC(int, _get_input_port_count)
+ GDVIRTUAL1RC(int, _get_input_port_type, int)
+ GDVIRTUAL1RC(String, _get_input_port_name, int)
+ GDVIRTUAL0RC(int, _get_output_port_count)
+ GDVIRTUAL1RC(int, _get_output_port_type, int)
+ GDVIRTUAL1RC(String, _get_output_port_name, int)
+ GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int)
+ GDVIRTUAL1RC(String, _get_global_code, int)
+ GDVIRTUAL0RC(bool, _is_highend)
+
protected:
void _set_input_port_default_value(int p_port, const Variant &p_value);
@@ -435,6 +449,7 @@ public:
QUAL_NONE,
QUAL_GLOBAL,
QUAL_INSTANCE,
+ QUAL_MAX,
};
private:
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 6fd6fd8f3b..e45dfdcb1b 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -38,7 +38,7 @@ VisualShaderNodeConstant::VisualShaderNodeConstant() {
////////////// Scalar(Float)
String VisualShaderNodeFloatConstant::get_caption() const {
- return "ScalarFloat";
+ return "FloatConstant";
}
int VisualShaderNodeFloatConstant::get_input_port_count() const {
@@ -69,8 +69,11 @@ String VisualShaderNodeFloatConstant::generate_code(Shader::Mode p_mode, VisualS
return " " + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n";
}
-void VisualShaderNodeFloatConstant::set_constant(float p_value) {
- constant = p_value;
+void VisualShaderNodeFloatConstant::set_constant(float p_constant) {
+ if (Math::is_equal_approx(constant, p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -85,7 +88,7 @@ Vector<StringName> VisualShaderNodeFloatConstant::get_editable_properties() cons
}
void VisualShaderNodeFloatConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeFloatConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeFloatConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeFloatConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant"), "set_constant", "get_constant");
@@ -97,7 +100,7 @@ VisualShaderNodeFloatConstant::VisualShaderNodeFloatConstant() {
////////////// Scalar(Int)
String VisualShaderNodeIntConstant::get_caption() const {
- return "ScalarInt";
+ return "IntConstant";
}
int VisualShaderNodeIntConstant::get_input_port_count() const {
@@ -128,8 +131,11 @@ String VisualShaderNodeIntConstant::generate_code(Shader::Mode p_mode, VisualSha
return " " + p_output_vars[0] + " = " + itos(constant) + ";\n";
}
-void VisualShaderNodeIntConstant::set_constant(int p_value) {
- constant = p_value;
+void VisualShaderNodeIntConstant::set_constant(int p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -144,7 +150,7 @@ Vector<StringName> VisualShaderNodeIntConstant::get_editable_properties() const
}
void VisualShaderNodeIntConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeIntConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeIntConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeIntConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::INT, "constant"), "set_constant", "get_constant");
@@ -156,7 +162,7 @@ VisualShaderNodeIntConstant::VisualShaderNodeIntConstant() {
////////////// Boolean
String VisualShaderNodeBooleanConstant::get_caption() const {
- return "Boolean";
+ return "BooleanConstant";
}
int VisualShaderNodeBooleanConstant::get_input_port_count() const {
@@ -187,8 +193,11 @@ String VisualShaderNodeBooleanConstant::generate_code(Shader::Mode p_mode, Visua
return " " + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n";
}
-void VisualShaderNodeBooleanConstant::set_constant(bool p_value) {
- constant = p_value;
+void VisualShaderNodeBooleanConstant::set_constant(bool p_constant) {
+ if (constant == p_constant) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -203,7 +212,7 @@ Vector<StringName> VisualShaderNodeBooleanConstant::get_editable_properties() co
}
void VisualShaderNodeBooleanConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeBooleanConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeBooleanConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeBooleanConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constant"), "set_constant", "get_constant");
@@ -215,7 +224,7 @@ VisualShaderNodeBooleanConstant::VisualShaderNodeBooleanConstant() {
////////////// Color
String VisualShaderNodeColorConstant::get_caption() const {
- return "Color";
+ return "ColorConstant";
}
int VisualShaderNodeColorConstant::get_input_port_count() const {
@@ -257,8 +266,11 @@ String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualS
return code;
}
-void VisualShaderNodeColorConstant::set_constant(Color p_value) {
- constant = p_value;
+void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -273,7 +285,7 @@ Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() cons
}
void VisualShaderNodeColorConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeColorConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant");
@@ -285,7 +297,7 @@ VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
////////////// Vector
String VisualShaderNodeVec3Constant::get_caption() const {
- return "Vector";
+ return "VectorConstant";
}
int VisualShaderNodeVec3Constant::get_input_port_count() const {
@@ -316,8 +328,11 @@ String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualSh
return " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n";
}
-void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) {
- constant = p_value;
+void VisualShaderNodeVec3Constant::set_constant(const Vector3 &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -332,7 +347,7 @@ Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const
}
void VisualShaderNodeVec3Constant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec3Constant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant");
@@ -344,7 +359,7 @@ VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() {
////////////// Transform3D
String VisualShaderNodeTransformConstant::get_caption() const {
- return "Transform3D";
+ return "TransformConstant";
}
int VisualShaderNodeTransformConstant::get_input_port_count() const {
@@ -383,8 +398,11 @@ String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, Vis
return code;
}
-void VisualShaderNodeTransformConstant::set_constant(Transform3D p_value) {
- constant = p_value;
+void VisualShaderNodeTransformConstant::set_constant(const Transform3D &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
emit_changed();
}
@@ -399,7 +417,7 @@ Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties()
}
void VisualShaderNodeTransformConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant);
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeTransformConstant::set_constant);
ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant);
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "constant"), "set_constant", "get_constant");
@@ -502,6 +520,8 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -685,8 +705,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
}
void VisualShaderNodeTexture::set_source(Source p_source) {
- source = p_source;
- switch (source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
+ switch (p_source) {
case SOURCE_TEXTURE:
simple_decl = true;
break;
@@ -705,7 +728,10 @@ void VisualShaderNodeTexture::set_source(Source p_source) {
case SOURCE_PORT:
simple_decl = false;
break;
+ default:
+ break;
}
+ source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
}
@@ -714,8 +740,8 @@ VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const {
return source;
}
-void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture::set_texture(Ref<Texture2D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -723,8 +749,12 @@ Ref<Texture2D> VisualShaderNodeTexture::get_texture() const {
return texture;
}
-void VisualShaderNodeTexture::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTexture::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -797,9 +827,12 @@ void VisualShaderNodeTexture::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL);
BIND_ENUM_CONSTANT(SOURCE_DEPTH);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
+
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeTexture::VisualShaderNodeTexture() {
@@ -1074,6 +1107,10 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader
}
void VisualShaderNodeSample3D::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1091,6 +1128,7 @@ void VisualShaderNodeSample3D::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
}
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
@@ -1127,7 +1165,7 @@ String VisualShaderNodeTexture2DArray::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture2DArray::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture;
+ dtp.param = texture_array;
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1140,13 +1178,13 @@ String VisualShaderNodeTexture2DArray::generate_global(Shader::Mode p_mode, Visu
return String();
}
-void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_texture_array) {
+ texture_array = p_texture_array;
emit_changed();
}
Ref<Texture2DArray> VisualShaderNodeTexture2DArray::get_texture_array() const {
- return texture;
+ return texture_array;
}
Vector<StringName> VisualShaderNodeTexture2DArray::get_editable_properties() const {
@@ -1197,8 +1235,8 @@ String VisualShaderNodeTexture3D::generate_global(Shader::Mode p_mode, VisualSha
return String();
}
-void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_value) {
- texture = p_value;
+void VisualShaderNodeTexture3D::set_texture(Ref<Texture3D> p_texture) {
+ texture = p_texture;
emit_changed();
}
@@ -1301,6 +1339,8 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
case TYPE_NORMAL_MAP:
u += " : hint_normal";
break;
+ default:
+ break;
}
return u + ";\n";
}
@@ -1364,6 +1404,10 @@ String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
}
void VisualShaderNodeCubemap::set_source(Source p_source) {
+ ERR_FAIL_INDEX(int(p_source), int(SOURCE_MAX));
+ if (source == p_source) {
+ return;
+ }
source = p_source;
emit_changed();
emit_signal(SNAME("editor_refresh_request"));
@@ -1373,8 +1417,8 @@ VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const {
return source;
}
-void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_value) {
- cube_map = p_value;
+void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_cube_map) {
+ cube_map = p_cube_map;
emit_changed();
}
@@ -1382,8 +1426,12 @@ Ref<Cubemap> VisualShaderNodeCubemap::get_cube_map() const {
return cube_map;
}
-void VisualShaderNodeCubemap::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeCubemap::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -1424,10 +1472,12 @@ void VisualShaderNodeCubemap::_bind_methods() {
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
+ BIND_ENUM_CONSTANT(SOURCE_MAX);
BIND_ENUM_CONSTANT(TYPE_DATA);
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
}
VisualShaderNodeCubemap::VisualShaderNodeCubemap() {
@@ -1497,12 +1547,17 @@ String VisualShaderNodeFloatOp::generate_code(Shader::Mode p_mode, VisualShader:
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
-
return code;
}
void VisualShaderNodeFloatOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1533,6 +1588,7 @@ void VisualShaderNodeFloatOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MIN);
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeFloatOp::VisualShaderNodeFloatOp() {
@@ -1594,12 +1650,18 @@ String VisualShaderNodeIntOp::generate_code(Shader::Mode p_mode, VisualShader::T
case OP_MIN:
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeIntOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), OP_ENUM_SIZE);
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1627,6 +1689,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_MOD);
BIND_ENUM_CONSTANT(OP_MAX);
BIND_ENUM_CONSTANT(OP_MIN);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeIntOp::VisualShaderNodeIntOp() {
@@ -1703,12 +1766,18 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -1741,6 +1810,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_ATAN2);
BIND_ENUM_CONSTANT(OP_REFLECT);
BIND_ENUM_CONSTANT(OP_STEP);
+ BIND_ENUM_CONSTANT(OP_ENUM_SIZE);
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
@@ -1844,14 +1914,19 @@ String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader:
}
} break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorOp::set_operator(Operator p_op) {
- op = p_op;
- switch (op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
+ switch (p_op) {
case OP_SCREEN:
simple_decl = true;
break;
@@ -1879,7 +1954,10 @@ void VisualShaderNodeColorOp::set_operator(Operator p_op) {
case OP_HARD_LIGHT:
simple_decl = false;
break;
+ default:
+ break;
}
+ op = p_op;
emit_changed();
}
@@ -1908,6 +1986,7 @@ void VisualShaderNodeColorOp::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BURN);
BIND_ENUM_CONSTANT(OP_SOFT_LIGHT);
BIND_ENUM_CONSTANT(OP_HARD_LIGHT);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
@@ -1915,76 +1994,99 @@ VisualShaderNodeColorOp::VisualShaderNodeColorOp() {
set_input_port_default_value(1, Vector3());
}
-////////////// Transform Mult
+////////////// Transform Op
-String VisualShaderNodeTransformMult::get_caption() const {
- return "TransformMult";
+String VisualShaderNodeTransformOp::get_caption() const {
+ return "TransformOp";
}
-int VisualShaderNodeTransformMult::get_input_port_count() const {
+int VisualShaderNodeTransformOp::get_input_port_count() const {
return 2;
}
-VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const {
+VisualShaderNodeTransformOp::PortType VisualShaderNodeTransformOp::get_input_port_type(int p_port) const {
return PORT_TYPE_TRANSFORM;
}
-String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const {
+String VisualShaderNodeTransformOp::get_input_port_name(int p_port) const {
return p_port == 0 ? "a" : "b";
}
-int VisualShaderNodeTransformMult::get_output_port_count() const {
+int VisualShaderNodeTransformOp::get_output_port_count() const {
return 1;
}
-VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const {
+VisualShaderNodeTransformOp::PortType VisualShaderNodeTransformOp::get_output_port_type(int p_port) const {
return PORT_TYPE_TRANSFORM;
}
-String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const {
+String VisualShaderNodeTransformOp::get_output_port_name(int p_port) const {
return "mult"; //no output port means the editor will be used as port
}
-String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- if (op == OP_AxB) {
- return " " + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
- } else if (op == OP_BxA) {
- return " " + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
- } else if (op == OP_AxB_COMP) {
- return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
- } else {
- return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n";
+String VisualShaderNodeTransformOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ switch (op) {
+ case OP_AxB:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n";
+ case OP_BxA:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n";
+ case OP_AxB_COMP:
+ return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ case OP_BxA_COMP:
+ return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n";
+ case OP_ADD:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " + " + p_input_vars[1] + ";\n";
+ case OP_A_MINUS_B:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " - " + p_input_vars[1] + ";\n";
+ case OP_B_MINUS_A:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " - " + p_input_vars[0] + ";\n";
+ case OP_A_DIV_B:
+ return " " + p_output_vars[0] + " = " + p_input_vars[0] + " / " + p_input_vars[1] + ";\n";
+ case OP_B_DIV_A:
+ return " " + p_output_vars[0] + " = " + p_input_vars[1] + " / " + p_input_vars[0] + ";\n";
+ default:
+ return "";
}
}
-void VisualShaderNodeTransformMult::set_operator(Operator p_op) {
+void VisualShaderNodeTransformOp::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
-VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const {
+VisualShaderNodeTransformOp::Operator VisualShaderNodeTransformOp::get_operator() const {
return op;
}
-Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const {
+Vector<StringName> VisualShaderNodeTransformOp::get_editable_properties() const {
Vector<StringName> props;
props.push_back("operator");
return props;
}
-void VisualShaderNodeTransformMult::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator);
- ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator);
+void VisualShaderNodeTransformOp::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformOp::set_operator);
+ ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component)"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B(per component),B x A(per component),A + B,A - B,B - A,A / B,B / A"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_AxB);
BIND_ENUM_CONSTANT(OP_BxA);
BIND_ENUM_CONSTANT(OP_AxB_COMP);
BIND_ENUM_CONSTANT(OP_BxA_COMP);
+ BIND_ENUM_CONSTANT(OP_ADD);
+ BIND_ENUM_CONSTANT(OP_A_MINUS_B);
+ BIND_ENUM_CONSTANT(OP_B_MINUS_A);
+ BIND_ENUM_CONSTANT(OP_A_DIV_B);
+ BIND_ENUM_CONSTANT(OP_B_DIV_A);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
-VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() {
+VisualShaderNodeTransformOp::VisualShaderNodeTransformOp() {
set_input_port_default_value(0, Transform3D());
set_input_port_default_value(1, Transform3D());
}
@@ -2032,6 +2134,10 @@ String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, Visu
}
void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(int(p_op), int(OP_MAX));
+ if (op == p_op) {
+ return;
+ }
op = p_op;
emit_changed();
}
@@ -2056,6 +2162,7 @@ void VisualShaderNodeTransformVecMult::_bind_methods() {
BIND_ENUM_CONSTANT(OP_BxA);
BIND_ENUM_CONSTANT(OP_3x3_AxB);
BIND_ENUM_CONSTANT(OP_3x3_BxA);
+ BIND_ENUM_CONSTANT(OP_MAX);
}
VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() {
@@ -2094,7 +2201,7 @@ String VisualShaderNodeFloatFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *scalar_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *functions[FUNC_MAX] = {
"sin($)",
"cos($)",
"tan($)",
@@ -2128,11 +2235,14 @@ String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShade
"trunc($)",
"1.0 - $"
};
-
- return " " + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeFloatFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2185,6 +2295,7 @@ void VisualShaderNodeFloatFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeFloatFunc::VisualShaderNodeFloatFunc() {
@@ -2222,16 +2333,20 @@ String VisualShaderNodeIntFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIntFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *int_func_id[FUNC_SIGN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"abs($)",
"-($)",
"sign($)"
};
- return " " + p_output_vars[0] + " = " + String(int_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ return " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
}
void VisualShaderNodeIntFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2255,6 +2370,7 @@ void VisualShaderNodeIntFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_ABS);
BIND_ENUM_CONSTANT(FUNC_NEGATE);
BIND_ENUM_CONSTANT(FUNC_SIGN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIntFunc::VisualShaderNodeIntFunc() {
@@ -2292,7 +2408,7 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_ONEMINUS + 1] = {
+ static const char *vec_func_id[FUNC_MAX] = {
"normalize($)",
"max(min($, vec3(1.0)), vec3(0.0))",
"-($)",
@@ -2358,14 +2474,18 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
}
void VisualShaderNodeVectorFunc::set_function(Function p_func) {
- func = p_func;
- if (func == FUNC_RGB2HSV) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
+ if (p_func == FUNC_RGB2HSV) {
simple_decl = false;
- } else if (func == FUNC_HSV2RGB) {
+ } else if (p_func == FUNC_HSV2RGB) {
simple_decl = false;
} else {
simple_decl = true;
}
+ func = p_func;
emit_changed();
}
@@ -2420,6 +2540,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_TANH);
BIND_ENUM_CONSTANT(FUNC_TRUNC);
BIND_ENUM_CONSTANT(FUNC_ONEMINUS);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
@@ -2478,12 +2599,18 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade
code += " " + p_output_vars[0] + " = vec3(r, g, b);\n";
code += " }\n";
break;
+ default:
+ break;
}
return code;
}
void VisualShaderNodeColorFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2506,6 +2633,7 @@ void VisualShaderNodeColorFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GRAYSCALE);
BIND_ENUM_CONSTANT(FUNC_SEPIA);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeColorFunc::VisualShaderNodeColorFunc() {
@@ -2544,17 +2672,21 @@ String VisualShaderNodeTransformFunc::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_TRANSPOSE + 1] = {
+ static const char *functions[FUNC_MAX] = {
"inverse($)",
"transpose($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeTransformFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2577,6 +2709,7 @@ void VisualShaderNodeTransformFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_INVERSE);
BIND_ENUM_CONSTANT(FUNC_TRANSPOSE);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() {
@@ -2619,8 +2752,6 @@ String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
return "offset";
case FUNC_SCALING:
return "pivot";
- case FUNC_MAX:
- break;
default:
break;
}
@@ -2673,24 +2804,23 @@ String VisualShaderNodeUVFunc::generate_code(Shader::Mode p_mode, VisualShader::
case FUNC_SCALING: {
code += vformat(" %s = fma((%s - %s), %s, %s);\n", p_output_vars[0], uv, offset_pivot, scale, offset_pivot);
} break;
- case FUNC_MAX:
+ default:
break;
}
return code;
}
void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) {
- ERR_FAIL_INDEX(int(p_func), FUNC_MAX);
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
}
- func = p_func;
-
if (p_func == FUNC_PANNING) {
set_input_port_default_value(2, Vector3()); // offset
} else { // FUNC_SCALING
set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
}
+ func = p_func;
emit_changed();
}
@@ -2866,18 +2996,22 @@ String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2901,6 +3035,7 @@ void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
@@ -2938,18 +3073,22 @@ String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) co
}
String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_Y + 1] = {
+ static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
"dFdy($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -2973,6 +3112,7 @@ void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
@@ -3037,7 +3177,7 @@ String VisualShaderNodeClamp::generate_code(Shader::Mode p_mode, VisualShader::T
}
void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3075,7 +3215,7 @@ Vector<StringName> VisualShaderNodeClamp::get_editable_properties() const {
}
void VisualShaderNodeClamp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeClamp::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector"), "set_op_type", "get_op_type");
@@ -3242,7 +3382,7 @@ String VisualShaderNodeStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3293,7 +3433,7 @@ Vector<StringName> VisualShaderNodeStep::get_editable_properties() const {
}
void VisualShaderNodeStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3366,7 +3506,7 @@ String VisualShaderNodeSmoothStep::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3420,7 +3560,7 @@ Vector<StringName> VisualShaderNodeSmoothStep::get_editable_properties() const {
}
void VisualShaderNodeSmoothStep::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSmoothStep::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3588,7 +3728,7 @@ String VisualShaderNodeMix::get_output_port_name(int p_port) const {
}
void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -3636,7 +3776,7 @@ Vector<StringName> VisualShaderNodeMix::get_editable_properties() const {
}
void VisualShaderNodeMix::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMix::set_op_type);
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
@@ -3905,6 +4045,10 @@ bool VisualShaderNodeFloatUniform::is_use_prop_slots() const {
}
void VisualShaderNodeFloatUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -3914,6 +4058,9 @@ VisualShaderNodeFloatUniform::Hint VisualShaderNodeFloatUniform::get_hint() cons
}
void VisualShaderNodeFloatUniform::set_min(float p_value) {
+ if (Math::is_equal_approx(hint_range_min, p_value)) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -3923,6 +4070,9 @@ float VisualShaderNodeFloatUniform::get_min() const {
}
void VisualShaderNodeFloatUniform::set_max(float p_value) {
+ if (Math::is_equal_approx(hint_range_max, p_value)) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -3932,6 +4082,9 @@ float VisualShaderNodeFloatUniform::get_max() const {
}
void VisualShaderNodeFloatUniform::set_step(float p_value) {
+ if (Math::is_equal_approx(hint_range_step, p_value)) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -3941,6 +4094,9 @@ float VisualShaderNodeFloatUniform::get_step() const {
}
void VisualShaderNodeFloatUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -3950,6 +4106,9 @@ bool VisualShaderNodeFloatUniform::is_default_value_enabled() const {
}
void VisualShaderNodeFloatUniform::set_default_value(float p_value) {
+ if (Math::is_equal_approx(default_value, p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -3987,6 +4146,7 @@ void VisualShaderNodeFloatUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeFloatUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4076,6 +4236,10 @@ bool VisualShaderNodeIntUniform::is_use_prop_slots() const {
}
void VisualShaderNodeIntUniform::set_hint(Hint p_hint) {
+ ERR_FAIL_INDEX(int(p_hint), int(HINT_MAX));
+ if (hint == p_hint) {
+ return;
+ }
hint = p_hint;
emit_changed();
}
@@ -4085,6 +4249,9 @@ VisualShaderNodeIntUniform::Hint VisualShaderNodeIntUniform::get_hint() const {
}
void VisualShaderNodeIntUniform::set_min(int p_value) {
+ if (hint_range_min == p_value) {
+ return;
+ }
hint_range_min = p_value;
emit_changed();
}
@@ -4094,6 +4261,9 @@ int VisualShaderNodeIntUniform::get_min() const {
}
void VisualShaderNodeIntUniform::set_max(int p_value) {
+ if (hint_range_max == p_value) {
+ return;
+ }
hint_range_max = p_value;
emit_changed();
}
@@ -4103,6 +4273,9 @@ int VisualShaderNodeIntUniform::get_max() const {
}
void VisualShaderNodeIntUniform::set_step(int p_value) {
+ if (hint_range_step == p_value) {
+ return;
+ }
hint_range_step = p_value;
emit_changed();
}
@@ -4111,8 +4284,11 @@ int VisualShaderNodeIntUniform::get_step() const {
return hint_range_step;
}
-void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeIntUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4120,8 +4296,11 @@ bool VisualShaderNodeIntUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeIntUniform::set_default_value(int p_value) {
- default_value = p_value;
+void VisualShaderNodeIntUniform::set_default_value(int p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4158,6 +4337,7 @@ void VisualShaderNodeIntUniform::_bind_methods() {
BIND_ENUM_CONSTANT(HINT_NONE);
BIND_ENUM_CONSTANT(HINT_RANGE);
BIND_ENUM_CONSTANT(HINT_RANGE_STEP);
+ BIND_ENUM_CONSTANT(HINT_MAX);
}
bool VisualShaderNodeIntUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -4218,8 +4398,11 @@ String VisualShaderNodeBooleanUniform::get_output_port_name(int p_port) const {
return ""; //no output port means the editor will be used as port
}
-void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_enabled) {
- default_value_enabled = p_enabled;
+void VisualShaderNodeBooleanUniform::set_default_value_enabled(bool p_default_value_enabled) {
+ if (default_value_enabled == p_default_value_enabled) {
+ return;
+ }
+ default_value_enabled = p_default_value_enabled;
emit_changed();
}
@@ -4227,8 +4410,11 @@ bool VisualShaderNodeBooleanUniform::is_default_value_enabled() const {
return default_value_enabled;
}
-void VisualShaderNodeBooleanUniform::set_default_value(bool p_value) {
- default_value = p_value;
+void VisualShaderNodeBooleanUniform::set_default_value(bool p_default_value) {
+ if (default_value == p_default_value) {
+ return;
+ }
+ default_value = p_default_value;
emit_changed();
}
@@ -4323,6 +4509,9 @@ String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
}
void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) {
+ if (default_value_enabled == p_enabled) {
+ return;
+ }
default_value_enabled = p_enabled;
emit_changed();
}
@@ -4332,6 +4521,9 @@ bool VisualShaderNodeColorUniform::is_default_value_enabled() const {
}
void VisualShaderNodeColorUniform::set_default_value(const Color &p_value) {
+ if (default_value.is_equal_approx(p_value)) {
+ return;
+ }
default_value = p_value;
emit_changed();
}
@@ -4575,7 +4767,10 @@ bool VisualShaderNodeTransformUniform::is_use_prop_slots() const {
}
bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) const {
- return true; // all qualifiers are supported
+ if (p_qual == Qualifier::QUAL_INSTANCE) {
+ return false;
+ }
+ return true;
}
bool VisualShaderNodeTransformUniform::is_convertible_to_constant() const {
@@ -4666,6 +4861,9 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4704,8 +4902,12 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual
return code;
}
-void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) {
- texture_type = p_type;
+void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) {
+ ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX));
+ if (texture_type == p_texture_type) {
+ return;
+ }
+ texture_type = p_texture_type;
emit_changed();
}
@@ -4713,8 +4915,12 @@ VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_
return texture_type;
}
-void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) {
- color_default = p_default;
+void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_color_default) {
+ ERR_FAIL_INDEX(int(p_color_default), int(COLOR_DEFAULT_MAX));
+ if (color_default == p_color_default) {
+ return;
+ }
+ color_default = p_color_default;
emit_changed();
}
@@ -4743,9 +4949,11 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_COLOR);
BIND_ENUM_CONSTANT(TYPE_NORMAL_MAP);
BIND_ENUM_CONSTANT(TYPE_ANISO);
+ BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE);
BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK);
+ BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX);
}
String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
@@ -4763,6 +4971,8 @@ bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) co
return true;
case Qualifier::QUAL_INSTANCE:
return false;
+ default:
+ break;
}
return false;
}
@@ -4928,6 +5138,9 @@ String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mod
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -4998,6 +5211,9 @@ String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, Vi
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5068,6 +5284,9 @@ String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, Visu
case TYPE_ANISO:
code += " : hint_aniso;\n";
break;
+ default:
+ code += ";\n";
+ break;
}
return code;
@@ -5224,7 +5443,7 @@ String VisualShaderNodeSwitch::get_output_port_name(int p_port) const {
}
void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5429,17 +5648,21 @@ String VisualShaderNodeIs::get_output_port_name(int p_port) const {
}
String VisualShaderNodeIs::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *funcs[FUNC_IS_NAN + 1] = {
+ static const char *functions[FUNC_MAX] = {
"isinf($)",
"isnan($)"
};
String code;
- code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
return code;
}
void VisualShaderNodeIs::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5462,6 +5685,7 @@ void VisualShaderNodeIs::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_IS_INF);
BIND_ENUM_CONSTANT(FUNC_IS_NAN);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
}
VisualShaderNodeIs::VisualShaderNodeIs() {
@@ -5475,14 +5699,14 @@ String VisualShaderNodeCompare::get_caption() const {
}
int VisualShaderNodeCompare::get_input_port_count() const {
- if (ctype == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
+ if (comparison_type == CTYPE_SCALAR && (func == FUNC_EQUAL || func == FUNC_NOT_EQUAL)) {
return 3;
}
return 2;
}
VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(int p_port) const {
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
@@ -5525,17 +5749,16 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const {
}
String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
- if (ctype == CTYPE_BOOLEAN || ctype == CTYPE_TRANSFORM) {
+ if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) {
if (func > FUNC_NOT_EQUAL) {
return TTR("Invalid comparison function for that type.");
}
}
-
return "";
}
String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *ops[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *operators[FUNC_MAX] = {
"==",
"!=",
">",
@@ -5544,7 +5767,7 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"<=",
};
- static const char *funcs[FUNC_LESS_THAN_EQUAL + 1] = {
+ static const char *functions[FUNC_MAX] = {
"equal($)",
"notEqual($)",
"greaterThan($)",
@@ -5553,31 +5776,31 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
"lessThanEqual($)",
};
- static const char *conds[COND_ANY + 1] = {
+ static const char *conditions[COND_MAX] = {
"all($)",
"any($)",
};
String code;
- switch (ctype) {
+ switch (comparison_type) {
case CTYPE_SCALAR:
if (func == FUNC_EQUAL) {
code += " " + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else if (func == FUNC_NOT_EQUAL) {
code += " " + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else {
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
}
break;
case CTYPE_SCALAR_INT:
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_VECTOR:
code += " {\n";
- code += " bvec3 _bv = " + String(funcs[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
- code += " " + p_output_vars[0] + " = " + String(conds[condition]).replace("$", "_bv") + ";\n";
+ code += " bvec3 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
code += " }\n";
break;
@@ -5585,14 +5808,14 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
case CTYPE_TRANSFORM:
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
- code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", ops[func]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
break;
default:
@@ -5601,10 +5824,12 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
return code;
}
-void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
- ctype = p_type;
-
- switch (ctype) {
+void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_type) {
+ ERR_FAIL_INDEX(int(p_comparison_type), int(CTYPE_MAX));
+ if (comparison_type == p_comparison_type) {
+ return;
+ }
+ switch (p_comparison_type) {
case CTYPE_SCALAR:
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 0.0);
@@ -5630,15 +5855,22 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) {
set_input_port_default_value(1, Transform3D());
simple_decl = true;
break;
+ default:
+ break;
}
+ comparison_type = p_comparison_type;
emit_changed();
}
VisualShaderNodeCompare::ComparisonType VisualShaderNodeCompare::get_comparison_type() const {
- return ctype;
+ return comparison_type;
}
void VisualShaderNodeCompare::set_function(Function p_func) {
+ ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
+ if (func == p_func) {
+ return;
+ }
func = p_func;
emit_changed();
}
@@ -5647,8 +5879,12 @@ VisualShaderNodeCompare::Function VisualShaderNodeCompare::get_function() const
return func;
}
-void VisualShaderNodeCompare::set_condition(Condition p_cond) {
- condition = p_cond;
+void VisualShaderNodeCompare::set_condition(Condition p_condition) {
+ ERR_FAIL_INDEX(int(p_condition), int(COND_MAX));
+ if (condition == p_condition) {
+ return;
+ }
+ condition = p_condition;
emit_changed();
}
@@ -5660,7 +5896,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
props.push_back("function");
- if (ctype == CTYPE_VECTOR) {
+ if (comparison_type == CTYPE_VECTOR) {
props.push_back("condition");
}
return props;
@@ -5685,6 +5921,7 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(CTYPE_VECTOR);
BIND_ENUM_CONSTANT(CTYPE_BOOLEAN);
BIND_ENUM_CONSTANT(CTYPE_TRANSFORM);
+ BIND_ENUM_CONSTANT(CTYPE_MAX);
BIND_ENUM_CONSTANT(FUNC_EQUAL);
BIND_ENUM_CONSTANT(FUNC_NOT_EQUAL);
@@ -5692,9 +5929,11 @@ void VisualShaderNodeCompare::_bind_methods() {
BIND_ENUM_CONSTANT(FUNC_GREATER_THAN_EQUAL);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN);
BIND_ENUM_CONSTANT(FUNC_LESS_THAN_EQUAL);
+ BIND_ENUM_CONSTANT(FUNC_MAX);
BIND_ENUM_CONSTANT(COND_ALL);
BIND_ENUM_CONSTANT(COND_ANY);
+ BIND_ENUM_CONSTANT(COND_MAX);
}
VisualShaderNodeCompare::VisualShaderNodeCompare() {
@@ -5752,7 +5991,7 @@ String VisualShaderNodeMultiplyAdd::generate_code(Shader::Mode p_mode, VisualSha
}
void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
if (op_type == p_op_type) {
return;
}
@@ -5875,7 +6114,10 @@ bool VisualShaderNodeBillboard::is_show_prop_names() const {
}
void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
- ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX);
+ ERR_FAIL_INDEX(int(p_billboard_type), int(BILLBOARD_TYPE_MAX));
+ if (billboard_type == p_billboard_type) {
+ return;
+ }
billboard_type = p_billboard_type;
simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED);
set_disabled(simple_decl);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 33a45a4384..2c952300fe 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -76,7 +76,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(float p_value);
+ void set_constant(float p_constant);
float get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -106,7 +106,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(int p_value);
+ void set_constant(int p_constant);
int get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -136,7 +136,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(bool p_value);
+ void set_constant(bool p_constant);
bool get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -167,7 +167,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Color p_value);
+ void set_constant(const Color &p_constant);
Color get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -197,7 +197,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Vector3 p_value);
+ void set_constant(const Vector3 &p_constant);
Vector3 get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -227,7 +227,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_constant(Transform3D p_value);
+ void set_constant(const Transform3D &p_constant);
Transform3D get_constant() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -251,12 +251,14 @@ public:
SOURCE_2D_NORMAL,
SOURCE_DEPTH,
SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -287,10 +289,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_texture(Ref<Texture2D> p_value);
+ void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -327,7 +329,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveTexture> p_value);
+ void set_texture(Ref<CurveTexture> p_texture);
Ref<CurveTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -360,7 +362,7 @@ public:
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_texture(Ref<CurveXYZTexture> p_value);
+ void set_texture(Ref<CurveXYZTexture> p_texture);
Ref<CurveXYZTexture> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -378,6 +380,7 @@ public:
enum Source {
SOURCE_TEXTURE,
SOURCE_PORT,
+ SOURCE_MAX,
};
protected:
@@ -410,7 +413,7 @@ VARIANT_ENUM_CAST(VisualShaderNodeSample3D::Source)
class VisualShaderNodeTexture2DArray : public VisualShaderNodeSample3D {
GDCLASS(VisualShaderNodeTexture2DArray, VisualShaderNodeSample3D);
- Ref<Texture2DArray> texture;
+ Ref<Texture2DArray> texture_array;
protected:
static void _bind_methods();
@@ -423,7 +426,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture_array(Ref<Texture2DArray> p_value);
+ void set_texture_array(Ref<Texture2DArray> p_texture_array);
Ref<Texture2DArray> get_texture_array() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -446,7 +449,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture(Ref<Texture3D> p_value);
+ void set_texture(Ref<Texture3D> p_texture);
Ref<Texture3D> get_texture() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -461,13 +464,15 @@ class VisualShaderNodeCubemap : public VisualShaderNode {
public:
enum Source {
SOURCE_TEXTURE,
- SOURCE_PORT
+ SOURCE_PORT,
+ SOURCE_MAX,
};
enum TextureType {
TYPE_DATA,
TYPE_COLOR,
- TYPE_NORMAL_MAP
+ TYPE_NORMAL_MAP,
+ TYPE_MAX,
};
private:
@@ -497,10 +502,10 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_cube_map(Ref<Cubemap> p_value);
+ void set_cube_map(Ref<Cubemap> p_cube_map);
Ref<Cubemap> get_cube_map() const;
- void set_texture_type(TextureType p_type);
+ void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -530,7 +535,8 @@ public:
OP_MAX,
OP_MIN,
OP_ATAN2,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -573,6 +579,7 @@ public:
OP_MOD,
OP_MAX,
OP_MIN,
+ OP_ENUM_SIZE,
};
protected:
@@ -619,7 +626,8 @@ public:
OP_CROSS,
OP_ATAN2,
OP_REFLECT,
- OP_STEP
+ OP_STEP,
+ OP_ENUM_SIZE,
};
protected:
@@ -665,7 +673,8 @@ public:
OP_DODGE,
OP_BURN,
OP_SOFT_LIGHT,
- OP_HARD_LIGHT
+ OP_HARD_LIGHT,
+ OP_MAX,
};
protected:
@@ -696,19 +705,25 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator)
-///////////////////////////////////////
-/// TRANSFORM-TRANSFORM MULTIPLICATION
-///////////////////////////////////////
+////////////////////////////////
+/// TRANSFORM-TRANSFORM OPERATOR
+////////////////////////////////
-class VisualShaderNodeTransformMult : public VisualShaderNode {
- GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode);
+class VisualShaderNodeTransformOp : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeTransformOp, VisualShaderNode);
public:
enum Operator {
OP_AxB,
OP_BxA,
OP_AxB_COMP,
- OP_BxA_COMP
+ OP_BxA_COMP,
+ OP_ADD,
+ OP_A_MINUS_B,
+ OP_B_MINUS_A,
+ OP_A_DIV_B,
+ OP_B_DIV_A,
+ OP_MAX,
};
protected:
@@ -734,10 +749,10 @@ public:
virtual Vector<StringName> get_editable_properties() const override;
- VisualShaderNodeTransformMult();
+ VisualShaderNodeTransformOp();
};
-VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator)
+VARIANT_ENUM_CAST(VisualShaderNodeTransformOp::Operator)
///////////////////////////////////////
/// TRANSFORM-VECTOR MULTIPLICATION
@@ -752,6 +767,7 @@ public:
OP_BxA,
OP_3x3_AxB,
OP_3x3_BxA,
+ OP_MAX,
};
protected:
@@ -822,7 +838,8 @@ public:
FUNC_RECIPROCAL,
FUNC_ROUNDEVEN,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -865,6 +882,7 @@ public:
FUNC_ABS,
FUNC_NEGATE,
FUNC_SIGN,
+ FUNC_MAX,
};
protected:
@@ -938,7 +956,8 @@ public:
FUNC_TAN,
FUNC_TANH,
FUNC_TRUNC,
- FUNC_ONEMINUS
+ FUNC_ONEMINUS,
+ FUNC_MAX,
};
protected:
@@ -979,7 +998,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode {
public:
enum Function {
FUNC_GRAYSCALE,
- FUNC_SEPIA
+ FUNC_SEPIA,
+ FUNC_MAX,
};
protected:
@@ -1020,7 +1040,8 @@ class VisualShaderNodeTransformFunc : public VisualShaderNode {
public:
enum Function {
FUNC_INVERSE,
- FUNC_TRANSPOSE
+ FUNC_TRANSPOSE,
+ FUNC_MAX,
};
protected:
@@ -1086,7 +1107,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_function(Function p_op);
+ void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1195,7 +1216,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1218,7 +1239,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1258,7 +1280,8 @@ public:
enum Function {
FUNC_SUM,
FUNC_X,
- FUNC_Y
+ FUNC_Y,
+ FUNC_MAX,
};
protected:
@@ -1365,7 +1388,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1407,7 +1430,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1495,7 +1518,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -1607,6 +1630,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1673,6 +1697,7 @@ public:
HINT_NONE,
HINT_RANGE,
HINT_RANGE_STEP,
+ HINT_MAX,
};
private:
@@ -1913,11 +1938,13 @@ public:
TYPE_COLOR,
TYPE_NORMAL_MAP,
TYPE_ANISO,
+ TYPE_MAX,
};
enum ColorDefault {
COLOR_DEFAULT_WHITE,
- COLOR_DEFAULT_BLACK
+ COLOR_DEFAULT_BLACK,
+ COLOR_DEFAULT_MAX,
};
protected:
@@ -2107,7 +2134,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -2155,6 +2182,7 @@ public:
enum Function {
FUNC_IS_INF,
FUNC_IS_NAN,
+ FUNC_MAX,
};
protected:
@@ -2200,6 +2228,7 @@ public:
CTYPE_VECTOR,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
+ CTYPE_MAX,
};
enum Function {
@@ -2209,15 +2238,17 @@ public:
FUNC_GREATER_THAN_EQUAL,
FUNC_LESS_THAN,
FUNC_LESS_THAN_EQUAL,
+ FUNC_MAX,
};
enum Condition {
COND_ALL,
COND_ANY,
+ COND_MAX,
};
protected:
- ComparisonType ctype = CTYPE_SCALAR;
+ ComparisonType comparison_type = CTYPE_SCALAR;
Function func = FUNC_EQUAL;
Condition condition = COND_ALL;
@@ -2285,7 +2316,7 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- void set_op_type(OpType p_type);
+ void set_op_type(OpType p_op_type);
OpType get_op_type() const;
virtual Vector<StringName> get_editable_properties() const override;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 2250cf8d95..5fe801e037 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -402,15 +402,16 @@ String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, Vi
}
void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
- ERR_FAIL_INDEX((int)p_op_type, OP_TYPE_MAX);
- if (p_op_type != op_type) {
- if (p_op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- } else {
- set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- }
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ if (p_op_type == OP_TYPE_SCALAR) {
+ set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(1, 1.0);
+ } else {
+ set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
}
op_type = p_op_type;
emit_changed();
@@ -500,8 +501,6 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
code += " __vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n";
code += " " + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n";
break;
- case MODE_MAX:
- break;
default:
break;
}
@@ -510,6 +509,10 @@ String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, V
}
void VisualShaderNodeParticleAccelerator::set_mode(Mode p_mode) {
+ ERR_FAIL_INDEX(int(p_mode), int(MODE_MAX));
+ if (mode == p_mode) {
+ return;
+ }
mode = p_mode;
emit_changed();
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index ecd187a885..f5435c3511 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -181,8 +181,8 @@ VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType)
// Process nodes
-class VisualShaderNodeParticleAccelerator : public VisualShaderNodeOutput {
- GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNodeOutput);
+class VisualShaderNodeParticleAccelerator : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNode);
public:
enum Mode {
diff --git a/scene/resources/line_shape_2d.cpp b/scene/resources/world_margin_shape_2d.cpp
index d206f12287..3b43681528 100644
--- a/scene/resources/line_shape_2d.cpp
+++ b/scene/resources/world_margin_shape_2d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.cpp */
+/* world_margin_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "line_shape_2d.h"
+#include "world_margin_shape_2d.h"
#include "core/math/geometry_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
Vector2 point = get_distance() * get_normal();
Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } };
@@ -48,7 +48,7 @@ bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tol
return false;
}
-void LineShape2D::_update_shape() {
+void WorldMarginShape2D::_update_shape() {
Array arr;
arr.push_back(normal);
arr.push_back(distance);
@@ -56,25 +56,25 @@ void LineShape2D::_update_shape() {
emit_changed();
}
-void LineShape2D::set_normal(const Vector2 &p_normal) {
+void WorldMarginShape2D::set_normal(const Vector2 &p_normal) {
normal = p_normal;
_update_shape();
}
-void LineShape2D::set_distance(real_t p_distance) {
+void WorldMarginShape2D::set_distance(real_t p_distance) {
distance = p_distance;
_update_shape();
}
-Vector2 LineShape2D::get_normal() const {
+Vector2 WorldMarginShape2D::get_normal() const {
return normal;
}
-real_t LineShape2D::get_distance() const {
+real_t WorldMarginShape2D::get_distance() const {
return distance;
}
-void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -83,7 +83,7 @@ void LineShape2D::draw(const RID &p_to_rid, const Color &p_color) {
RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3);
}
-Rect2 LineShape2D::get_rect() const {
+Rect2 WorldMarginShape2D::get_rect() const {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -96,22 +96,22 @@ Rect2 LineShape2D::get_rect() const {
return rect;
}
-real_t LineShape2D::get_enclosing_radius() const {
+real_t WorldMarginShape2D::get_enclosing_radius() const {
return distance;
}
-void LineShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_normal", "normal"), &LineShape2D::set_normal);
- ClassDB::bind_method(D_METHOD("get_normal"), &LineShape2D::get_normal);
+void WorldMarginShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal);
+ ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal);
- ClassDB::bind_method(D_METHOD("set_distance", "distance"), &LineShape2D::set_distance);
- ClassDB::bind_method(D_METHOD("get_distance"), &LineShape2D::get_distance);
+ ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance);
+ ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
}
-LineShape2D::LineShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->line_shape_create()) {
+WorldMarginShape2D::WorldMarginShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) {
_update_shape();
}
diff --git a/scene/resources/line_shape_2d.h b/scene/resources/world_margin_shape_2d.h
index 210a1aa9e6..3c1d593ffe 100644
--- a/scene/resources/line_shape_2d.h
+++ b/scene/resources/world_margin_shape_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* line_shape_2d.h */
+/* world_margin_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LINE_SHAPE_2D_H
-#define LINE_SHAPE_2D_H
+#ifndef WORLD_MARGIN_SHAPE_2D_H
+#define WORLD_MARGIN_SHAPE_2D_H
#include "scene/resources/shape_2d.h"
-class LineShape2D : public Shape2D {
- GDCLASS(LineShape2D, Shape2D);
+class WorldMarginShape2D : public Shape2D {
+ GDCLASS(WorldMarginShape2D, Shape2D);
- // LineShape2D is often used for one-way platforms, where the normal pointing up makes sense.
+ // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense.
Vector2 normal = Vector2(0, -1);
real_t distance = 0.0;
@@ -58,7 +58,7 @@ public:
virtual Rect2 get_rect() const override;
virtual real_t get_enclosing_radius() const override;
- LineShape2D();
+ WorldMarginShape2D();
};
-#endif // LINE_SHAPE_2D_H
+#endif // WORLD_MARGIN_SHAPE_2D_H