diff options
Diffstat (limited to 'scene/resources')
88 files changed, 10657 insertions, 3317 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 6f64ac6d04..640ec50eb9 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -43,8 +43,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { if (tracks.size() == track && what == "type") { String type = p_value; - if (type == "transform") { - add_track(TYPE_TRANSFORM); + if (type == "transform" || type == "transform3d") { + add_track(TYPE_TRANSFORM3D); } else if (type == "value") { add_track(TYPE_VALUE); } else if (type == "method") { @@ -75,7 +75,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "enabled") { track_set_enabled(track, p_value); } else if (what == "keys" || what == "key_values") { - if (track_get_type(track) == TYPE_TRANSFORM) { + if (track_get_type(track) == TYPE_TRANSFORM3D) { TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]); Vector<float> values = p_value; int vcount = values.size(); @@ -318,7 +318,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_INDEX_V(track, tracks.size(), false); if (what == "type") { switch (track_get_type(track)) { - case TYPE_TRANSFORM: + case TYPE_TRANSFORM3D: r_ret = "transform"; break; case TYPE_VALUE: @@ -351,7 +351,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "enabled") { r_ret = track_is_enabled(track); } else if (what == "keys") { - if (track_get_type(track) == TYPE_TRANSFORM) { + if (track_get_type(track) == TYPE_TRANSFORM3D) { Vector<float> keys; int kk = track_get_key_count(track); keys.resize(kk * 12); @@ -361,7 +361,7 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { int idx = 0; for (int i = 0; i < track_get_key_count(track); i++) { Vector3 loc; - Quat rot; + Quaternion rot; Vector3 scale; transform_track_get_key(track, i, &loc, &rot, &scale); @@ -590,7 +590,7 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } switch (p_type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = memnew(TransformTrack); tracks.insert(p_at_pos, tt); } break; @@ -628,7 +628,7 @@ void Animation::remove_track(int p_track) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); _clear(tt->transforms); @@ -671,7 +671,7 @@ int Animation::get_track_count() const { } Animation::TrackType Animation::track_get_type(int p_track) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM); + ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D); return tracks[p_track]->type; } @@ -773,12 +773,12 @@ void Animation::_clear(T &p_keys) { p_keys.clear(); } -Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) const { +Error Animation::transform_track_get_key(int p_track, int p_key, 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]; TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER); if (r_loc) { @@ -794,10 +794,10 @@ 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 Quat &p_rot, const Vector3 &p_scale) { +int Animation::transform_track_insert_key(int p_track, float 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_TRANSFORM, -1); + ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1); TransformTrack *tt = static_cast<TransformTrack *>(t); @@ -823,7 +823,7 @@ void Animation::track_remove_key(int p_track, int p_idx) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_idx, tt->transforms.size()); tt->transforms.remove(p_idx); @@ -869,7 +869,7 @@ int Animation::track_find_key(int p_track, float p_time, bool p_exact) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); int k = _find(tt->transforms, p_time); if (k < 0 || k >= tt->transforms.size()) { @@ -951,14 +951,14 @@ void Animation::track_insert_key(int p_track, float p_time, const Variant &p_key Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { Dictionary d = p_key; Vector3 loc; if (d.has("location")) { loc = d["location"]; } - Quat rot; + Quaternion rot; if (d.has("rotation")) { rot = d["rotation"]; } @@ -1053,7 +1053,7 @@ int Animation::track_get_key_count(int p_track) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); return tt->transforms.size(); } break; @@ -1088,7 +1088,7 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant()); @@ -1156,7 +1156,7 @@ float Animation::track_get_key_time(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); return tt->transforms[p_key_idx].time; @@ -1201,7 +1201,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, float p_time) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); TKey<TransformKey> key = tt->transforms[p_key_idx]; @@ -1265,7 +1265,7 @@ float Animation::track_get_key_transition(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); return tt->transforms[p_key_idx].transition; @@ -1301,7 +1301,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); @@ -1384,7 +1384,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); tt->transforms.write[p_key_idx].transition = p_transition; @@ -1462,7 +1462,7 @@ Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_ return p_a.lerp(p_b, p_c); } -Quat Animation::_interpolate(const Quat &p_a, const Quat &p_b, float p_c) const { +Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, float p_c) const { return p_a.slerp(p_b, p_c); } @@ -1490,7 +1490,7 @@ Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); } -Quat Animation::_cubic_interpolate(const Quat &p_pre_a, const Quat &p_a, const Quat &p_b, const Quat &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, float p_c) const { return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c); } @@ -1555,11 +1555,11 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a return a.cubic_interpolate(b, pa, pb, p_c); } - case Variant::QUAT: { - Quat a = p_a; - Quat b = p_b; - Quat pa = p_pre_a; - Quat pb = p_post_b; + case Variant::QUATERNION: { + Quaternion a = p_a; + Quaternion b = p_b; + Quaternion pa = p_pre_a; + Quaternion pb = p_post_b; return a.cubic_slerp(b, pa, pb, p_c); } @@ -1728,10 +1728,10 @@ 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, Quat *r_rot, Vector3 *r_scale) const { +Error Animation::transform_track_interpolate(int p_track, float 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_TRANSFORM, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); TransformTrack *tt = static_cast<TransformTrack *>(t); @@ -1932,7 +1932,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float // handle loop by splitting switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { const TransformTrack *tt = static_cast<const TransformTrack *>(t); _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices); _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices); @@ -1988,7 +1988,7 @@ void Animation::track_get_key_indices_in_range(int p_track, float p_time, float } switch (t->type) { - case TYPE_TRANSFORM: { + case TYPE_TRANSFORM3D: { const TransformTrack *tt = static_cast<const TransformTrack *>(t); _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices); @@ -2681,7 +2681,7 @@ void Animation::_bind_methods() { ADD_SIGNAL(MethodInfo("tracks_changed")); BIND_ENUM_CONSTANT(TYPE_VALUE); - BIND_ENUM_CONSTANT(TYPE_TRANSFORM); + BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D); BIND_ENUM_CONSTANT(TYPE_METHOD); BIND_ENUM_CONSTANT(TYPE_BEZIER); BIND_ENUM_CONSTANT(TYPE_AUDIO); @@ -2751,9 +2751,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons { //rotation - const Quat &q0 = t0.value.rot; - const Quat &q1 = t1.value.rot; - const Quat &q2 = t2.value.rot; + const Quaternion &q0 = t0.value.rot; + const Quaternion &q1 = t1.value.rot; + const Quaternion &q2 = t2.value.rot; //localize both to rotation from q0 @@ -2763,8 +2763,8 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons } } else { - Quat r02 = (q0.inverse() * q2).normalized(); - Quat r01 = (q0.inverse() * q1).normalized(); + Quaternion r02 = (q0.inverse() * q2).normalized(); + Quaternion r01 = (q0.inverse() * q1).normalized(); Vector3 v02, v01; real_t a02, a01; @@ -2877,7 +2877,7 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err, float p_allowed_angular_err, float p_max_optimizable_angle) { ERR_FAIL_INDEX(p_idx, tracks.size()); - ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D); TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]); bool prev_erased = false; TKey<TransformKey> first_erased; @@ -2917,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) { for (int i = 0; i < tracks.size(); i++) { - if (tracks[i]->type == TYPE_TRANSFORM) { + 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 66bc71c834..1484007333 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -42,7 +42,7 @@ class Animation : public Resource { public: enum TrackType { TYPE_VALUE, ///< Set a value in a property, can be interpolated. - TYPE_TRANSFORM, ///< Transform a node or a bone. + TYPE_TRANSFORM3D, ///< Transform a node or a bone. TYPE_METHOD, ///< Call any method on a specific node. TYPE_BEZIER, ///< Bezier curve TYPE_AUDIO, @@ -88,7 +88,7 @@ private: struct TransformKey { Vector3 loc; - Quat rot; + Quaternion rot; Vector3 scale; }; @@ -97,7 +97,7 @@ private: struct TransformTrack : public Track { Vector<TKey<TransformKey>> transforms; - TransformTrack() { type = TYPE_TRANSFORM; } + TransformTrack() { type = TYPE_TRANSFORM3D; } }; /* PROPERTY VALUE TRACK */ @@ -186,13 +186,13 @@ private: _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, float p_c) const; _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, float p_c) const; - _FORCE_INLINE_ Quat _interpolate(const Quat &p_a, const Quat &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_ 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_ Quat _cubic_interpolate(const Quat &p_pre_a, const Quat &p_a, const Quat &p_b, const Quat &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; @@ -213,7 +213,7 @@ private: private: Array _transform_track_interpolate(int p_track, float p_time) const { Vector3 loc; - Quat rot; + Quaternion rot; Vector3 scale; transform_track_interpolate(p_track, p_time, &loc, &rot, &scale); Array ret; @@ -291,8 +291,8 @@ public: 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; - int transform_track_insert_key(int p_track, float p_time, const Vector3 &p_loc, const Quat &p_rot = Quat(), const Vector3 &p_scale = Vector3()); - Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quat *r_rot, Vector3 *r_scale) 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()); + 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; @@ -321,7 +321,7 @@ public: 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, Quat *r_rot, Vector3 *r_scale) const; + Error transform_track_interpolate(int p_track, float 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; diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 9a9f019dda..8ffd2df112 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -30,8 +30,8 @@ #include "audio_stream_sample.h" +#include "core/io/file_access.h" #include "core/io/marshalls.h" -#include "core/os/file_access.h" void AudioStreamPlaybackSample::start(float p_from_pos) { if (base->format == AudioStreamSample::FORMAT_IMA_ADPCM) { @@ -596,7 +596,7 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { Ref<AudioStreamPlaybackSample> sample; - sample.instance(); + sample.instantiate(); sample->base = Ref<AudioStreamSample>(this); return sample; } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 0ffeb8a5bf..de557494c3 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -487,7 +487,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo Point2i from; Ref<BitMap> fill; - fill.instance(); + fill.instantiate(); fill->create(get_size()); Vector<Vector<Vector2>> polygons; @@ -525,7 +525,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); Ref<BitMap> copy; - copy.instance(); + copy.instantiate(); copy->create(get_size()); copy->bitmask = bitmask; @@ -604,7 +604,7 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con void BitMap::resize(const Size2 &p_new_size) { Ref<BitMap> new_bitmap; - new_bitmap.instance(); + new_bitmap.instantiate(); new_bitmap->create(p_new_size); int lw = MIN(width, p_new_size.width); int lh = MIN(height, p_new_size.height); @@ -621,7 +621,7 @@ void BitMap::resize(const Size2 &p_new_size) { Ref<Image> BitMap::convert_to_image() const { Ref<Image> image; - image.instance(); + image.instantiate(); image->create(width, height, false, Image::FORMAT_L8); for (int i = 0; i < width; i++) { diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp index 6e7adc0bd7..008914c5ee 100644 --- a/scene/resources/box_shape_3d.cpp +++ b/scene/resources/box_shape_3d.cpp @@ -56,6 +56,26 @@ void BoxShape3D::_update_shape() { Shape3D::_update_shape(); } +#ifndef DISABLE_DEPRECATED +bool BoxShape3D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "extents") { // Compatibility with Godot 3.x. + // Convert to `size`, twice as big. + set_size((Vector3)p_value * 2); + return true; + } + return false; +} + +bool BoxShape3D::_get(const StringName &p_name, Variant &r_property) const { + if (p_name == "extents") { // Compatibility with Godot 3.x. + // Convert to `extents`, half as big. + r_property = size / 2; + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void BoxShape3D::set_size(const Vector3 &p_size) { size = p_size; _update_shape(); diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h index fce05d61ed..91978a0e6a 100644 --- a/scene/resources/box_shape_3d.h +++ b/scene/resources/box_shape_3d.h @@ -39,6 +39,10 @@ class BoxShape3D : public Shape3D { protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_property) const; +#endif // DISABLE_DEPRECATED virtual void _update_shape() override; diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index 34c6bc05bc..b633196424 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -175,11 +175,11 @@ void CameraEffects::_bind_methods() { ADD_GROUP("DOF Blur", "dof_blur_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); // Override exposure diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp index 9e030bc077..6b895da606 100644 --- a/scene/resources/convex_polygon_shape_3d.cpp +++ b/scene/resources/convex_polygon_shape_3d.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "convex_polygon_shape_3d.h" -#include "core/math/quick_hull.h" +#include "core/math/convex_hull.h" #include "servers/physics_server_3d.h" Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { @@ -38,7 +38,7 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { if (points.size() > 3) { Vector<Vector3> varr = Variant(points); Geometry3D::MeshData md; - Error err = QuickHull::build(varr, md); + Error err = ConvexHullComputer::convex_hull(varr, md); if (err == OK) { Vector<Vector3> lines; lines.resize(md.edges.size() * 2); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 846da39221..53979e16df 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -286,7 +286,7 @@ void Curve::set_min_value(float p_min) { } // Note: min and max are indicative values, // it's still possible that existing points are out of range at this point. - emit_signal(SIGNAL_RANGE_CHANGED); + emit_signal(SNAME(SIGNAL_RANGE_CHANGED)); } void Curve::set_max_value(float p_max) { @@ -296,7 +296,7 @@ void Curve::set_max_value(float p_max) { _minmax_set_once |= 0b01; // second bit is "max set" _max_value = p_max; } - emit_signal(SIGNAL_RANGE_CHANGED); + emit_signal(SNAME(SIGNAL_RANGE_CHANGED)); } real_t Curve::interpolate(real_t offset) const { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b671ee4644..1bbb84f43d 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -390,6 +390,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("shadow_outline_size", "Label", 1 * scale); theme->set_constant("line_spacing", "Label", 3 * scale); + theme->set_type_variation("HeaderSmall", "Label"); + theme->set_font_size("font_size", "HeaderSmall", default_font_size + 4); + + theme->set_type_variation("HeaderMedium", "Label"); + theme->set_font_size("font_size", "HeaderMedium", default_font_size + 8); + + theme->set_type_variation("HeaderLarge", "Label"); + theme->set_font_size("font_size", "HeaderLarge", default_font_size + 12); + // LineEdit theme->set_stylebox("normal", "LineEdit", make_stylebox(line_edit_png, 5, 5, 5, 5)); @@ -432,7 +441,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_stylebox("focus", "TextEdit", focus); theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0)); - theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_icon("tab", "TextEdit", make_icon(tab_png)); theme->set_icon("space", "TextEdit", make_icon(space_png)); @@ -441,27 +449,17 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font_size("font_size", "TextEdit", -1); theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0)); - theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2)); - theme->set_color("completion_selected_color", "TextEdit", Color(0.26, 0.26, 0.27)); - theme->set_color("completion_existing_color", "TextEdit", Color(0.87, 0.87, 0.87, 0.13)); - theme->set_color("completion_scroll_color", "TextEdit", control_font_pressed_color); - theme->set_color("completion_font_color", "TextEdit", Color(0.67, 0.67, 0.67)); theme->set_color("font_color", "TextEdit", control_font_color); theme->set_color("font_selected_color", "TextEdit", Color(0, 0, 0)); theme->set_color("font_readonly_color", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f)); theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1)); theme->set_color("selection_color", "TextEdit", control_selection_color); - theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4)); - theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8)); theme->set_color("caret_color", "TextEdit", control_font_color); theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0)); theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2)); theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15)); - theme->set_constant("completion_lines", "TextEdit", 7); - theme->set_constant("completion_max_width", "TextEdit", 50); - theme->set_constant("completion_scroll_width", "TextEdit", 3); theme->set_constant("line_spacing", "TextEdit", 4 * scale); theme->set_constant("outline_size", "TextEdit", 0); @@ -479,6 +477,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png)); theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png)); theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png)); + theme->set_icon("folded_eol_icon", "CodeEdit", make_icon(ellipsis_png)); theme->set_font("font", "CodeEdit", Ref<Font>()); theme->set_font_size("font_size", "CodeEdit", -1); @@ -494,12 +493,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f)); theme->set_color("font_outline_color", "CodeEdit", Color(1, 1, 1)); theme->set_color("selection_color", "CodeEdit", control_selection_color); - theme->set_color("mark_color", "CodeEdit", Color(1.0, 0.4, 0.4, 0.4)); theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8)); theme->set_color("breakpoint_color", "CodeEdit", Color(0.9, 0.29, 0.3)); theme->set_color("executing_line_color", "CodeEdit", Color(0.98, 0.89, 0.27)); - theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("current_line_color", "CodeEdit", Color(0.25, 0.25, 0.26, 0.8)); + theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("caret_color", "CodeEdit", control_font_color); theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0)); theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2)); @@ -570,7 +568,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // ScrollContainer Ref<StyleBoxEmpty> empty; - empty.instance(); + empty.instantiate(); theme->set_stylebox("bg", "ScrollContainer", empty); // WindowDialog @@ -721,6 +719,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("guide_color", "Tree", Color(0, 0, 0, 0.1)); theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2)); theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27)); + theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27)); + theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27)); theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color); theme->set_constant("hseparation", "Tree", 4 * scale); @@ -728,6 +728,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("item_margin", "Tree", 12 * scale); theme->set_constant("button_margin", "Tree", 4 * scale); theme->set_constant("draw_relationship_lines", "Tree", 0); + theme->set_constant("relationship_line_width", "Tree", 1); + theme->set_constant("parent_hl_line_width", "Tree", 1); + theme->set_constant("children_hl_line_width", "Tree", 1); + theme->set_constant("parent_hl_line_margin", "Tree", 0); theme->set_constant("draw_guides", "Tree", 1); theme->set_constant("scroll_border", "Tree", 4); theme->set_constant("scroll_speed", "Tree", 12); @@ -984,12 +988,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const void make_default_theme(bool p_hidpi, Ref<Font> p_font) { Ref<Theme> t; - t.instance(); + t.instantiate(); Ref<StyleBox> default_style; Ref<Texture2D> default_icon; Ref<Font> default_font; - int default_font_size = 16; + if (p_font.is_valid()) { // Use the custom font defined in the Project Settings. default_font = p_font; @@ -998,10 +1002,10 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { // The default DynamicFont is chosen to have a small file size since it's // embedded in both editor and export template binaries. Ref<Font> dynamic_font; - dynamic_font.instance(); + dynamic_font.instantiate(); Ref<FontData> dynamic_font_data; - dynamic_font_data.instance(); + dynamic_font_data.instantiate(); dynamic_font_data->load_memory(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size, "ttf", default_font_size); dynamic_font->add_data(dynamic_font_data); diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index a7b2bec5a4..4cd781e814 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -33,6 +33,8 @@ #include "scene/resources/theme.h" +const int default_font_size = 16; + void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); void make_default_theme(bool p_hidpi, Ref<Font> p_font); void clear_default_theme(); diff --git a/scene/resources/default_theme/ellipsis.png b/scene/resources/default_theme/ellipsis.png Binary files differnew file mode 100644 index 0000000000..c949e2c95b --- /dev/null +++ b/scene/resources/default_theme/ellipsis.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index 190f2a03d9..7d747e3c9e 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -74,6 +74,10 @@ static const unsigned char dropdown_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, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char ellipsis_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc9, 0x11, 0xce, 0xcc, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x78, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x95, 0xd1, 0x31, 0xa, 0xc2, 0x50, 0x10, 0x4, 0xd0, 0xb7, 0x1f, 0xf, 0x60, 0x67, 0xa, 0xf, 0x12, 0x6f, 0x60, 0xe9, 0x51, 0x5, 0x3d, 0x44, 0xee, 0x61, 0xa1, 0xa5, 0xf6, 0x81, 0xb5, 0xc8, 0x47, 0x82, 0x84, 0x4f, 0xb2, 0xdd, 0xb0, 0x33, 0xb3, 0xb3, 0x4c, 0x40, 0x66, 0xee, 0x71, 0xc2, 0x1, 0x3b, 0xcb, 0x33, 0xe2, 0x85, 0x21, 0x22, 0xde, 0x51, 0x45, 0x97, 0x86, 0x60, 0xc9, 0xe0, 0x5a, 0xea, 0xa5, 0xb5, 0x22, 0x95, 0xdb, 0x97, 0x1a, 0xf, 0x6e, 0xb8, 0xcf, 0x8, 0x2d, 0xdc, 0x95, 0xd9, 0x22, 0xfe, 0x9c, 0x9b, 0x38, 0x32, 0xf3, 0x8c, 0xe3, 0x86, 0xa8, 0xf0, 0x28, 0x18, 0x4c, 0xf, 0xaf, 0x9d, 0x11, 0x43, 0xf0, 0xab, 0xa3, 0x47, 0xa7, 0x5d, 0xc7, 0xd3, 0x54, 0xc7, 0xe7, 0xb, 0xb9, 0xce, 0x1f, 0xc6, 0x2d, 0x99, 0x55, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char error_icon_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, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x0, 0x1, 0x0, 0x2, 0x10, 0x0, 0x1, 0x14, 0xc2, 0xc0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index c04b271d81..8550af8bcb 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1329,7 +1329,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.00,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 6f87c524d8..032171847d 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -813,7 +813,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size) const { if (cache.has(hash)) { buffer = cache.get(hash); } else { - buffer.instance(); + buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); @@ -838,7 +838,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { - lines_buffer.instance(); + lines_buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); @@ -870,7 +870,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t if (cache.has(hash)) { buffer = cache.get(hash); } else { - buffer.instance(); + buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); cache.insert(hash, buffer); @@ -905,7 +905,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S if (cache_wrap.has(wrp_hash)) { lines_buffer = cache_wrap.get(wrp_hash); } else { - lines_buffer.instance(); + lines_buffer.instantiate(); int size = p_size <= 0 ? data[0]->get_base_size() : p_size; lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); lines_buffer->set_width(p_width); @@ -1041,7 +1041,7 @@ RES ResourceFormatLoaderFont::load(const String &p_path, const String &p_origina } Ref<FontData> dfont; - dfont.instance(); + dfont.instantiate(); dfont->load_resource(p_path); if (r_error) { @@ -1096,11 +1096,11 @@ RES ResourceFormatLoaderCompatFont::load(const String &p_path, const String &p_o } Ref<FontData> dfont; - dfont.instance(); + dfont.instantiate(); dfont->load_resource(p_path); Ref<Font> font; - font.instance(); + font.instantiate(); font->add_data(dfont); if (r_error) { diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp new file mode 100644 index 0000000000..ebe65605f8 --- /dev/null +++ b/scene/resources/immediate_mesh.cpp @@ -0,0 +1,416 @@ +/*************************************************************************/ +/* immediate_mesh.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 "immediate_mesh.h" + +void ImmediateMesh::surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material) { + ERR_FAIL_COND_MSG(surface_active, "Already creating a new surface."); + active_surface_data.primitive = p_primitive; + active_surface_data.material = p_material; + surface_active = true; +} +void ImmediateMesh::surface_set_color(const Color &p_color) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + + if (!uses_colors) { + colors.resize(vertices.size()); + for (uint32_t i = 0; i < colors.size(); i++) { + colors[i] = p_color; + } + uses_colors = true; + } + + current_color = p_color; +} +void ImmediateMesh::surface_set_normal(const Vector3 &p_normal) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + + if (!uses_normals) { + normals.resize(vertices.size()); + for (uint32_t i = 0; i < normals.size(); i++) { + normals[i] = p_normal; + } + uses_normals = true; + } + + current_normal = p_normal; +} +void ImmediateMesh::surface_set_tangent(const Plane &p_tangent) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_tangents) { + tangents.resize(vertices.size()); + for (uint32_t i = 0; i < tangents.size(); i++) { + tangents[i] = p_tangent; + } + uses_tangents = true; + } + + current_tangent = p_tangent; +} +void ImmediateMesh::surface_set_uv(const Vector2 &p_uv) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_uvs) { + uvs.resize(vertices.size()); + for (uint32_t i = 0; i < uvs.size(); i++) { + uvs[i] = p_uv; + } + uses_uvs = true; + } + + current_uv = p_uv; +} +void ImmediateMesh::surface_set_uv2(const Vector2 &p_uv2) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + if (!uses_uv2s) { + uv2s.resize(vertices.size()); + for (uint32_t i = 0; i < uv2s.size(); i++) { + uv2s[i] = p_uv2; + } + uses_uv2s = true; + } + + current_uv2 = p_uv2; +} +void ImmediateMesh::surface_add_vertex(const Vector3 &p_vertex) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(vertices.size() && active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface."); + + if (uses_colors) { + colors.push_back(current_color); + } + if (uses_normals) { + normals.push_back(current_normal); + } + if (uses_tangents) { + tangents.push_back(current_tangent); + } + if (uses_uvs) { + uvs.push_back(current_uv); + } + if (uses_uv2s) { + uv2s.push_back(current_uv2); + } + vertices.push_back(p_vertex); +} + +void ImmediateMesh::surface_add_vertex_2d(const Vector2 &p_vertex) { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(vertices.size() && !active_surface_data.vertex_2d, "Can't mix 2D and 3D vertices in a surface."); + + if (uses_colors) { + colors.push_back(current_color); + } + if (uses_normals) { + normals.push_back(current_normal); + } + if (uses_tangents) { + tangents.push_back(current_tangent); + } + if (uses_uvs) { + uvs.push_back(current_uv); + } + if (uses_uv2s) { + uv2s.push_back(current_uv2); + } + Vector3 v(p_vertex.x, p_vertex.y, 0); + vertices.push_back(v); + + active_surface_data.vertex_2d = true; +} +void ImmediateMesh::surface_end() { + ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it."); + ERR_FAIL_COND_MSG(!vertices.size(), "No vertices were added, surface cant be created."); + + uint32_t format = ARRAY_FORMAT_VERTEX; + + uint32_t vertex_stride = 0; + if (active_surface_data.vertex_2d) { + format |= ARRAY_FLAG_USE_2D_VERTICES; + vertex_stride = sizeof(float) * 2; + } else { + vertex_stride = sizeof(float) * 3; + } + + uint32_t normal_offset = 0; + if (uses_normals) { + format |= ARRAY_FORMAT_NORMAL; + normal_offset = vertex_stride; + vertex_stride += sizeof(uint32_t); + } + uint32_t tangent_offset = 0; + if (uses_tangents) { + format |= ARRAY_FORMAT_TANGENT; + tangent_offset += vertex_stride; + vertex_stride += sizeof(uint32_t); + } + + AABB aabb; + + { + surface_vertex_create_cache.resize(vertex_stride * vertices.size()); + uint8_t *surface_vertex_ptr = surface_vertex_create_cache.ptrw(); + for (uint32_t i = 0; i < vertices.size(); i++) { + { + float *vtx = (float *)&surface_vertex_ptr[i * vertex_stride]; + vtx[0] = vertices[i].x; + vtx[1] = vertices[i].y; + if (!active_surface_data.vertex_2d) { + vtx[2] = vertices[i].z; + } + if (i == 0) { + aabb.position = vertices[i]; + } else { + aabb.expand_to(vertices[i]); + } + } + if (uses_normals) { + uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset]; + + Vector3 n = normals[i] * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + *normal = value; + } + if (uses_tangents) { + uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset]; + Plane t = tangents[i]; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + + *tangent = value; + } + } + } + + if (uses_colors || uses_uvs || uses_uv2s) { + uint32_t attribute_stride = 0; + + if (uses_colors) { + format |= ARRAY_FORMAT_COLOR; + attribute_stride += sizeof(uint8_t) * 4; + } + uint32_t uv_offset = 0; + if (uses_uvs) { + format |= ARRAY_FORMAT_TEX_UV; + uv_offset = attribute_stride; + attribute_stride += sizeof(float) * 2; + } + uint32_t uv2_offset = 0; + if (uses_uv2s) { + format |= ARRAY_FORMAT_TEX_UV2; + uv2_offset = attribute_stride; + attribute_stride += sizeof(float) * 2; + } + + surface_attribute_create_cache.resize(vertices.size() * attribute_stride); + + uint8_t *surface_attribute_ptr = surface_attribute_create_cache.ptrw(); + + for (uint32_t i = 0; i < vertices.size(); i++) { + if (uses_colors) { + uint8_t *color8 = (uint8_t *)&surface_attribute_ptr[i * attribute_stride]; + + color8[0] = uint8_t(CLAMP(colors[i].r * 255.0, 0.0, 255.0)); + color8[1] = uint8_t(CLAMP(colors[i].g * 255.0, 0.0, 255.0)); + color8[2] = uint8_t(CLAMP(colors[i].b * 255.0, 0.0, 255.0)); + color8[3] = uint8_t(CLAMP(colors[i].a * 255.0, 0.0, 255.0)); + } + if (uses_uvs) { + float *uv = (float *)&surface_attribute_ptr[i * attribute_stride + uv_offset]; + + uv[0] = uvs[i].x; + uv[1] = uvs[i].y; + } + + if (uses_uv2s) { + float *uv2 = (float *)&surface_attribute_ptr[i * attribute_stride + uv2_offset]; + + uv2[0] = uv2s[i].x; + uv2[1] = uv2s[i].y; + } + } + } + + RS::SurfaceData sd; + + sd.primitive = RS::PrimitiveType(active_surface_data.primitive); + sd.format = format; + sd.vertex_data = surface_vertex_create_cache; + if (uses_colors || uses_uvs || uses_uv2s) { + sd.attribute_data = surface_attribute_create_cache; + } + sd.vertex_count = vertices.size(); + sd.aabb = aabb; + if (active_surface_data.material.is_valid()) { + sd.material = active_surface_data.material->get_rid(); + } + + RS::get_singleton()->mesh_add_surface(mesh, sd); + + active_surface_data.aabb = aabb; + + active_surface_data.format = format; + active_surface_data.array_len = vertices.size(); + + surfaces.push_back(active_surface_data); + + colors.clear(); + normals.clear(); + tangents.clear(); + uvs.clear(); + uv2s.clear(); + vertices.clear(); + + uses_colors = false; + uses_normals = false; + uses_tangents = false; + uses_uvs = false; + uses_uv2s = false; + + surface_active = false; +} + +void ImmediateMesh::clear_surfaces() { + RS::get_singleton()->mesh_clear(mesh); + surfaces.clear(); + surface_active = false; + + colors.clear(); + normals.clear(); + tangents.clear(); + uvs.clear(); + uv2s.clear(); + vertices.clear(); + + uses_colors = false; + uses_normals = false; + uses_tangents = false; + uses_uvs = false; + uses_uv2s = false; +} + +int ImmediateMesh::get_surface_count() const { + return surfaces.size(); +} +int ImmediateMesh::surface_get_array_len(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), -1); + return surfaces[p_idx].array_len; +} +int ImmediateMesh::surface_get_array_index_len(int p_idx) const { + return 0; +} +bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const { + return false; +} +Array ImmediateMesh::surface_get_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array()); + return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface); +} +Array ImmediateMesh::surface_get_blend_shape_arrays(int p_surface) const { + return Array(); +} +Dictionary ImmediateMesh::surface_get_lods(int p_surface) const { + return Dictionary(); +} +uint32_t ImmediateMesh::surface_get_format(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), 0); + return surfaces[p_idx].format; +} +Mesh::PrimitiveType ImmediateMesh::surface_get_primitive_type(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), PRIMITIVE_MAX); + return surfaces[p_idx].primitive; +} +void ImmediateMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_idx, int(surfaces.size())); + surfaces[p_idx].material = p_material; + RID mat; + if (p_material.is_valid()) { + mat = p_material->get_rid(); + } + RS::get_singleton()->mesh_surface_set_material(mesh, p_idx, mat); +} +Ref<Material> ImmediateMesh::surface_get_material(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, int(surfaces.size()), Ref<Material>()); + return surfaces[p_idx].material; +} +int ImmediateMesh::get_blend_shape_count() const { + return 0; +} +StringName ImmediateMesh::get_blend_shape_name(int p_index) const { + return StringName(); +} +void ImmediateMesh::set_blend_shape_name(int p_index, const StringName &p_name) { +} + +AABB ImmediateMesh::get_aabb() const { + AABB aabb; + for (uint32_t i = 0; i < surfaces.size(); i++) { + if (i == 0) { + aabb = surfaces[i].aabb; + } else { + aabb.merge(surfaces[i].aabb); + } + } + return aabb; +} + +void ImmediateMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("surface_begin", "primitive", "material"), &ImmediateMesh::surface_begin, DEFVAL(Ref<Material>())); + ClassDB::bind_method(D_METHOD("surface_set_color", "color"), &ImmediateMesh::surface_set_color); + ClassDB::bind_method(D_METHOD("surface_set_normal", "normal"), &ImmediateMesh::surface_set_normal); + ClassDB::bind_method(D_METHOD("surface_set_tangent", "tangent"), &ImmediateMesh::surface_set_tangent); + ClassDB::bind_method(D_METHOD("surface_set_uv", "uv"), &ImmediateMesh::surface_set_uv); + ClassDB::bind_method(D_METHOD("surface_set_uv2", "uv2"), &ImmediateMesh::surface_set_uv2); + ClassDB::bind_method(D_METHOD("surface_add_vertex", "vertex"), &ImmediateMesh::surface_add_vertex); + ClassDB::bind_method(D_METHOD("surface_add_vertex_2d", "vertex"), &ImmediateMesh::surface_add_vertex_2d); + ClassDB::bind_method(D_METHOD("surface_end"), &ImmediateMesh::surface_end); + + ClassDB::bind_method(D_METHOD("clear_surfaces"), &ImmediateMesh::clear_surfaces); +} + +RID ImmediateMesh::get_rid() const { + return mesh; +} + +ImmediateMesh::ImmediateMesh() { + mesh = RS::get_singleton()->mesh_create(); +} +ImmediateMesh::~ImmediateMesh() { + RS::get_singleton()->free(mesh); +} diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h new file mode 100644 index 0000000000..7010d40719 --- /dev/null +++ b/scene/resources/immediate_mesh.h @@ -0,0 +1,117 @@ +/*************************************************************************/ +/* immediate_mesh.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 IMMEDIATE_MESH_H +#define IMMEDIATE_MESH_H + +#include "core/templates/local_vector.h" +#include "scene/resources/mesh.h" + +class ImmediateMesh : public Mesh { + GDCLASS(ImmediateMesh, Mesh) + + RID mesh; + + bool uses_colors = false; + bool uses_normals = false; + bool uses_tangents = false; + bool uses_uvs = false; + bool uses_uv2s = false; + + Color current_color; + Vector3 current_normal; + Plane current_tangent; + Vector2 current_uv; + Vector2 current_uv2; + + LocalVector<Color> colors; + LocalVector<Vector3> normals; + LocalVector<Plane> tangents; + LocalVector<Vector2> uvs; + LocalVector<Vector2> uv2s; + LocalVector<Vector3> vertices; + + struct Surface { + PrimitiveType primitive; + Ref<Material> material; + bool vertex_2d = false; + int array_len = 0; + uint32_t format = 0; + AABB aabb; + }; + + LocalVector<Surface> surfaces; + + bool surface_active = false; + Surface active_surface_data; + + Vector<uint8_t> surface_vertex_create_cache; + Vector<uint8_t> surface_attribute_create_cache; + +protected: + static void _bind_methods(); + +public: + void surface_begin(PrimitiveType p_primitive, const Ref<Material> &p_material = Ref<Material>()); + void surface_set_color(const Color &p_color); + void surface_set_normal(const Vector3 &p_normal); + void surface_set_tangent(const Plane &p_tangent); + void surface_set_uv(const Vector2 &p_uv); + void surface_set_uv2(const Vector2 &p_uv2); + void surface_add_vertex(const Vector3 &p_vertex); + void surface_add_vertex_2d(const Vector2 &p_vertex); + void surface_end(); + + void clear_surfaces(); + + virtual int get_surface_count() const override; + virtual int surface_get_array_len(int p_idx) const override; + virtual int surface_get_array_index_len(int p_idx) const override; + virtual bool surface_is_softbody_friendly(int p_idx) const override; + virtual Array surface_get_arrays(int p_surface) const override; + virtual Array surface_get_blend_shape_arrays(int p_surface) const override; + virtual Dictionary surface_get_lods(int p_surface) const override; + virtual uint32_t surface_get_format(int p_idx) const override; + virtual PrimitiveType surface_get_primitive_type(int p_idx) const override; + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override; + virtual Ref<Material> surface_get_material(int p_idx) const override; + virtual int get_blend_shape_count() const override; + virtual StringName get_blend_shape_name(int p_index) const override; + virtual void set_blend_shape_name(int p_index, const StringName &p_name) override; + + virtual AABB get_aabb() const override; + + virtual RID get_rid() const override; + + ImmediateMesh(); + ~ImmediateMesh(); +}; + +#endif // IMMEDIATEMESH_H diff --git a/scene/resources/line_shape_2d.h b/scene/resources/line_shape_2d.h index 9f0405ad29..210a1aa9e6 100644 --- a/scene/resources/line_shape_2d.h +++ b/scene/resources/line_shape_2d.h @@ -36,7 +36,8 @@ class LineShape2D : public Shape2D { GDCLASS(LineShape2D, Shape2D); - Vector2 normal = Vector2(0, 1); + // LineShape2D is often used for one-way platforms, where the normal pointing up makes sense. + Vector2 normal = Vector2(0, -1); real_t distance = 0.0; void _update_shape(); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 75dd417f7f..7d49533afd 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -77,7 +77,7 @@ RID Material::get_rid() const { void Material::_validate_property(PropertyInfo &property) const { if (!_can_do_next_pass() && property.name == "next_pass") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -130,7 +130,7 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { } } if (pr) { - RenderingServer::get_singleton()->material_set_param(_get_material(), pr, p_value); + set_shader_param(pr, p_value); return true; } } @@ -152,7 +152,12 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { } if (pr) { - r_ret = RenderingServer::get_singleton()->material_get_param(_get_material(), pr); + const Map<StringName, Variant>::Element *E = param_cache.find(pr); + if (E) { + r_ret = E->get(); + } else { + r_ret = Variant(); + } return true; } } @@ -219,11 +224,31 @@ Ref<Shader> ShaderMaterial::get_shader() const { } void ShaderMaterial::set_shader_param(const StringName &p_param, const Variant &p_value) { - RS::get_singleton()->material_set_param(_get_material(), p_param, p_value); + if (p_value.get_type() == Variant::NIL) { + param_cache.erase(p_param); + RS::get_singleton()->material_set_param(_get_material(), p_param, Variant()); + } else { + param_cache[p_param] = p_value; + if (p_value.get_type() == Variant::OBJECT) { + RID tex_rid = p_value; + if (tex_rid == RID()) { + param_cache.erase(p_param); + RS::get_singleton()->material_set_param(_get_material(), p_param, Variant()); + } else { + RS::get_singleton()->material_set_param(_get_material(), p_param, tex_rid); + } + } else { + RS::get_singleton()->material_set_param(_get_material(), p_param, p_value); + } + } } Variant ShaderMaterial::get_shader_param(const StringName &p_param) const { - return RS::get_singleton()->material_get_param(_get_material(), p_param); + if (param_cache.has(p_param)) { + return param_cache[p_param]; + } else { + return Variant(); + } } void ShaderMaterial::_shader_changed() { @@ -345,7 +370,6 @@ void BaseMaterial3D::init_shaders() { shader_names->refraction_texture_channel = "refraction_texture_channel"; shader_names->transmittance_color = "transmittance_color"; - shader_names->transmittance_curve = "transmittance_curve"; shader_names->transmittance_depth = "transmittance_depth"; shader_names->transmittance_boost = "transmittance_boost"; @@ -505,9 +529,6 @@ void BaseMaterial3D::_update_shader() { case DIFFUSE_LAMBERT_WRAP: code += ",diffuse_lambert_wrap"; break; - case DIFFUSE_OREN_NAYAR: - code += ",diffuse_oren_nayar"; - break; case DIFFUSE_TOON: code += ",diffuse_toon"; break; @@ -695,7 +716,6 @@ void BaseMaterial3D::_update_shader() { code += "uniform vec4 transmittance_color : hint_color;\n"; code += "uniform float transmittance_depth;\n"; code += "uniform sampler2D texture_subsurface_transmittance : hint_white," + texfilter_str + ";\n"; - code += "uniform float transmittance_curve;\n"; code += "uniform float transmittance_boost;\n"; } @@ -737,463 +757,476 @@ void BaseMaterial3D::_update_shader() { code += "void vertex() {\n"; if (flags[FLAG_SRGB_VERTEX_COLOR]) { - code += "\tif (!OUTPUT_IS_SRGB) {\n"; - code += "\t\tCOLOR.rgb = mix(pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb * (1.0 / 12.92), lessThan(COLOR.rgb, vec3(0.04045)));\n"; - code += "\t}\n"; + code += " if (!OUTPUT_IS_SRGB) {\n"; + code += " COLOR.rgb = mix(pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb * (1.0 / 12.92), lessThan(COLOR.rgb, vec3(0.04045)));\n"; + code += " }\n"; } if (flags[FLAG_USE_POINT_SIZE]) { - code += "\tPOINT_SIZE=point_size;\n"; + code += " POINT_SIZE=point_size;\n"; } if (shading_mode == SHADING_MODE_PER_VERTEX) { - code += "\tROUGHNESS=roughness;\n"; + code += " ROUGHNESS=roughness;\n"; } if (!flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tUV=UV*uv1_scale.xy+uv1_offset.xy;\n"; + code += " UV=UV*uv1_scale.xy+uv1_offset.xy;\n"; } switch (billboard_mode) { case BILLBOARD_DISABLED: { } break; case BILLBOARD_ENABLED: { - code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; } } break; case BILLBOARD_FIXED_Y: { - code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } else { - code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n"; } } break; case BILLBOARD_PARTICLES: { //make billboard - code += "\tmat4 mat_world = mat4(normalize(CAMERA_MATRIX[0])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[1])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[2])*length(WORLD_MATRIX[2]),WORLD_MATRIX[3]);\n"; + code += " mat4 mat_world = mat4(normalize(CAMERA_MATRIX[0])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[1])*length(WORLD_MATRIX[0]),normalize(CAMERA_MATRIX[2])*length(WORLD_MATRIX[2]),WORLD_MATRIX[3]);\n"; //rotate by rotation - code += "\tmat_world = mat_world * mat4( vec4(cos(INSTANCE_CUSTOM.x),-sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0),vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " mat_world = mat_world * mat4( vec4(cos(INSTANCE_CUSTOM.x),-sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0),vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n"; //set modelview - code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n"; + code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n"; //handle animation - code += "\tfloat h_frames = float(particles_anim_h_frames);\n"; - code += "\tfloat v_frames = float(particles_anim_v_frames);\n"; - code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n"; - code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; - code += "\tif (!particles_anim_loop) {\n"; - code += "\t\tparticle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n"; - code += "\t} else {\n"; - code += "\t\tparticle_frame = mod(particle_frame, particle_total_frames);\n"; - code += "\t}"; - code += "\tUV /= vec2(h_frames, v_frames);\n"; - code += "\tUV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n"; + code += " float h_frames = float(particles_anim_h_frames);\n"; + code += " float v_frames = float(particles_anim_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"; } break; case BILLBOARD_MAX: break; // Internal value, skip. } if (flags[FLAG_FIXED_SIZE]) { - code += "\tif (PROJECTION_MATRIX[3][3] != 0.0) {\n"; + code += " if (PROJECTION_MATRIX[3][3] != 0.0) {\n"; //orthogonal matrix, try to do about the same //with viewport size - code += "\t\tfloat h = abs(1.0 / (2.0 * PROJECTION_MATRIX[1][1]));\n"; - code += "\t\tfloat sc = (h * 2.0); //consistent with Y-fov\n"; - code += "\t\tMODELVIEW_MATRIX[0]*=sc;\n"; - code += "\t\tMODELVIEW_MATRIX[1]*=sc;\n"; - code += "\t\tMODELVIEW_MATRIX[2]*=sc;\n"; - code += "\t} else {\n"; + code += " float h = abs(1.0 / (2.0 * PROJECTION_MATRIX[1][1]));\n"; + code += " float sc = (h * 2.0); //consistent with Y-fov\n"; + code += " MODELVIEW_MATRIX[0]*=sc;\n"; + code += " MODELVIEW_MATRIX[1]*=sc;\n"; + code += " MODELVIEW_MATRIX[2]*=sc;\n"; + code += " } else {\n"; //just scale by depth - code += "\t\tfloat sc = -(MODELVIEW_MATRIX)[3].z;\n"; - code += "\t\tMODELVIEW_MATRIX[0]*=sc;\n"; - code += "\t\tMODELVIEW_MATRIX[1]*=sc;\n"; - code += "\t\tMODELVIEW_MATRIX[2]*=sc;\n"; - code += "\t}\n"; + code += " float sc = -(MODELVIEW_MATRIX)[3].z;\n"; + code += " MODELVIEW_MATRIX[0]*=sc;\n"; + code += " MODELVIEW_MATRIX[1]*=sc;\n"; + code += " MODELVIEW_MATRIX[2]*=sc;\n"; + code += " }\n"; } if (detail_uv == DETAIL_UV_2 && !flags[FLAG_UV2_USE_TRIPLANAR]) { - code += "\tUV2=UV2*uv2_scale.xy+uv2_offset.xy;\n"; + code += " UV2=UV2*uv2_scale.xy+uv2_offset.xy;\n"; } if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) { //generate tangent and binormal in world space - code += "\tTANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);\n"; - code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.y);\n"; - code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n"; - code += "\tTANGENT = normalize(TANGENT);\n"; + code += " TANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);\n"; + code += " TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.y);\n"; + code += " TANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n"; + code += " TANGENT = normalize(TANGENT);\n"; - code += "\tBINORMAL = vec3(0.0,-1.0,0.0) * abs(NORMAL.x);\n"; - code += "\tBINORMAL+= vec3(0.0,0.0,1.0) * abs(NORMAL.y);\n"; - code += "\tBINORMAL+= vec3(0.0,-1.0,0.0) * abs(NORMAL.z);\n"; - code += "\tBINORMAL = normalize(BINORMAL);\n"; + code += " BINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);\n"; + code += " BINORMAL+= vec3(0.0,0.0,-1.0) * abs(NORMAL.y);\n"; + code += " BINORMAL+= vec3(0.0,1.0,0.0) * abs(NORMAL.z);\n"; + code += " BINORMAL = normalize(BINORMAL);\n"; } if (flags[FLAG_UV1_USE_TRIPLANAR]) { if (flags[FLAG_UV1_USE_WORLD_TRIPLANAR]) { - code += "\tuv1_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n"; - code += "\tuv1_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n"; + code += " uv1_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n"; + code += " uv1_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n"; } else { - code += "\tuv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n"; - code += "\tuv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n"; + code += " uv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n"; + code += " uv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n"; } - code += "\tuv1_power_normal/=dot(uv1_power_normal,vec3(1.0));\n"; - code += "\tuv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; + code += " uv1_power_normal/=dot(uv1_power_normal,vec3(1.0));\n"; + code += " uv1_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; } if (flags[FLAG_UV2_USE_TRIPLANAR]) { if (flags[FLAG_UV2_USE_WORLD_TRIPLANAR]) { - code += "\tuv2_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n"; - code += "\tuv2_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n"; + code += " uv2_power_normal=pow(abs(mat3(WORLD_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n"; + code += " uv2_triplanar_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n"; } else { - code += "\tuv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n"; - code += "\tuv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n"; + code += " uv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n"; + code += " uv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n"; } - code += "\tuv2_power_normal/=dot(uv2_power_normal,vec3(1.0));\n"; - code += "\tuv2_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; + code += " uv2_power_normal/=dot(uv2_power_normal,vec3(1.0));\n"; + code += " uv2_triplanar_pos *= vec3(1.0,-1.0, 1.0);\n"; } if (grow_enabled) { - code += "\tVERTEX+=NORMAL*grow;\n"; + code += " VERTEX+=NORMAL*grow;\n"; } code += "}\n"; code += "\n\n"; if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) { code += "vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos) {\n"; - code += "\tvec4 samp=vec4(0.0);\n"; - code += "\tsamp+= texture(p_sampler,p_triplanar_pos.xy) * p_weights.z;\n"; - code += "\tsamp+= texture(p_sampler,p_triplanar_pos.xz) * p_weights.y;\n"; - code += "\tsamp+= texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;\n"; - code += "\treturn samp;\n"; + code += " vec4 samp=vec4(0.0);\n"; + code += " samp+= texture(p_sampler,p_triplanar_pos.xy) * p_weights.z;\n"; + code += " samp+= texture(p_sampler,p_triplanar_pos.xz) * p_weights.y;\n"; + code += " samp+= texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)) * p_weights.x;\n"; + code += " return samp;\n"; code += "}\n"; } code += "\n\n"; code += "void fragment() {\n"; if (!flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec2 base_uv = UV;\n"; + code += " vec2 base_uv = UV;\n"; } if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2]) || (features[FEATURE_EMISSION] && flags[FLAG_EMISSION_ON_UV2])) { - code += "\tvec2 base_uv2 = UV2;\n"; + code += " vec2 base_uv2 = UV2;\n"; + } + + if (features[FEATURE_HEIGHT_MAPPING] && flags[FLAG_UV1_USE_TRIPLANAR]) { + // Display both resource name and albedo texture name. + // Materials are often built-in to scenes, so displaying the resource name alone may not be meaningful. + // On the other hand, albedo textures are almost always external to the scene. + if (textures[TEXTURE_ALBEDO].is_valid()) { + WARN_PRINT(vformat("%s (albedo %s): Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path(), textures[TEXTURE_ALBEDO]->get_path())); + } else if (!get_path().is_empty()) { + WARN_PRINT(vformat("%s: Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path())); + } else { + // Resource wasn't saved yet. + WARN_PRINT("Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping."); + } } if (!RenderingServer::get_singleton()->is_low_end() && features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //heightmap not supported with triplanar - code += "\t{\n"; - code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-) + code += " {\n"; + code += " vec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-) if (deep_parallax) { - code += "\t\tfloat num_layers = mix(float(heightmap_max_layers),float(heightmap_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n"; - code += "\t\tfloat layer_depth = 1.0 / num_layers;\n"; - code += "\t\tfloat current_layer_depth = 0.0;\n"; - code += "\t\tvec2 P = view_dir.xy * heightmap_scale;\n"; - code += "\t\tvec2 delta = P / num_layers;\n"; - code += "\t\tvec2 ofs = base_uv;\n"; + code += " float num_layers = mix(float(heightmap_max_layers),float(heightmap_min_layers), abs(dot(vec3(0.0, 0.0, 1.0), view_dir)));\n"; + code += " float layer_depth = 1.0 / num_layers;\n"; + code += " float current_layer_depth = 0.0;\n"; + code += " vec2 P = view_dir.xy * heightmap_scale;\n"; + code += " vec2 delta = P / num_layers;\n"; + code += " vec2 ofs = base_uv;\n"; if (flags[FLAG_INVERT_HEIGHTMAP]) { - code += "\t\tfloat depth = texture(texture_heightmap, ofs).r;\n"; + code += " float depth = texture(texture_heightmap, ofs).r;\n"; } else { - code += "\t\tfloat depth = 1.0 - texture(texture_heightmap, ofs).r;\n"; + code += " float depth = 1.0 - texture(texture_heightmap, ofs).r;\n"; } - code += "\t\tfloat current_depth = 0.0;\n"; - code += "\t\twhile(current_depth < depth) {\n"; - code += "\t\t\tofs -= delta;\n"; + code += " float current_depth = 0.0;\n"; + code += " while(current_depth < depth) {\n"; + code += " ofs -= delta;\n"; if (flags[FLAG_INVERT_HEIGHTMAP]) { - code += "\t\t\tdepth = texture(texture_heightmap, ofs).r;\n"; + code += " depth = texture(texture_heightmap, ofs).r;\n"; } else { - code += "\t\t\tdepth = 1.0 - texture(texture_heightmap, ofs).r;\n"; + code += " depth = 1.0 - texture(texture_heightmap, ofs).r;\n"; } - code += "\t\t\tcurrent_depth += layer_depth;\n"; - code += "\t\t}\n"; - code += "\t\tvec2 prev_ofs = ofs + delta;\n"; - code += "\t\tfloat after_depth = depth - current_depth;\n"; + code += " current_depth += layer_depth;\n"; + code += " }\n"; + code += " vec2 prev_ofs = ofs + delta;\n"; + code += " float after_depth = depth - current_depth;\n"; if (flags[FLAG_INVERT_HEIGHTMAP]) { - code += "\t\tfloat before_depth = texture(texture_heightmap, prev_ofs).r - current_depth + layer_depth;\n"; + code += " float before_depth = texture(texture_heightmap, prev_ofs).r - current_depth + layer_depth;\n"; } else { - code += "\t\tfloat before_depth = ( 1.0 - texture(texture_heightmap, prev_ofs).r ) - current_depth + layer_depth;\n"; + code += " float before_depth = ( 1.0 - texture(texture_heightmap, prev_ofs).r ) - current_depth + layer_depth;\n"; } - code += "\t\tfloat weight = after_depth / (after_depth - before_depth);\n"; - code += "\t\tofs = mix(ofs,prev_ofs,weight);\n"; + code += " float weight = after_depth / (after_depth - before_depth);\n"; + code += " ofs = mix(ofs,prev_ofs,weight);\n"; } else { if (flags[FLAG_INVERT_HEIGHTMAP]) { - code += "\t\tfloat depth = texture(texture_heightmap, base_uv).r;\n"; + code += " float depth = texture(texture_heightmap, base_uv).r;\n"; } else { - code += "\t\tfloat depth = 1.0 - texture(texture_heightmap, base_uv).r;\n"; + code += " float depth = 1.0 - texture(texture_heightmap, base_uv).r;\n"; } - code += "\t\tvec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n"; + code += " vec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n"; } - code += "\t\tbase_uv=ofs;\n"; + code += " base_uv=ofs;\n"; if (features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) { - code += "\t\tbase_uv2-=ofs;\n"; + code += " base_uv2-=ofs;\n"; } - code += "\t}\n"; + code += " }\n"; } if (flags[FLAG_USE_POINT_SIZE]) { - code += "\tvec4 albedo_tex = texture(texture_albedo,POINT_COORD);\n"; + code += " vec4 albedo_tex = texture(texture_albedo,POINT_COORD);\n"; } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec4 albedo_tex = triplanar_texture(texture_albedo,uv1_power_normal,uv1_triplanar_pos);\n"; + code += " vec4 albedo_tex = triplanar_texture(texture_albedo,uv1_power_normal,uv1_triplanar_pos);\n"; } else { - code += "\tvec4 albedo_tex = texture(texture_albedo,base_uv);\n"; + code += " vec4 albedo_tex = texture(texture_albedo,base_uv);\n"; } } if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) { - code += "\talbedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n"; + code += " albedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n"; } if (flags[FLAG_ALBEDO_FROM_VERTEX_COLOR]) { - code += "\talbedo_tex *= COLOR;\n"; + code += " albedo_tex *= COLOR;\n"; } - code += "\tALBEDO = albedo.rgb * albedo_tex.rgb;\n"; + code += " ALBEDO = albedo.rgb * albedo_tex.rgb;\n"; if (!orm) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n"; + code += " float metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_triplanar_pos),metallic_texture_channel);\n"; } else { - code += "\tfloat metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n"; + code += " float metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n"; } - code += "\tMETALLIC = metallic_tex * metallic;\n"; + code += " METALLIC = metallic_tex * metallic;\n"; switch (roughness_texture_channel) { case TEXTURE_CHANNEL_RED: { - code += "\tvec4 roughness_texture_channel = vec4(1.0,0.0,0.0,0.0);\n"; + code += " vec4 roughness_texture_channel = vec4(1.0,0.0,0.0,0.0);\n"; } break; case TEXTURE_CHANNEL_GREEN: { - code += "\tvec4 roughness_texture_channel = vec4(0.0,1.0,0.0,0.0);\n"; + code += " vec4 roughness_texture_channel = vec4(0.0,1.0,0.0,0.0);\n"; } break; case TEXTURE_CHANNEL_BLUE: { - code += "\tvec4 roughness_texture_channel = vec4(0.0,0.0,1.0,0.0);\n"; + code += " vec4 roughness_texture_channel = vec4(0.0,0.0,1.0,0.0);\n"; } break; case TEXTURE_CHANNEL_ALPHA: { - code += "\tvec4 roughness_texture_channel = vec4(0.0,0.0,0.0,1.0);\n"; + code += " vec4 roughness_texture_channel = vec4(0.0,0.0,0.0,1.0);\n"; } break; case TEXTURE_CHANNEL_GRAYSCALE: { - code += "\tvec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);\n"; + code += " vec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);\n"; } break; case TEXTURE_CHANNEL_MAX: break; // Internal value, skip. } if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n"; + code += " float roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_triplanar_pos),roughness_texture_channel);\n"; } else { - code += "\tfloat roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n"; + code += " float roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n"; } - code += "\tROUGHNESS = roughness_tex * roughness;\n"; - code += "\tSPECULAR = specular;\n"; + code += " ROUGHNESS = roughness_tex * roughness;\n"; + code += " SPECULAR = specular;\n"; } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n"; + code += " vec4 orm_tex = triplanar_texture(texture_orm,uv1_power_normal,uv1_triplanar_pos);\n"; } else { - code += "\tfloat orm_tex = texture(texture_orm,base_uv);\n"; + code += " vec4 orm_tex = texture(texture_orm,base_uv);\n"; } - code += "\tROUGHNESS = orm_tex.g;\n"; - code += "\tMETALLIC = orm_tex.b;\n"; + code += " ROUGHNESS = orm_tex.g;\n"; + code += " METALLIC = orm_tex.b;\n"; } if (features[FEATURE_NORMAL_MAPPING]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tNORMAL_MAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; + code += " NORMAL_MAP = triplanar_texture(texture_normal,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; } else { - code += "\tNORMAL_MAP = texture(texture_normal,base_uv).rgb;\n"; + code += " NORMAL_MAP = texture(texture_normal,base_uv).rgb;\n"; } - code += "\tNORMAL_MAP_DEPTH = normal_scale;\n"; + code += " NORMAL_MAP_DEPTH = normal_scale;\n"; } if (features[FEATURE_EMISSION]) { if (flags[FLAG_EMISSION_ON_UV2]) { if (flags[FLAG_UV2_USE_TRIPLANAR]) { - code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n"; + code += " vec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n"; } else { - code += "\tvec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n"; + code += " vec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n"; } } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; + code += " vec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; } else { - code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n"; + code += " vec3 emission_tex = texture(texture_emission,base_uv).rgb;\n"; } } if (emission_op == EMISSION_OP_ADD) { - code += "\tEMISSION = (emission.rgb+emission_tex)*emission_energy;\n"; + code += " EMISSION = (emission.rgb+emission_tex)*emission_energy;\n"; } else { - code += "\tEMISSION = (emission.rgb*emission_tex)*emission_energy;\n"; + code += " EMISSION = (emission.rgb*emission_tex)*emission_energy;\n"; } } if (features[FEATURE_REFRACTION]) { if (features[FEATURE_NORMAL_MAPPING]) { - code += "\tvec3 unpacked_normal = NORMAL_MAP;\n"; - code += "\tunpacked_normal.xy = unpacked_normal.xy * 2.0 - 1.0;\n"; - code += "\tunpacked_normal.z = sqrt(max(0.0, 1.0 - dot(unpacked_normal.xy, unpacked_normal.xy)));\n"; - code += "\tvec3 ref_normal = normalize( mix(NORMAL,TANGENT * unpacked_normal.x + BINORMAL * unpacked_normal.y + NORMAL * unpacked_normal.z,NORMAL_MAP_DEPTH) );\n"; + code += " vec3 unpacked_normal = NORMAL_MAP;\n"; + code += " unpacked_normal.xy = unpacked_normal.xy * 2.0 - 1.0;\n"; + code += " unpacked_normal.z = sqrt(max(0.0, 1.0 - dot(unpacked_normal.xy, unpacked_normal.xy)));\n"; + code += " vec3 ref_normal = normalize( mix(NORMAL,TANGENT * unpacked_normal.x + BINORMAL * unpacked_normal.y + NORMAL * unpacked_normal.z,NORMAL_MAP_DEPTH) );\n"; } else { - code += "\tvec3 ref_normal = NORMAL;\n"; + code += " vec3 ref_normal = NORMAL;\n"; } if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(triplanar_texture(texture_refraction,uv1_power_normal,uv1_triplanar_pos),refraction_texture_channel) * refraction;\n"; + code += " vec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(triplanar_texture(texture_refraction,uv1_power_normal,uv1_triplanar_pos),refraction_texture_channel) * refraction;\n"; } else { - code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n"; + code += " vec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n"; } - code += "\tfloat ref_amount = 1.0 - albedo.a * albedo_tex.a;\n"; - code += "\tEMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n"; - code += "\tALBEDO *= 1.0 - ref_amount;\n"; - code += "\tALPHA = 1.0;\n"; + code += " float ref_amount = 1.0 - albedo.a * albedo_tex.a;\n"; + code += " EMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n"; + code += " ALBEDO *= 1.0 - ref_amount;\n"; + code += " ALPHA = 1.0;\n"; } else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) { - code += "\tALPHA = albedo.a * albedo_tex.a;\n"; + code += " ALPHA = albedo.a * albedo_tex.a;\n"; } if (transparency == TRANSPARENCY_ALPHA_HASH) { - code += "\tALPHA_HASH_SCALE = alpha_hash_scale;\n"; + code += " ALPHA_HASH_SCALE = alpha_hash_scale;\n"; } else if (transparency == TRANSPARENCY_ALPHA_SCISSOR) { - code += "\tALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;\n"; + code += " ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;\n"; } if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF && (transparency == TRANSPARENCY_ALPHA_HASH || transparency == TRANSPARENCY_ALPHA_SCISSOR)) { - code += "\tALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge;\n"; - code += "\tALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size);\n"; + code += " ALPHA_ANTIALIASING_EDGE = alpha_antialiasing_edge;\n"; + code += " ALPHA_TEXTURE_COORDINATE = UV * vec2(albedo_texture_size);\n"; } if (proximity_fade_enabled) { - code += "\tfloat depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n"; - code += "\tvec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n"; - code += "\tworld_pos.xyz/=world_pos.w;\n"; - code += "\tALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n"; + code += " float depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n"; + code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n"; + code += " world_pos.xyz/=world_pos.w;\n"; + code += " ALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n"; } if (distance_fade != DISTANCE_FADE_DISABLED) { if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) { if (!RenderingServer::get_singleton()->is_low_end()) { - code += "\t{\n"; + code += " {\n"; if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { - code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; + code += " float fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; } else { - code += "\t\tfloat fade_distance=-VERTEX.z;\n"; + code += " float fade_distance=-VERTEX.z;\n"; } - code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; - code += "\t\tint x = int(FRAGCOORD.x) % 4;\n"; - code += "\t\tint y = int(FRAGCOORD.y) % 4;\n"; - code += "\t\tint index = x + y * 4;\n"; - code += "\t\tfloat limit = 0.0;\n\n"; - code += "\t\tif (x < 8) {\n"; - code += "\t\t\tif (index == 0) limit = 0.0625;\n"; - code += "\t\t\tif (index == 1) limit = 0.5625;\n"; - code += "\t\t\tif (index == 2) limit = 0.1875;\n"; - code += "\t\t\tif (index == 3) limit = 0.6875;\n"; - code += "\t\t\tif (index == 4) limit = 0.8125;\n"; - code += "\t\t\tif (index == 5) limit = 0.3125;\n"; - code += "\t\t\tif (index == 6) limit = 0.9375;\n"; - code += "\t\t\tif (index == 7) limit = 0.4375;\n"; - code += "\t\t\tif (index == 8) limit = 0.25;\n"; - code += "\t\t\tif (index == 9) limit = 0.75;\n"; - code += "\t\t\tif (index == 10) limit = 0.125;\n"; - code += "\t\t\tif (index == 11) limit = 0.625;\n"; - code += "\t\t\tif (index == 12) limit = 1.0;\n"; - code += "\t\t\tif (index == 13) limit = 0.5;\n"; - code += "\t\t\tif (index == 14) limit = 0.875;\n"; - code += "\t\t\tif (index == 15) limit = 0.375;\n"; - code += "\t\t}\n\n"; - code += "\tif (fade < limit)\n"; - code += "\t\tdiscard;\n"; - code += "\t}\n\n"; + code += " float fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; + code += " int x = int(FRAGCOORD.x) % 4;\n"; + code += " int y = int(FRAGCOORD.y) % 4;\n"; + code += " int index = x + y * 4;\n"; + code += " float limit = 0.0;\n\n"; + code += " if (x < 8) {\n"; + code += " if (index == 0) limit = 0.0625;\n"; + code += " if (index == 1) limit = 0.5625;\n"; + code += " if (index == 2) limit = 0.1875;\n"; + code += " if (index == 3) limit = 0.6875;\n"; + code += " if (index == 4) limit = 0.8125;\n"; + code += " if (index == 5) limit = 0.3125;\n"; + code += " if (index == 6) limit = 0.9375;\n"; + code += " if (index == 7) limit = 0.4375;\n"; + code += " if (index == 8) limit = 0.25;\n"; + code += " if (index == 9) limit = 0.75;\n"; + code += " if (index == 10) limit = 0.125;\n"; + code += " if (index == 11) limit = 0.625;\n"; + code += " if (index == 12) limit = 1.0;\n"; + code += " if (index == 13) limit = 0.5;\n"; + code += " if (index == 14) limit = 0.875;\n"; + code += " if (index == 15) limit = 0.375;\n"; + code += " }\n\n"; + code += " if (fade < limit)\n"; + code += " discard;\n"; + code += " }\n\n"; } } else { - code += "\tALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n"; + code += " ALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n"; } } if (features[FEATURE_RIM]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec2 rim_tex = triplanar_texture(texture_rim,uv1_power_normal,uv1_triplanar_pos).xy;\n"; + code += " vec2 rim_tex = triplanar_texture(texture_rim,uv1_power_normal,uv1_triplanar_pos).xy;\n"; } else { - code += "\tvec2 rim_tex = texture(texture_rim,base_uv).xy;\n"; + code += " vec2 rim_tex = texture(texture_rim,base_uv).xy;\n"; } - code += "\tRIM = rim*rim_tex.x;"; - code += "\tRIM_TINT = rim_tint*rim_tex.y;\n"; + code += " RIM = rim*rim_tex.x;"; + code += " RIM_TINT = rim_tint*rim_tex.y;\n"; } if (features[FEATURE_CLEARCOAT]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec2 clearcoat_tex = triplanar_texture(texture_clearcoat,uv1_power_normal,uv1_triplanar_pos).xy;\n"; + code += " vec2 clearcoat_tex = triplanar_texture(texture_clearcoat,uv1_power_normal,uv1_triplanar_pos).xy;\n"; } else { - code += "\tvec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n"; + code += " vec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n"; } - code += "\tCLEARCOAT = clearcoat*clearcoat_tex.x;"; - code += "\tCLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n"; + code += " CLEARCOAT = clearcoat*clearcoat_tex.x;"; + code += " CLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n"; } if (features[FEATURE_ANISOTROPY]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec3 anisotropy_tex = triplanar_texture(texture_flowmap,uv1_power_normal,uv1_triplanar_pos).rga;\n"; + code += " vec3 anisotropy_tex = triplanar_texture(texture_flowmap,uv1_power_normal,uv1_triplanar_pos).rga;\n"; } else { - code += "\tvec3 anisotropy_tex = texture(texture_flowmap,base_uv).rga;\n"; + code += " vec3 anisotropy_tex = texture(texture_flowmap,base_uv).rga;\n"; } - code += "\tANISOTROPY = anisotropy_ratio*anisotropy_tex.b;\n"; - code += "\tANISOTROPY_FLOW = anisotropy_tex.rg*2.0-1.0;\n"; + code += " ANISOTROPY = anisotropy_ratio*anisotropy_tex.b;\n"; + code += " ANISOTROPY_FLOW = anisotropy_tex.rg*2.0-1.0;\n"; } if (features[FEATURE_AMBIENT_OCCLUSION]) { if (!orm) { if (flags[FLAG_AO_ON_UV2]) { if (flags[FLAG_UV2_USE_TRIPLANAR]) { - code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_triplanar_pos),ao_texture_channel);\n"; + code += " AO = dot(triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_triplanar_pos),ao_texture_channel);\n"; } else { - code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv2),ao_texture_channel);\n"; + code += " AO = dot(texture(texture_ambient_occlusion,base_uv2),ao_texture_channel);\n"; } } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_triplanar_pos),ao_texture_channel);\n"; + code += " AO = dot(triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_triplanar_pos),ao_texture_channel);\n"; } else { - code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv),ao_texture_channel);\n"; + code += " AO = dot(texture(texture_ambient_occlusion,base_uv),ao_texture_channel);\n"; } } } else { - code += "\tAO = orm_tex.r;\n"; + code += " AO = orm_tex.r;\n"; } - code += "\tAO_LIGHT_AFFECT = ao_light_affect;\n"; + code += " AO_LIGHT_AFFECT = ao_light_affect;\n"; } if (features[FEATURE_SUBSURFACE_SCATTERING]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat sss_tex = triplanar_texture(texture_subsurface_scattering,uv1_power_normal,uv1_triplanar_pos).r;\n"; + code += " float sss_tex = triplanar_texture(texture_subsurface_scattering,uv1_power_normal,uv1_triplanar_pos).r;\n"; } else { - code += "\tfloat sss_tex = texture(texture_subsurface_scattering,base_uv).r;\n"; + code += " float sss_tex = texture(texture_subsurface_scattering,base_uv).r;\n"; } - code += "\tSSS_STRENGTH=subsurface_scattering_strength*sss_tex;\n"; + code += " SSS_STRENGTH=subsurface_scattering_strength*sss_tex;\n"; } if (features[FEATURE_SUBSURFACE_TRANSMITTANCE]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec4 trans_color_tex = triplanar_texture(texture_subsurface_transmittance,uv1_power_normal,uv1_triplanar_pos);\n"; + code += " vec4 trans_color_tex = triplanar_texture(texture_subsurface_transmittance,uv1_power_normal,uv1_triplanar_pos);\n"; } else { - code += "\tvec4 trans_color_tex = texture(texture_subsurface_transmittance,base_uv);\n"; + code += " vec4 trans_color_tex = texture(texture_subsurface_transmittance,base_uv);\n"; } - code += "\tSSS_TRANSMITTANCE_COLOR=transmittance_color*trans_color_tex;\n"; + code += " SSS_TRANSMITTANCE_COLOR=transmittance_color*trans_color_tex;\n"; - code += "\tSSS_TRANSMITTANCE_DEPTH=transmittance_depth;\n"; - code += "\tSSS_TRANSMITTANCE_CURVE=transmittance_curve;\n"; - code += "\tSSS_TRANSMITTANCE_BOOST=transmittance_boost;\n"; + code += " SSS_TRANSMITTANCE_DEPTH=transmittance_depth;\n"; + code += " SSS_TRANSMITTANCE_BOOST=transmittance_boost;\n"; } if (features[FEATURE_BACKLIGHT]) { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec3 backlight_tex = triplanar_texture(texture_backlight,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; + code += " vec3 backlight_tex = triplanar_texture(texture_backlight,uv1_power_normal,uv1_triplanar_pos).rgb;\n"; } else { - code += "\tvec3 backlight_tex = texture(texture_backlight,base_uv).rgb;\n"; + code += " vec3 backlight_tex = texture(texture_backlight,base_uv).rgb;\n"; } - code += "\tBACKLIGHT = (backlight.rgb+backlight_tex);\n"; + code += " BACKLIGHT = (backlight.rgb+backlight_tex);\n"; } if (features[FEATURE_DETAIL]) { @@ -1201,41 +1234,41 @@ void BaseMaterial3D::_update_shader() { if (triplanar) { String tp_uv = detail_uv == DETAIL_UV_1 ? "uv1" : "uv2"; - code += "\tvec4 detail_tex = triplanar_texture(texture_detail_albedo," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n"; - code += "\tvec4 detail_norm_tex = triplanar_texture(texture_detail_normal," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n"; + code += " vec4 detail_tex = triplanar_texture(texture_detail_albedo," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n"; + code += " vec4 detail_norm_tex = triplanar_texture(texture_detail_normal," + tp_uv + "_power_normal," + tp_uv + "_triplanar_pos);\n"; } else { String det_uv = detail_uv == DETAIL_UV_1 ? "base_uv" : "base_uv2"; - code += "\tvec4 detail_tex = texture(texture_detail_albedo," + det_uv + ");\n"; - code += "\tvec4 detail_norm_tex = texture(texture_detail_normal," + det_uv + ");\n"; + code += " vec4 detail_tex = texture(texture_detail_albedo," + det_uv + ");\n"; + code += " vec4 detail_norm_tex = texture(texture_detail_normal," + det_uv + ");\n"; } if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal,uv1_triplanar_pos);\n"; + code += " vec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal,uv1_triplanar_pos);\n"; } else { - code += "\tvec4 detail_mask_tex = texture(texture_detail_mask,base_uv);\n"; + code += " vec4 detail_mask_tex = texture(texture_detail_mask,base_uv);\n"; } switch (detail_blend_mode) { case BLEND_MODE_MIX: { - code += "\tvec3 detail = mix(ALBEDO.rgb,detail_tex.rgb,detail_tex.a);\n"; + code += " vec3 detail = mix(ALBEDO.rgb,detail_tex.rgb,detail_tex.a);\n"; } break; case BLEND_MODE_ADD: { - code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb+detail_tex.rgb,detail_tex.a);\n"; + code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb+detail_tex.rgb,detail_tex.a);\n"; } break; case BLEND_MODE_SUB: { - code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb-detail_tex.rgb,detail_tex.a);\n"; + code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb-detail_tex.rgb,detail_tex.a);\n"; } break; case BLEND_MODE_MUL: { - code += "\tvec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb*detail_tex.rgb,detail_tex.a);\n"; + code += " vec3 detail = mix(ALBEDO.rgb,ALBEDO.rgb*detail_tex.rgb,detail_tex.a);\n"; } break; case BLEND_MODE_MAX: break; // Internal value, skip. } - code += "\tvec3 detail_norm = mix(NORMAL_MAP,detail_norm_tex.rgb,detail_tex.a);\n"; - code += "\tNORMAL_MAP = mix(NORMAL_MAP,detail_norm,detail_mask_tex.r);\n"; - code += "\tALBEDO.rgb = mix(ALBEDO.rgb,detail,detail_mask_tex.r);\n"; + code += " vec3 detail_norm = mix(NORMAL_MAP,detail_norm_tex.rgb,detail_tex.a);\n"; + code += " NORMAL_MAP = mix(NORMAL_MAP,detail_norm,detail_mask_tex.r);\n"; + code += " ALBEDO.rgb = mix(ALBEDO.rgb,detail,detail_mask_tex.r);\n"; } code += "}\n"; @@ -1427,15 +1460,6 @@ float BaseMaterial3D::get_transmittance_depth() const { return transmittance_depth; } -void BaseMaterial3D::set_transmittance_curve(float p_curve) { - transmittance_curve = p_curve; - RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_curve, p_curve); -} - -float BaseMaterial3D::get_transmittance_curve() const { - return transmittance_curve; -} - void BaseMaterial3D::set_transmittance_boost(float p_boost) { transmittance_boost = p_boost; RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_boost, p_boost); @@ -1699,7 +1723,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { _validate_high_end("heightmap", property); if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) { @@ -1729,46 +1753,47 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { // alpha scissor slider isn't needed when alpha antialiasing is enabled if (property.name == "alpha_scissor_threshold" && transparency != TRANSPARENCY_ALPHA_SCISSOR) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } // alpha hash scale slider is only needed if transparency is alpha hash if (property.name == "alpha_hash_scale" && transparency != TRANSPARENCY_ALPHA_HASH) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "alpha_antialiasing_mode" && !can_select_aa) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } // we can't choose an antialiasing mode if alpha isn't possible if (property.name == "alpha_antialiasing_edge" && !alpha_aa_enabled) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "blend_mode" && alpha_aa_enabled) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "heightmap_min_layers" || property.name == "heightmap_max_layers") && !deep_parallax) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } - if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture" || property.name == "subsurf_scatter_transmittance_curve")) { - property.usage = 0; + if (flags[FLAG_SUBSURFACE_MODE_SKIN] && (property.name == "subsurf_scatter_transmittance_color" || property.name == "subsurf_scatter_transmittance_texture")) { + property.usage = PROPERTY_USAGE_NONE; } if (orm) { if (property.name == "shading_mode") { - property.hint_string = "Unshaded,PerPixel"; //vertex not supported in ORM mode, since no individual roughness. + // Vertex not supported in ORM mode, since no individual roughness. + property.hint_string = "Unshaded,Per-Pixel"; } if (property.name.begins_with("roughness") || property.name.begins_with("metallic") || property.name.begins_with("ao_texture")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } else { if (property.name == "orm_texture") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1776,47 +1801,47 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { if (shading_mode != SHADING_MODE_PER_VERTEX) { //these may still work per vertex if (property.name.begins_with("ao")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("emission")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("metallic")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("rim")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("roughness")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("subsurf_scatter")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } //these definitely only need per pixel if (property.name.begins_with("anisotropy")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("clearcoat")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("normal")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("backlight")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("transmittance")) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } } @@ -2065,7 +2090,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y) { +Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, RID *r_shader_rid) { int version = 0; if (p_shaded) { version = 1; @@ -2090,11 +2115,14 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b } if (materials_for_2d[version].is_valid()) { - return materials_for_2d[version]->get_rid(); + if (r_shader_rid) { + *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + } + return materials_for_2d[version]; } Ref<StandardMaterial3D> material; - material.instance(); + material.instantiate(); material->set_shading_mode(p_shaded ? SHADING_MODE_PER_PIXEL : SHADING_MODE_UNSHADED); material->set_transparency(p_transparent ? (p_opaque_prepass ? TRANSPARENCY_ALPHA_DEPTH_PRE_PASS : (p_cut_alpha ? TRANSPARENCY_ALPHA_SCISSOR : TRANSPARENCY_ALPHA)) : TRANSPARENCY_DISABLED); @@ -2108,7 +2136,11 @@ RID BaseMaterial3D::get_material_rid_for_2d(bool p_shaded, bool p_transparent, b materials_for_2d[version] = material; - return materials_for_2d[version]->get_rid(); + if (r_shader_rid) { + *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + } + + return materials_for_2d[version]; } void BaseMaterial3D::set_on_top_of_alpha() { @@ -2177,6 +2209,8 @@ BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const { } RID BaseMaterial3D::get_shader_rid() const { + MutexLock lock(material_mutex); + ((BaseMaterial3D *)this)->_update_shader(); ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); return shader_map[current_key].shader; } @@ -2248,9 +2282,6 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transmittance_depth", "depth"), &BaseMaterial3D::set_transmittance_depth); ClassDB::bind_method(D_METHOD("get_transmittance_depth"), &BaseMaterial3D::get_transmittance_depth); - ClassDB::bind_method(D_METHOD("set_transmittance_curve", "curve"), &BaseMaterial3D::set_transmittance_curve); - ClassDB::bind_method(D_METHOD("get_transmittance_curve"), &BaseMaterial3D::get_transmittance_curve); - ClassDB::bind_method(D_METHOD("set_transmittance_boost", "boost"), &BaseMaterial3D::set_transmittance_boost); ClassDB::bind_method(D_METHOD("get_transmittance_boost"), &BaseMaterial3D::get_transmittance_boost); @@ -2387,19 +2418,19 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &BaseMaterial3D::get_distance_fade_min_distance); ADD_GROUP("Transparency", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth PrePass"), "set_transparency", "get_transparency"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_blend_mode", "get_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_blend_mode", "get_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST); ADD_GROUP("Shading", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,PerPixel,PerVertex"), "set_shading_mode", "get_shading_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Oren Nayar,Toon"), "set_diffuse_mode", "get_diffuse_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Toon"), "set_diffuse_mode", "get_diffuse_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Blinn,Phong,Toon,Disabled"), "set_specular_mode", "get_specular_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT); @@ -2485,7 +2516,6 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "subsurf_scatter_transmittance_color"), "set_transmittance_color", "get_transmittance_color"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "subsurf_scatter_transmittance_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_SUBSURFACE_TRANSMITTANCE); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_depth", PROPERTY_HINT_RANGE, "0.001,8,0.001,or_greater"), "set_transmittance_depth", "get_transmittance_depth"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_curve", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_transmittance_curve", "get_transmittance_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "subsurf_scatter_transmittance_boost", PROPERTY_HINT_RANGE, "0.00,1.0,0.01"), "set_transmittance_boost", "get_transmittance_boost"); ADD_GROUP("Back Lighting", "backlight_"); @@ -2502,7 +2532,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Detail", "detail_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "detail_enabled"), "set_feature", "get_feature", FEATURE_DETAIL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_mask", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_MASK); - ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_detail_blend_mode", "get_detail_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_detail_blend_mode", "get_detail_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_uv_layer", PROPERTY_HINT_ENUM, "UV1,UV2"), "set_detail_uv", "get_detail_uv"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_ALBEDO); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_NORMAL); @@ -2653,7 +2683,6 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); BIND_ENUM_CONSTANT(DIFFUSE_LAMBERT); BIND_ENUM_CONSTANT(DIFFUSE_LAMBERT_WRAP); - BIND_ENUM_CONSTANT(DIFFUSE_OREN_NAYAR); BIND_ENUM_CONSTANT(DIFFUSE_TOON); BIND_ENUM_CONSTANT(SPECULAR_SCHLICK_GGX); @@ -2703,7 +2732,6 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_backlight(Color(0, 0, 0)); set_transmittance_color(Color(1, 1, 1, 1)); set_transmittance_depth(0.1); - set_transmittance_curve(1.0); set_transmittance_boost(0.0); set_refraction(0.05); set_point_size(1); diff --git a/scene/resources/material.h b/scene/resources/material.h index ad1b7b3e33..e2838e1399 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -79,6 +79,8 @@ class ShaderMaterial : public Material { GDCLASS(ShaderMaterial, Material); Ref<Shader> shader; + Map<StringName, Variant> param_cache; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -243,7 +245,6 @@ public: DIFFUSE_BURLEY, DIFFUSE_LAMBERT, DIFFUSE_LAMBERT_WRAP, - DIFFUSE_OREN_NAYAR, DIFFUSE_TOON, DIFFUSE_MAX }; @@ -390,7 +391,6 @@ private: StringName heightmap_scale; StringName subsurface_scattering_strength; StringName transmittance_color; - StringName transmittance_curve; StringName transmittance_depth; StringName transmittance_boost; StringName backlight; @@ -459,7 +459,6 @@ private: float transmittance_amount; Color transmittance_color; float transmittance_depth; - float transmittance_curve; float transmittance_boost; Color backlight; @@ -603,9 +602,6 @@ public: void set_transmittance_depth(float p_depth); float get_transmittance_depth() const; - void set_transmittance_curve(float p_curve); - float get_transmittance_curve() const; - void set_transmittance_boost(float p_boost); float get_transmittance_boost() const; @@ -739,7 +735,7 @@ public: static void finish_shaders(); static void flush_changes(); - static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false); + static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 33ad15b938..2f92872ad5 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -30,6 +30,7 @@ #include "mesh.h" +#include "core/math/convex_hull.h" #include "core/templates/pair.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" @@ -221,9 +222,17 @@ Vector<Face3> Mesh::get_faces() const { */ } -Ref<Shape3D> Mesh::create_convex_shape() const { - Vector<Vector3> vertices; +Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { + if (p_simplify) { + Vector<Ref<Shape3D>> decomposed = convex_decompose(1); + if (decomposed.size() == 1) { + return decomposed[0]; + } else { + ERR_PRINT("Convex shape simplification failed, falling back to simpler process."); + } + } + Vector<Vector3> vertices; for (int i = 0; i < get_surface_count(); i++) { Array a = surface_get_arrays(i); ERR_FAIL_COND_V(a.is_empty(), Ref<ConvexPolygonShape3D>()); @@ -232,6 +241,18 @@ Ref<Shape3D> Mesh::create_convex_shape() const { } Ref<ConvexPolygonShape3D> shape = memnew(ConvexPolygonShape3D); + + if (p_clean) { + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(vertices, md); + if (err == OK) { + shape->set_points(md.vertices); + return shape; + } else { + ERR_PRINT("Convex shape cleaning failed, falling back to simpler process."); + } + } + shape->set_points(vertices); return shape; } @@ -543,12 +564,12 @@ void Mesh::clear_cache() const { debug_lines.clear(); } -Vector<Ref<Shape3D>> Mesh::convex_decompose() const { +Vector<Ref<Shape3D>> Mesh::convex_decompose(int p_max_convex_hulls) const { ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>()); const Vector<Face3> faces = get_faces(); - Vector<Vector<Face3>> decomposed = convex_composition_function(faces); + Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_max_convex_hulls); Vector<Ref<Shape3D>> ret; @@ -571,7 +592,7 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { } Ref<ConvexPolygonShape3D> shape; - shape.instance(); + shape.instantiate(); shape->set_points(convex_points); ret.push_back(shape); } @@ -582,164 +603,328 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { int Mesh::get_builtin_bind_pose_count() const { return 0; } -Transform Mesh::get_builtin_bind_pose(int p_index) const { - return Transform(); + +Transform3D Mesh::get_builtin_bind_pose(int p_index) const { + return Transform3D(); } Mesh::Mesh() { } -#if 0 -static Vector<uint8_t> _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_format, uint32_t p_elements) { - enum ArrayType { - OLD_ARRAY_VERTEX = 0, - OLD_ARRAY_NORMAL = 1, - OLD_ARRAY_TANGENT = 2, - OLD_ARRAY_COLOR = 3, - OLD_ARRAY_TEX_UV = 4, - OLD_ARRAY_TEX_UV2 = 5, - OLD_ARRAY_BONES = 6, - OLD_ARRAY_WEIGHTS = 7, - OLD_ARRAY_INDEX = 8, - OLD_ARRAY_MAX = 9 - }; - - enum ArrayFormat { - /* OLD_ARRAY FORMAT FLAGS */ - OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory - OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL, - OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT, - OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR, - OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV, - OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2, - OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES, - OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS, - OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX, - - OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1), - OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE), - OLD_ARRAY_COMPRESS_DEFAULT = OLD_ARRAY_COMPRESS_NORMAL | OLD_ARRAY_COMPRESS_TANGENT | OLD_ARRAY_COMPRESS_COLOR | OLD_ARRAY_COMPRESS_TEX_UV | OLD_ARRAY_COMPRESS_TEX_UV2, - - OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1, - OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3, - }; - - bool vertex_16bit = p_format & ((1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE))); - bool has_bones = (p_format & OLD_ARRAY_FORMAT_BONES); - bool bone_8 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_INDEX << 2)); - bool weight_32 = has_bones && !(p_format & (OLD_ARRAY_COMPRESS_TEX_UV2 << 2)); - - print_line("convert vertex16: " + itos(vertex_16bit) + " convert bone 8 " + itos(bone_8) + " convert weight 32 " + itos(weight_32)); - - if (!vertex_16bit && !bone_8 && !weight_32) { - return p_src; - } +enum OldArrayType { + OLD_ARRAY_VERTEX, + OLD_ARRAY_NORMAL, + OLD_ARRAY_TANGENT, + OLD_ARRAY_COLOR, + OLD_ARRAY_TEX_UV, + OLD_ARRAY_TEX_UV2, + OLD_ARRAY_BONES, + OLD_ARRAY_WEIGHTS, + OLD_ARRAY_INDEX, + OLD_ARRAY_MAX, +}; + +enum OldArrayFormat { + /* OLD_ARRAY FORMAT FLAGS */ + OLD_ARRAY_FORMAT_VERTEX = 1 << OLD_ARRAY_VERTEX, // mandatory + OLD_ARRAY_FORMAT_NORMAL = 1 << OLD_ARRAY_NORMAL, + OLD_ARRAY_FORMAT_TANGENT = 1 << OLD_ARRAY_TANGENT, + OLD_ARRAY_FORMAT_COLOR = 1 << OLD_ARRAY_COLOR, + OLD_ARRAY_FORMAT_TEX_UV = 1 << OLD_ARRAY_TEX_UV, + OLD_ARRAY_FORMAT_TEX_UV2 = 1 << OLD_ARRAY_TEX_UV2, + OLD_ARRAY_FORMAT_BONES = 1 << OLD_ARRAY_BONES, + OLD_ARRAY_FORMAT_WEIGHTS = 1 << OLD_ARRAY_WEIGHTS, + OLD_ARRAY_FORMAT_INDEX = 1 << OLD_ARRAY_INDEX, + + OLD_ARRAY_COMPRESS_BASE = (OLD_ARRAY_INDEX + 1), + OLD_ARRAY_COMPRESS_VERTEX = 1 << (OLD_ARRAY_VERTEX + OLD_ARRAY_COMPRESS_BASE), // mandatory + OLD_ARRAY_COMPRESS_NORMAL = 1 << (OLD_ARRAY_NORMAL + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TANGENT = 1 << (OLD_ARRAY_TANGENT + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_COLOR = 1 << (OLD_ARRAY_COLOR + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV = 1 << (OLD_ARRAY_TEX_UV + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_TEX_UV2 = 1 << (OLD_ARRAY_TEX_UV2 + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_BONES = 1 << (OLD_ARRAY_BONES + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_WEIGHTS = 1 << (OLD_ARRAY_WEIGHTS + OLD_ARRAY_COMPRESS_BASE), + OLD_ARRAY_COMPRESS_INDEX = 1 << (OLD_ARRAY_INDEX + OLD_ARRAY_COMPRESS_BASE), + + OLD_ARRAY_FLAG_USE_2D_VERTICES = OLD_ARRAY_COMPRESS_INDEX << 1, + OLD_ARRAY_FLAG_USE_16_BIT_BONES = OLD_ARRAY_COMPRESS_INDEX << 2, + OLD_ARRAY_FLAG_USE_DYNAMIC_UPDATE = OLD_ARRAY_COMPRESS_INDEX << 3, - bool vertex_2d = (p_format & (OLD_ARRAY_COMPRESS_INDEX << 1)); +}; - uint32_t src_stride = p_src.size() / p_elements; - uint32_t dst_stride = src_stride + (vertex_16bit ? 4 : 0) + (bone_8 ? 4 : 0) - (weight_32 ? 8 : 0); +static Array _convert_old_array(const Array &p_old) { + Array new_array; + new_array.resize(Mesh::ARRAY_MAX); + new_array[Mesh::ARRAY_VERTEX] = p_old[OLD_ARRAY_VERTEX]; + new_array[Mesh::ARRAY_NORMAL] = p_old[OLD_ARRAY_NORMAL]; + new_array[Mesh::ARRAY_TANGENT] = p_old[OLD_ARRAY_TANGENT]; + new_array[Mesh::ARRAY_COLOR] = p_old[OLD_ARRAY_COLOR]; + new_array[Mesh::ARRAY_TEX_UV] = p_old[OLD_ARRAY_TEX_UV]; + new_array[Mesh::ARRAY_TEX_UV2] = p_old[OLD_ARRAY_TEX_UV2]; + new_array[Mesh::ARRAY_BONES] = p_old[OLD_ARRAY_BONES]; + new_array[Mesh::ARRAY_WEIGHTS] = p_old[OLD_ARRAY_WEIGHTS]; + new_array[Mesh::ARRAY_INDEX] = p_old[OLD_ARRAY_INDEX]; + return new_array; +} + +static Mesh::PrimitiveType _old_primitives[7] = { + Mesh::PRIMITIVE_POINTS, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_LINE_STRIP, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_TRIANGLES, + Mesh::PRIMITIVE_TRIANGLE_STRIP, + Mesh::PRIMITIVE_TRIANGLE_STRIP +}; - Vector<uint8_t> ret = p_src; +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; + uint32_t dst_attribute_stride; + uint32_t dst_skin_stride; + uint32_t dst_offsets[Mesh::ARRAY_MAX]; + RenderingServer::get_singleton()->mesh_surface_make_offsets_from_format(p_new_format & (~RS::ARRAY_FORMAT_INDEX), p_elements, 0, dst_offsets, dst_vertex_stride, dst_attribute_stride, dst_skin_stride); - ret.resize(dst_stride * p_elements); - { - uint8_t *w = ret.ptrw(); - const uint8_t *r = p_src.ptr(); - - for (uint32_t i = 0; i < p_elements; i++) { - uint32_t remaining = src_stride; - const uint8_t *src = (const uint8_t *)(r + src_stride * i); - uint8_t *dst = (uint8_t *)(w + dst_stride * i); - - if (!vertex_2d) { //3D - if (vertex_16bit) { - float *dstw = (float *)dst; - const uint16_t *srcr = (const uint16_t *)src; - dstw[0] = Math::half_to_float(srcr[0]); - dstw[1] = Math::half_to_float(srcr[1]); - dstw[2] = Math::half_to_float(srcr[2]); - remaining -= 8; - src += 8; + vertex_data.resize(dst_vertex_stride * p_elements); + attribute_data.resize(dst_attribute_stride * p_elements); + skin_data.resize(dst_skin_stride * p_elements); + + uint8_t *dst_vertex_ptr = vertex_data.ptrw(); + uint8_t *dst_attribute_ptr = attribute_data.ptrw(); + uint8_t *dst_skin_ptr = skin_data.ptrw(); + + const uint8_t *src_vertex_ptr = p_src.ptr(); + uint32_t src_vertex_stride = p_src.size() / p_elements; + + uint32_t src_offset = 0; + for (uint32_t j = 0; j < OLD_ARRAY_INDEX; j++) { + if (!(p_old_format & (1 << j))) { + continue; + } + switch (j) { + case OLD_ARRAY_VERTEX: { + if (p_old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) { + if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } } else { - src += 12; - remaining -= 12; + if (p_old_format & OLD_ARRAY_COMPRESS_VERTEX) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + dst[2] = Math::half_to_float(src[2]); + } + src_offset += sizeof(uint16_t) * 4; //+pad + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride]; + float *dst = (float *)&dst_vertex_ptr[i * dst_vertex_stride]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + src_offset += sizeof(float) * 3; + } } - dst += 12; - } else { - if (vertex_16bit) { - float *dstw = (float *)dst; - const uint16_t *srcr = (const uint16_t *)src; - dstw[0] = Math::half_to_float(srcr[0]); - dstw[1] = Math::half_to_float(srcr[1]); - remaining -= 4; - src += 4; + } break; + case OLD_ARRAY_NORMAL: { + if (p_old_format & OLD_ARRAY_COMPRESS_NORMAL) { + const float multiplier = 1.f / 127.f * 1023.0f; + + for (uint32_t i = 0; i < p_elements; i++) { + const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); + *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; + } + src_offset += sizeof(uint32_t); } else { - src += 8; - remaining -= 8; + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); + *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; + } + src_offset += sizeof(float) * 3; } - dst += 8; - } - - if (has_bones) { - remaining -= bone_8 ? 4 : 8; - remaining -= weight_32 ? 16 : 8; - } - - for (uint32_t j = 0; j < remaining; j++) { - dst[j] = src[j]; - } - if (has_bones) { - dst += remaining; - src += remaining; - - if (bone_8) { - const uint8_t *src_bones = (const uint8_t *)src; - uint16_t *dst_bones = (uint16_t *)dst; + } break; + case OLD_ARRAY_TANGENT: { + if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { + const float multiplier = 1.f / 127.f * 1023.0f; + + for (uint32_t i = 0; i < p_elements; i++) { + const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * multiplier), 0, 1023); + *dst |= CLAMP(int(src[1] * multiplier), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * multiplier), 0, 1023) << 20; + if (src[3] > 0) { + *dst |= 3 << 30; + } + } + src_offset += sizeof(uint32_t); + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]]; + + *dst = 0; + *dst |= CLAMP(int(src[0] * 1023.0), 0, 1023); + *dst |= CLAMP(int(src[1] * 1023.0), 0, 1023) << 10; + *dst |= CLAMP(int(src[2] * 1023.0), 0, 1023) << 20; + if (src[3] > 0) { + *dst |= 3 << 30; + } + } + src_offset += sizeof(float) * 4; + } - dst_bones[0] = src_bones[0]; - dst_bones[1] = src_bones[1]; - dst_bones[2] = src_bones[2]; - dst_bones[3] = src_bones[3]; + } break; + case OLD_ARRAY_COLOR: { + if (p_old_format & OLD_ARRAY_COMPRESS_COLOR) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint32_t *src = (const uint32_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint32_t *dst = (uint32_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]]; - src += 4; + *dst = *src; + } + src_offset += sizeof(uint32_t); } else { - for (uint32_t j = 0; j < 8; j++) { - dst[j] = src[j]; + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint8_t *dst = (uint8_t *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_COLOR]]; + + dst[0] = uint8_t(CLAMP(src[0] * 255.0, 0.0, 255.0)); + dst[1] = uint8_t(CLAMP(src[1] * 255.0, 0.0, 255.0)); + dst[2] = uint8_t(CLAMP(src[2] * 255.0, 0.0, 255.0)); + dst[3] = uint8_t(CLAMP(src[3] * 255.0, 0.0, 255.0)); } - - src += 8; + src_offset += sizeof(float) * 4; } + } break; + case OLD_ARRAY_TEX_UV: { + if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]]; + + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV]]; - dst += 8; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } - if (weight_32) { - const float *src_weights = (const float *)src; - uint16_t *dst_weights = (uint16_t *)dst; + } break; + case OLD_ARRAY_TEX_UV2: { + if (p_old_format & OLD_ARRAY_COMPRESS_TEX_UV2) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]]; - dst_weights[0] = CLAMP(src_weights[0] * 65535, 0, 65535); //16bits unorm - dst_weights[1] = CLAMP(src_weights[1] * 65535, 0, 65535); - dst_weights[2] = CLAMP(src_weights[2] * 65535, 0, 65535); - dst_weights[3] = CLAMP(src_weights[3] * 65535, 0, 65535); + dst[0] = Math::half_to_float(src[0]); + dst[1] = Math::half_to_float(src[1]); + } + src_offset += sizeof(uint16_t) * 2; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + float *dst = (float *)&dst_attribute_ptr[i * dst_attribute_stride + dst_offsets[Mesh::ARRAY_TEX_UV2]]; + dst[0] = src[0]; + dst[1] = src[1]; + } + src_offset += sizeof(float) * 2; + } + } break; + case OLD_ARRAY_BONES: { + if (p_old_format & OLD_ARRAY_FLAG_USE_16_BIT_BONES) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + src_offset += sizeof(uint16_t) * 4; + } else { + for (uint32_t i = 0; i < p_elements; i++) { + const uint8_t *src = (const uint8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_BONES]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + src_offset += sizeof(uint8_t) * 4; + } + } break; + case OLD_ARRAY_WEIGHTS: { + if (p_old_format & OLD_ARRAY_COMPRESS_WEIGHTS) { + for (uint32_t i = 0; i < p_elements; i++) { + const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]]; + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + } + src_offset += sizeof(uint16_t) * 4; } else { - for (uint32_t j = 0; j < 8; j++) { - dst[j] = src[j]; + for (uint32_t i = 0; i < p_elements; i++) { + const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; + uint16_t *dst = (uint16_t *)&dst_skin_ptr[i * dst_skin_stride + dst_offsets[Mesh::ARRAY_WEIGHTS]]; + + dst[0] = uint16_t(CLAMP(src[0] * 65535.0, 0, 65535.0)); + dst[1] = uint16_t(CLAMP(src[1] * 65535.0, 0, 65535.0)); + dst[2] = uint16_t(CLAMP(src[2] * 65535.0, 0, 65535.0)); + dst[3] = uint16_t(CLAMP(src[3] * 65535.0, 0, 65535.0)); } + src_offset += sizeof(float) * 4; } + } break; + default: { } } } - - return ret; } -#endif bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -778,10 +963,13 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { if (d.has("arrays")) { //oldest format (2.x) ERR_FAIL_COND_V(!d.has("morph_arrays"), false); - add_surface_from_arrays(PrimitiveType(int(d["primitive"])), d["arrays"], d["morph_arrays"]); + Array morph_arrays = d["morph_arrays"]; + for (int i = 0; i < morph_arrays.size(); i++) { + morph_arrays[i] = _convert_old_array(morph_arrays[i]); + } + add_surface_from_arrays(_old_primitives[int(d["primitive"])], _convert_old_array(d["arrays"]), morph_arrays); } else if (d.has("array_data")) { -#if 0 //print_line("array data (old style"); //older format (3.x) Vector<uint8_t> array_data = d["array_data"]; @@ -791,48 +979,76 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } ERR_FAIL_COND_V(!d.has("format"), false); - uint32_t format = d["format"]; + uint32_t old_format = d["format"]; uint32_t primitive = d["primitive"]; - uint32_t primitive_remap[7] = { - PRIMITIVE_POINTS, - PRIMITIVE_LINES, - PRIMITIVE_LINE_STRIP, - PRIMITIVE_LINES, - PRIMITIVE_TRIANGLES, - PRIMITIVE_TRIANGLE_STRIP, - PRIMITIVE_TRIANGLE_STRIP - }; - - primitive = primitive_remap[primitive]; //compatibility + primitive = _old_primitives[primitive]; //compatibility ERR_FAIL_COND_V(!d.has("vertex_count"), false); int vertex_count = d["vertex_count"]; - array_data = _fix_array_compatibility(array_data, format, vertex_count); + uint32_t new_format = ARRAY_FORMAT_VERTEX; + + if (old_format & OLD_ARRAY_FORMAT_NORMAL) { + new_format |= ARRAY_FORMAT_NORMAL; + } + if (old_format & OLD_ARRAY_FORMAT_TANGENT) { + new_format |= ARRAY_FORMAT_TANGENT; + } + if (old_format & OLD_ARRAY_FORMAT_COLOR) { + new_format |= ARRAY_FORMAT_COLOR; + } + if (old_format & OLD_ARRAY_FORMAT_TEX_UV) { + new_format |= ARRAY_FORMAT_TEX_UV; + } + if (old_format & OLD_ARRAY_FORMAT_TEX_UV2) { + new_format |= ARRAY_FORMAT_TEX_UV2; + } + if (old_format & OLD_ARRAY_FORMAT_BONES) { + new_format |= ARRAY_FORMAT_BONES; + } + if (old_format & OLD_ARRAY_FORMAT_WEIGHTS) { + new_format |= ARRAY_FORMAT_WEIGHTS; + } + if (old_format & OLD_ARRAY_FORMAT_INDEX) { + new_format |= ARRAY_FORMAT_INDEX; + } + if (old_format & OLD_ARRAY_FLAG_USE_2D_VERTICES) { + new_format |= OLD_ARRAY_FLAG_USE_2D_VERTICES; + } + + Vector<uint8_t> vertex_array; + Vector<uint8_t> attribute_array; + Vector<uint8_t> skin_array; + + _fix_array_compatibility(array_data, old_format, new_format, vertex_count, vertex_array, attribute_array, skin_array); int index_count = 0; if (d.has("index_count")) { index_count = d["index_count"]; } - Vector<Vector<uint8_t>> blend_shapes; + Vector<uint8_t> blend_shapes; if (d.has("blend_shape_data")) { Array blend_shape_data = d["blend_shape_data"]; for (int i = 0; i < blend_shape_data.size(); i++) { + Vector<uint8_t> blend_vertex_array; + Vector<uint8_t> blend_attribute_array; + Vector<uint8_t> blend_skin_array; + Vector<uint8_t> shape = blend_shape_data[i]; - shape = _fix_array_compatibility(shape, format, vertex_count); + _fix_array_compatibility(shape, old_format, new_format, vertex_count, blend_vertex_array, blend_attribute_array, blend_skin_array); - blend_shapes.push_back(shape); + blend_shapes.append_array(blend_vertex_array); } } //clear unused flags - print_line("format pre: " + itos(format)); - format &= ~uint32_t((1 << (ARRAY_VERTEX + ARRAY_COMPRESS_BASE)) | (ARRAY_COMPRESS_INDEX << 2) | (ARRAY_COMPRESS_TEX_UV2 << 2)); - print_line("format post: " + itos(format)); + print_line("format pre: " + itos(old_format)); + + print_line("format post: " + itos(new_format)); ERR_FAIL_COND_V(!d.has("aabb"), false); AABB aabb = d["aabb"]; @@ -847,8 +1063,8 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } } - add_surface(format, PrimitiveType(primitive), array_data, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb); -#endif + add_surface(new_format, PrimitiveType(primitive), vertex_array, attribute_array, skin_array, vertex_count, array_index_data, index_count, aabb, blend_shapes, bone_aabb); + } else { ERR_FAIL_V(false); } @@ -1119,9 +1335,9 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < surfaces.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } else { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } } } @@ -1334,9 +1550,21 @@ String ArrayMesh::surface_get_name(int p_idx) const { return surfaces[p_idx].name; } -void ArrayMesh::surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +void ArrayMesh::surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, p_surface, p_offset, p_data); + emit_changed(); +} + +void ArrayMesh::surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, p_surface, p_offset, p_data); + emit_changed(); +} + +void ArrayMesh::surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data) { ERR_FAIL_INDEX(p_surface, surfaces.size()); - RS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data); + RS::get_singleton()->mesh_surface_update_skin_region(mesh, p_surface, p_offset, p_data); emit_changed(); } @@ -1410,12 +1638,12 @@ struct ArrayMeshLightmapSurface { uint32_t format = 0; }; -Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) { +Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_texel_size) { Vector<uint8_t> null_cache; return lightmap_unwrap_cached(p_base_transform, p_texel_size, null_cache, null_cache, false); } -Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) { +Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); @@ -1431,7 +1659,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float Basis basis = p_base_transform.get_basis(); Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); - Transform transform; + Transform3D transform; transform.scale(scale); Basis normal_basis = transform.basis.inverse().transposed(); @@ -1536,7 +1764,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float for (int i = 0; i < lightmap_surfaces.size(); i++) { Ref<SurfaceTool> st; - st.instance(); + st.instantiate(); st->begin(Mesh::PRIMITIVE_TRIANGLES); st->set_material(lightmap_surfaces[i].material); surfaces_tools.push_back(st); //stay there @@ -1634,7 +1862,9 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); - ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region); + ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region); + ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region); + ClassDB::bind_method(D_METHOD("surface_update_skin_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_skin_region); ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len); ClassDB::bind_method(D_METHOD("surface_get_array_index_len", "surf_idx"), &ArrayMesh::surface_get_array_index_len); ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format); @@ -1643,7 +1873,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape); - ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape); + ClassDB::bind_method(D_METHOD("create_convex_shape", "clean", "simplify"), &ArrayMesh::create_convex_shape, DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline); ClassDB::bind_method(D_METHOD("regen_normal_maps"), &ArrayMesh::regen_normal_maps); ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normal_maps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index aa830d7b50..27b0eb098b 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -149,7 +149,7 @@ public: void generate_debug_mesh_indices(Vector<Vector3> &r_points); Ref<Shape3D> create_trimesh_shape() const; - Ref<Shape3D> create_convex_shape() const; + Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; Ref<Mesh> create_outline(float p_margin) const; @@ -159,14 +159,14 @@ public: Size2i get_lightmap_size_hint() const; void clear_cache() const; - typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &); + typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls); static ConvexDecompositionFunc convex_composition_function; - Vector<Ref<Shape3D>> convex_decompose() const; + Vector<Ref<Shape3D>> convex_decompose(int p_max_convex_hulls = -1) const; virtual int get_builtin_bind_pose_count() const; - virtual Transform get_builtin_bind_pose(int p_index) const; + virtual Transform3D get_builtin_bind_pose(int p_index) const; Mesh(); }; @@ -233,7 +233,9 @@ public: void set_blend_shape_mode(BlendShapeMode p_mode); BlendShapeMode get_blend_shape_mode() const; - void surface_update_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_vertex_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_attribute_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); + void surface_update_skin_region(int p_surface, int p_offset, const Vector<uint8_t> &p_data); int get_surface_count() const override; @@ -262,8 +264,8 @@ public: void regen_normal_maps(); - Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); - Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true); + Error lightmap_unwrap(const Transform3D &p_base_transform = Transform3D(), float p_texel_size = 0.05); + Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true); virtual void reload_from_file() override; diff --git a/scene/resources/mesh_data_tool.h b/scene/resources/mesh_data_tool.h index f5c8f11437..b0ebfba7ee 100644 --- a/scene/resources/mesh_data_tool.h +++ b/scene/resources/mesh_data_tool.h @@ -33,8 +33,8 @@ #include "scene/resources/mesh.h" -class MeshDataTool : public Reference { - GDCLASS(MeshDataTool, Reference); +class MeshDataTool : public RefCounted { + GDCLASS(MeshDataTool, RefCounted); int format = 0; struct Vertex { diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index ad90481fbd..33c9ca6d1e 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -97,10 +97,10 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { String name = "item/" + itos(E->key()) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, name + "name")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "mesh_transform")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform")); p_list->push_back(PropertyInfo(Variant::ARRAY, name + "shapes")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "navmesh_transform")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "navmesh_transform")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "preview", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER)); } } @@ -145,7 +145,7 @@ void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navm notify_property_list_changed(); } -void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_transform) { +void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].navmesh_transform = p_transform; notify_change_to_owners(); @@ -180,8 +180,8 @@ Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const { return item_map[p_item].navmesh; } -Transform MeshLibrary::get_item_navmesh_transform(int p_item) const { - ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); +Transform3D MeshLibrary::get_item_navmesh_transform(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); return item_map[p_item].navmesh_transform; } diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 1da624c275..1e8a6bf3ff 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -44,14 +44,14 @@ class MeshLibrary : public Resource { public: struct ShapeData { Ref<Shape3D> shape; - Transform local_transform; + Transform3D local_transform; }; struct Item { String name; Ref<Mesh> mesh; Vector<ShapeData> shapes; Ref<Texture2D> preview; - Transform navmesh_transform; + Transform3D navmesh_transform; Ref<NavigationMesh> navmesh; }; @@ -73,13 +73,13 @@ public: void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); - void set_item_navmesh_transform(int p_item, const Transform &p_transform); + void set_item_navmesh_transform(int p_item, const Transform3D &p_transform); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_preview(int p_item, const Ref<Texture2D> &p_preview); String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; Ref<NavigationMesh> get_item_navmesh(int p_item) const; - Transform get_item_navmesh_transform(int p_item) const; + Transform3D get_item_navmesh_transform(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const; Ref<Texture2D> get_item_preview(int p_item) const; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 4991887eb3..8894f0bb11 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -50,7 +50,7 @@ void MultiMesh::_set_transform_array(const Vector<Vector3> &p_array) { const Vector3 *r = xforms.ptr(); for (int i = 0; i < len / 4; i++) { - Transform t; + Transform3D t; t.basis[0] = r[i * 4 + 0]; t.basis[1] = r[i * 4 + 1]; t.basis[2] = r[i * 4 + 2]; @@ -75,7 +75,7 @@ Vector<Vector3> MultiMesh::_get_transform_array() const { Vector3 *w = xforms.ptrw(); for (int i = 0; i < instance_count; i++) { - Transform t = get_instance_transform(i); + Transform3D t = get_instance_transform(i); w[i * 4 + 0] = t.basis[0]; w[i * 4 + 1] = t.basis[1]; w[i * 4 + 2] = t.basis[2]; @@ -236,7 +236,7 @@ int MultiMesh::get_visible_instance_count() const { return visible_instance_count; } -void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) { +void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_transform) { RenderingServer::get_singleton()->multimesh_instance_set_transform(multimesh, p_instance, p_transform); } @@ -244,7 +244,7 @@ void MultiMesh::set_instance_transform_2d(int p_instance, const Transform2D &p_t RenderingServer::get_singleton()->multimesh_instance_set_transform_2d(multimesh, p_instance, p_transform); } -Transform MultiMesh::get_instance_transform(int p_instance) const { +Transform3D MultiMesh::get_instance_transform(int p_instance) const { return RenderingServer::get_singleton()->multimesh_instance_get_transform(multimesh, p_instance); } @@ -349,10 +349,10 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array); ClassDB::bind_method(D_METHOD("_get_custom_data_array"), &MultiMesh::_get_custom_data_array); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", 0), "_set_transform_array", "_get_transform_array"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "transform_2d_array", PROPERTY_HINT_NONE, "", 0), "_set_transform_2d_array", "_get_transform_2d_array"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", 0), "_set_color_array", "_get_color_array"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", 0), "_set_custom_data_array", "_get_custom_data_array"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_array", "_get_transform_array"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "transform_2d_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_2d_array", "_get_transform_2d_array"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_color_array", "_get_color_array"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_custom_data_array", "_get_custom_data_array"); #endif BIND_ENUM_CONSTANT(TRANSFORM_2D); diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index ca5c42d47a..2fe0927e6f 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -92,9 +92,9 @@ public: void set_visible_instance_count(int p_count); int get_visible_instance_count() const; - void set_instance_transform(int p_instance, const Transform &p_transform); + void set_instance_transform(int p_instance, const Transform3D &p_transform); void set_instance_transform_2d(int p_instance, const Transform2D &p_transform); - Transform get_instance_transform(int p_instance) const; + Transform3D get_instance_transform(int p_instance) const; Transform2D get_instance_transform_2d(int p_instance) const; void set_instance_color(int p_instance, const Color &p_color); diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 0a25bb2ed1..d2be2bdba1 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -501,14 +501,14 @@ void NavigationMesh::_bind_methods() { void NavigationMesh::_validate_property(PropertyInfo &property) const { if (property.name == "geometry/collision_mask") { if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } if (property.name == "geometry/source_group_name") { if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index ab8a4b7934..9bb2a4ddb8 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -41,11 +41,11 @@ #define PACKED_SCENE_VERSION 2 -bool SceneState::can_instance() const { +bool SceneState::can_instantiate() const { return nodes.size() > 0; } -Node *SceneState::instance(GenEditState p_edit_state) const { +Node *SceneState::instantiate(GenEditState p_edit_state) const { // nodes where instancing failed (because something is missing) List<Node *> stray_instances; @@ -109,7 +109,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { //scene inheritance on root node Ref<PackedScene> sdata = props[base_scene_idx]; ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); //only main gets main edit state ERR_FAIL_COND_V(!node, nullptr); if (p_edit_state != GEN_EDIT_STATE_DISABLED) { node->set_scene_inherited_state(sdata->get_state()); @@ -122,7 +122,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (disable_placeholders) { Ref<PackedScene> sdata = ResourceLoader::load(path, "PackedScene"); ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); ERR_FAIL_COND_V(!node, nullptr); } else { InstancePlaceholder *ip = memnew(InstancePlaceholder); @@ -133,7 +133,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } else { Ref<PackedScene> sdata = props[n.instance & FLAG_MASK]; ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); - node = sdata->instance(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); + node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); ERR_FAIL_COND_V(!node, nullptr); } @@ -152,7 +152,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (ClassDB::is_class_enabled(snames[n.type])) { //node belongs to this scene and must be created - obj = ClassDB::instance(snames[n.type]); + obj = ClassDB::instantiate(snames[n.type]); } if (!Object::cast_to<Node>(obj)) { @@ -180,7 +180,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } if (node) { - // may not have found the node (part of instanced scene and removed) + // may not have found the node (part of instantiated scene and removed) // if found all is good, otherwise ignore //properties @@ -266,7 +266,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { parent->move_child(node, n.index); } } else { - //it may be possible that an instanced scene has changed + //it may be possible that an instantiated scene has changed //and the node has nowhere to go anymore stray_instances.push_back(node); //can't be added, go to stray list } @@ -368,7 +368,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { // this function handles all the work related to properly packing scenes, be it - // instanced or inherited. + // instantiated or inherited. // given the complexity of this process, an attempt will be made to properly // document it. if you fail to understand something, please ask! @@ -377,7 +377,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } - // save the child instanced scenes that are chosen as editable, so they can be restored + // save the child instantiated scenes that are chosen as editable, so they can be restored // upon load back if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) { editable_instances.push_back(p_owner->get_path_to(p_node)); @@ -386,7 +386,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map NodeData nd; nd.name = _nm_get_string(p_node->get_name(), name_map); - nd.instance = -1; //not instanced by default + nd.instance = -1; //not instantiated by default //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { @@ -396,18 +396,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map //This (hopefully) happens if the node is a scene root, so its index is irrelevant. nd.index = -1; } else { - //part of an inherited scene, or parent is from an instanced scene + //part of an inherited scene, or parent is from an instantiated scene nd.index = p_node->get_index(); } - // if this node is part of an instanced scene or sub-instanced scene + // if this node is part of an instantiated scene or sub-instantiated scene // we need to get the corresponding instance states. // with the instance states, we can query for identical properties/groups // and only save what has changed List<PackState> pack_state_stack; - bool instanced_by_owner = true; + bool instantiated_by_owner = true; { Node *n = p_node; @@ -423,11 +423,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map ps.node = node; ps.state = state; pack_state_stack.push_back(ps); - instanced_by_owner = false; + instantiated_by_owner = false; } } - if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instanced_by_owner) { + if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { if (p_node->get_scene_instance_load_placeholder()) { //it's a placeholder, use the placeholder path nd.instance = _vm_get_variant(p_node->get_filename(), variant_map); @@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map StringName type = p_node->get_class(); Ref<Script> script = p_node->get_script(); - if (script.is_valid()) { + if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) { + // Should be called in the editor only and not at runtime, + // otherwise it can cause problems because of missing instance state support. script->update_exports(); } @@ -500,8 +502,8 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // } if (pack_state_stack.size()) { - // we are on part of an instanced subscene - // or part of instanced scene. + // we are on part of an instantiated subscene + // or part of instantiated scene. // only save what has been changed // only save changed properties in instance @@ -571,7 +573,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map } /* if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name)) - continue; //group was instanced, don't add here + continue; //group was instantiated, don't add here */ bool skip = false; @@ -594,7 +596,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // save the right owner // for the saved scene root this is -1 // for nodes of the saved scene this is 0 - // for nodes of instanced scenes this is >0 + // for nodes of instantiated scenes this is >0 if (p_node == p_owner) { //saved scene root @@ -612,20 +614,20 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map //this node is not part of an instancing process, so save the type nd.type = _nm_get_string(p_node->get_class(), name_map); } else { - // this node is part of an instanced process, so do not save the type. - // instead, save that it was instanced + // this node is part of an instantiated process, so do not save the type. + // instead, save that it was instantiated nd.type = TYPE_INSTANCED; } // determine whether to save this node or not - // if this node is part of an instanced sub-scene, we can skip storing it if basically + // if this node is part of an instantiated sub-scene, we can skip storing it if basically // no properties changed and no groups were added to it. // below condition is true for all nodes of the scene being saved, and ones in subscenes // that hold changes bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist save_node = save_node || p_node == p_owner; // owner is always saved - save_node = save_node || (p_node->get_owner() == p_owner && instanced_by_owner); //part of scene and not instanced + save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated int idx = nodes.size(); int parent_node = NO_PARENT_SAVED; @@ -893,6 +895,13 @@ Error SceneState::pack(Node *p_scene) { node_paths.write[E->get()] = scene->get_path_to(E->key()); } + if (Engine::get_singleton()->is_editor_hint()) { + // Build node path cache + for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) { + node_path_cache[scene->get_path_to(E->key())] = E->get(); + } + } + return OK; } @@ -927,10 +936,12 @@ Ref<SceneState> SceneState::_get_base_scene_state() const { } int SceneState::find_node_by_path(const NodePath &p_node) const { + ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built."); + if (!node_path_cache.has(p_node)) { if (_get_base_scene_state().is_valid()) { int idx = _get_base_scene_state()->find_node_by_path(p_node); - if (idx >= 0) { + if (idx != -1) { int rkey = _find_base_scene_node_remap_key(idx); if (rkey == -1) { rkey = nodes.size() + base_scene_node_remap.size(); @@ -946,7 +957,7 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { //for nodes that _do_ exist in current scene, still try to look for - //the node in the instanced scene, as a property may be missing + //the node in the instantiated scene, as a property may be missing //from the local one int idx = _get_base_scene_state()->find_node_by_path(p_node); if (idx != -1) { @@ -1614,16 +1625,16 @@ void PackedScene::clear() { state->clear(); } -bool PackedScene::can_instance() const { - return state->can_instance(); +bool PackedScene::can_instantiate() const { + return state->can_instantiate(); } -Node *PackedScene::instance(GenEditState p_edit_state) const { +Node *PackedScene::instantiate(GenEditState p_edit_state) const { #ifndef TOOLS_ENABLED ERR_FAIL_COND_V_MSG(p_edit_state != GEN_EDIT_STATE_DISABLED, nullptr, "Edit state is only for editors, does not work without tools compiled."); #endif - Node *s = state->instance((SceneState::GenEditState)p_edit_state); + Node *s = state->instantiate((SceneState::GenEditState)p_edit_state); if (!s) { return nullptr; } @@ -1671,8 +1682,8 @@ void PackedScene::reset_state() { } void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); - ClassDB::bind_method(D_METHOD("instance", "edit_state"), &PackedScene::instance, DEFVAL(GEN_EDIT_STATE_DISABLED)); - ClassDB::bind_method(D_METHOD("can_instance"), &PackedScene::can_instance); + ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); + ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 78a0aeaa9a..55708f7914 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -34,8 +34,8 @@ #include "core/io/resource.h" #include "scene/main/node.h" -class SceneState : public Reference { - GDCLASS(SceneState, Reference); +class SceneState : public RefCounted { + GDCLASS(SceneState, RefCounted); Vector<StringName> names; Vector<Variant> variants; @@ -136,8 +136,8 @@ public: void clear(); - bool can_instance() const; - Node *instance(GenEditState p_edit_state) const; + bool can_instantiate() const; + Node *instantiate(GenEditState p_edit_state) const; //unbuild API @@ -213,8 +213,8 @@ public: void clear(); - bool can_instance() const; - Node *instance(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const; + bool can_instantiate() const; + Node *instantiate(GenEditState p_edit_state = GEN_EDIT_STATE_DISABLED) const; void recreate_state(); void replace_state(Ref<SceneState> p_by); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 59e699326d..00bea312b3 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -569,7 +569,7 @@ void ParticlesMaterial::_update_shader() { code += " vec4(1.250, -1.050, -0.203, 0.0),\n"; code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n"; if (color_ramp.is_valid()) { - code += " COLOR = hue_rot_mat * textureLod(color_ramp, vec2(tv, 0.0), 0.0);\n"; + code += " COLOR = hue_rot_mat * textureLod(color_ramp, vec2(tv, 0.0), 0.0) * color_value;\n"; } else { code += " COLOR = hue_rot_mat * color_value;\n"; } @@ -852,52 +852,54 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture2D tex_parameters[p_param] = p_texture; + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + switch (p_param) { case PARAM_INITIAL_LINEAR_VELOCITY: { //do none for this one } break; case PARAM_ANGULAR_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, tex_rid); _adjust_curve_range(p_texture, -360, 360); } break; case PARAM_ORBIT_VELOCITY: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, tex_rid); _adjust_curve_range(p_texture, -500, 500); } break; case PARAM_LINEAR_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_RADIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_TANGENTIAL_ACCEL: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, tex_rid); _adjust_curve_range(p_texture, -200, 200); } break; case PARAM_DAMPING: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, tex_rid); _adjust_curve_range(p_texture, 0, 100); } break; case PARAM_ANGLE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, tex_rid); _adjust_curve_range(p_texture, -360, 360); } break; case PARAM_SCALE: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, tex_rid); _adjust_curve_range(p_texture, 0, 1); } break; case PARAM_HUE_VARIATION: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, tex_rid); _adjust_curve_range(p_texture, -1, 1); } break; case PARAM_ANIM_SPEED: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, tex_rid); _adjust_curve_range(p_texture, 0, 200); } break; case PARAM_ANIM_OFFSET: { - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, tex_rid); } break; case PARAM_MAX: break; // Can't happen, but silences warning @@ -923,7 +925,8 @@ Color ParticlesMaterial::get_color() const { void ParticlesMaterial::set_color_ramp(const Ref<Texture2D> &p_texture) { color_ramp = p_texture; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, p_texture); + RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, tex_rid); _queue_shader_change(); notify_property_list_changed(); } @@ -965,17 +968,20 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { void ParticlesMaterial::set_emission_point_texture(const Ref<Texture2D> &p_points) { emission_point_texture = p_points; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points); + RID tex_rid = p_points.is_valid() ? p_points->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, tex_rid); } void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture2D> &p_normals) { emission_normal_texture = p_normals; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals); + RID tex_rid = p_normals.is_valid() ? p_normals->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, tex_rid); } void ParticlesMaterial::set_emission_color_texture(const Ref<Texture2D> &p_colors) { emission_color_texture = p_colors; - RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors); + RID tex_rid = p_colors.is_valid() ? p_colors->get_rid() : RID(); + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, tex_rid); _queue_shader_change(); } @@ -1040,40 +1046,36 @@ RID ParticlesMaterial::get_shader_rid() const { } void ParticlesMaterial::_validate_property(PropertyInfo &property) const { - if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; - } - if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "sub_emitter_amount_at_end" && sub_emitter_mode != SUB_EMITTER_AT_END) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -1292,7 +1294,7 @@ void ParticlesMaterial::_bind_methods() { 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::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"), "set_param", "get_param", PARAM_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::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_GROUP("Scale", ""); @@ -1316,7 +1318,7 @@ void ParticlesMaterial::_bind_methods() { 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_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,At End,At Collision"), "set_sub_emitter_mode", "get_sub_emitter_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity"); @@ -1391,7 +1393,7 @@ ParticlesMaterial::ParticlesMaterial() : set_sub_emitter_keep_velocity(false); set_attractor_interaction_enabled(true); - set_collision_enabled(true); + set_collision_enabled(false); set_collision_bounce(0.0); set_collision_friction(0.0); set_collision_use_scale(false); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 64b43f82c6..dfa45cc810 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -207,7 +207,7 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); } @@ -714,7 +714,7 @@ int BoxMesh::get_subdivide_depth() const { BoxMesh::BoxMesh() {} /** - CylinderMesh + CylinderMesh */ void CylinderMesh::_create_mesh_array(Array &p_arr) const { @@ -866,9 +866,9 @@ void CylinderMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CylinderMesh::set_rings); ClassDB::bind_method(D_METHOD("get_rings"), &CylinderMesh::get_rings); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_top_radius", "get_top_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius"); - 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::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_top_radius", "get_top_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_bottom_radius", "get_bottom_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,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"); } @@ -955,7 +955,7 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { u /= (subdivide_w + 1.0); v /= (subdivide_d + 1.0); - points.push_back(Vector3(-x, 0.0, -z)); + points.push_back(Vector3(-x, 0.0, -z) + center_offset); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(1.0 - u, 1.0 - v)); /* 1.0 - uv to match orientation with Quad */ @@ -993,10 +993,13 @@ void PlaneMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_width"), &PlaneMesh::get_subdivide_width); ClassDB::bind_method(D_METHOD("set_subdivide_depth", "subdivide"), &PlaneMesh::set_subdivide_depth); ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PlaneMesh::get_subdivide_depth); + ClassDB::bind_method(D_METHOD("set_center_offset", "offset"), &PlaneMesh::set_center_offset); + ClassDB::bind_method(D_METHOD("get_center_offset"), &PlaneMesh::get_center_offset); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } void PlaneMesh::set_size(const Size2 &p_size) { @@ -1026,6 +1029,15 @@ int PlaneMesh::get_subdivide_depth() const { return subdivide_d; } +void PlaneMesh::set_center_offset(const Vector3 p_offset) { + center_offset = p_offset; + _request_update(); +} + +Vector3 PlaneMesh::get_center_offset() const { + return center_offset; +} + PlaneMesh::PlaneMesh() {} /** @@ -1326,10 +1338,10 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); Vector3 quad_faces[4] = { - Vector3(-_size.x, -_size.y, 0), - Vector3(-_size.x, _size.y, 0), - Vector3(_size.x, _size.y, 0), - Vector3(_size.x, -_size.y, 0), + Vector3(-_size.x, -_size.y, 0) + center_offset, + Vector3(-_size.x, _size.y, 0) + center_offset, + Vector3(_size.x, _size.y, 0) + center_offset, + Vector3(_size.x, -_size.y, 0) + center_offset, }; static const int indices[6] = { @@ -1365,7 +1377,11 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { void QuadMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size); ClassDB::bind_method(D_METHOD("get_size"), &QuadMesh::get_size); + ClassDB::bind_method(D_METHOD("set_center_offset", "center_offset"), &QuadMesh::set_center_offset); + ClassDB::bind_method(D_METHOD("get_center_offset"), &QuadMesh::get_center_offset); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } QuadMesh::QuadMesh() { @@ -1381,6 +1397,15 @@ Size2 QuadMesh::get_size() const { return size; } +void QuadMesh::set_center_offset(Vector3 p_center_offset) { + center_offset = p_center_offset; + _request_update(); +} + +Vector3 QuadMesh::get_center_offset() const { + return center_offset; +} + /** SphereMesh */ @@ -1607,10 +1632,10 @@ int TubeTrailMesh::get_builtin_bind_pose_count() const { return sections + 1; } -Transform TubeTrailMesh::get_builtin_bind_pose(int p_index) const { +Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const { float depth = section_length * sections; - Transform xform; + Transform3D xform; xform.origin.y = depth / 2.0 - section_length * float(p_index); xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y @@ -1931,10 +1956,10 @@ int RibbonTrailMesh::get_builtin_bind_pose_count() const { return sections + 1; } -Transform RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { +Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { float depth = section_length * sections; - Transform xform; + Transform3D xform; xform.origin.y = depth / 2.0 - section_length * float(p_index); xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index ec5806489e..a3de34d3e3 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -211,6 +211,7 @@ private: Size2 size = Size2(2.0, 2.0); int subdivide_w = 0; int subdivide_d = 0; + Vector3 center_offset; protected: static void _bind_methods(); @@ -226,6 +227,9 @@ public: void set_subdivide_depth(const int p_divisions); int get_subdivide_depth() const; + void set_center_offset(const Vector3 p_offset); + Vector3 get_center_offset() const; + PlaneMesh(); }; @@ -274,6 +278,7 @@ class QuadMesh : public PrimitiveMesh { private: Size2 size = Size2(1.0, 1.0); + Vector3 center_offset; protected: static void _bind_methods(); @@ -284,6 +289,9 @@ public: void set_size(const Size2 &p_size); Size2 get_size() const; + + void set_center_offset(const Vector3 p_offset); + Vector3 get_center_offset() const; }; /** @@ -374,7 +382,7 @@ public: Ref<Curve> get_curve() const; virtual int get_builtin_bind_pose_count() const override; - virtual Transform get_builtin_bind_pose(int p_index) const override; + virtual Transform3D get_builtin_bind_pose(int p_index) const override; TubeTrailMesh(); }; @@ -424,7 +432,7 @@ public: Ref<Curve> get_curve() const; virtual int get_builtin_bind_pose_count() const override; - virtual Transform get_builtin_bind_pose(int p_index) const override; + virtual Transform3D get_builtin_bind_pose(int p_index) const override; RibbonTrailMesh(); }; diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index dc4c6dc2d7..17ce0b34ac 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -37,6 +37,26 @@ void RectangleShape2D::_update_shape() { emit_changed(); } +#ifndef DISABLE_DEPRECATED +bool RectangleShape2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "extents") { // Compatibility with Godot 3.x. + // Convert to `size`, twice as big. + set_size((Vector2)p_value * 2); + return true; + } + return false; +} + +bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const { + if (p_name == "extents") { // Compatibility with Godot 3.x. + // Convert to `extents`, half as big. + r_property = size / 2; + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + void RectangleShape2D::set_size(const Vector2 &p_size) { size = p_size; _update_shape(); diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h index 8d747c86af..f1e8be4c5b 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -41,6 +41,10 @@ class RectangleShape2D : public Shape2D { protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_property) const; +#endif // DISABLE_DEPRECATED public: void set_size(const Vector2 &p_size); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 2414704a57..ee61e64ed3 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -31,14 +31,14 @@ #include "resource_format_text.h" #include "core/config/project_settings.h" +#include "core/io/dir_access.h" #include "core/io/resource_format_binary.h" -#include "core/os/dir_access.h" #include "core/version.h" //version 2: changed names for basis, aabb, Vectors, etc. #define FORMAT_VERSION 2 -#include "core/os/dir_access.h" +#include "core/io/dir_access.h" #include "core/version.h" #define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data()); @@ -65,7 +65,7 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia if (!p_data->resource_map.has(index)) { Ref<DummyResource> dr; - dr.instance(); + dr.instantiate(); dr->set_subindex(index); p_data->resource_map[index] = dr; p_data->resource_set.insert(dr); @@ -183,7 +183,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) { Ref<PackedScene> packed_scene; - packed_scene.instance(); + packed_scene.instantiate(); while (true) { if (next_tag.name == "node") { @@ -208,7 +208,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("type")) { type = packed_scene->get_state()->add_name(next_tag.fields["type"]); } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced + type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated } if (next_tag.fields.has("instance")) { @@ -522,7 +522,7 @@ Error ResourceLoaderText::load() { } else { //create - Object *obj = ClassDB::instance(type); + Object *obj = ClassDB::instantiate(type); if (!obj) { error_text += "Can't create sub resource of type: " + type; _printerr(); @@ -575,6 +575,7 @@ 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_subindex(id); } if (progress && resources_total > 0) { @@ -603,7 +604,7 @@ Error ResourceLoaderText::load() { } if (!resource.is_valid()) { - Object *obj = ClassDB::instance(res_type); + Object *obj = ClassDB::instantiate(res_type); if (!obj) { error_text += "Can't create sub resource of type: " + res_type; _printerr(); @@ -1021,7 +1022,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) int lindex = dummy_read.external_resources.size(); Ref<DummyResource> dr; - dr.instance(); + dr.instantiate(); dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external dummy_read.external_resources[dr] = lindex; dummy_read.rev_external_resources[index] = dr; diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 2dc683415d..f5d9cca859 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -31,9 +31,9 @@ #ifndef RESOURCE_FORMAT_TEXT_H #define RESOURCE_FORMAT_TEXT_H +#include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/os/file_access.h" #include "core/variant/variant_parser.h" #include "scene/resources/packed_scene.h" diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 77c6199794..f19d08dbb1 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -30,7 +30,7 @@ #include "shader.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "scene/scene_string_names.h" #include "servers/rendering/shader_language.h" #include "servers/rendering_server.h" @@ -167,7 +167,7 @@ RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_origi } Ref<Shader> shader; - shader.instance(); + shader.instantiate(); Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); @@ -184,7 +184,7 @@ RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_origi } void ResourceFormatLoaderShader::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("shader"); + p_extensions->push_back("gdshader"); } bool ResourceFormatLoaderShader::handles_type(const String &p_type) const { @@ -193,7 +193,7 @@ bool ResourceFormatLoaderShader::handles_type(const String &p_type) const { String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const { String el = p_path.get_extension().to_lower(); - if (el == "shader") { + if (el == "gdshader") { return "Shader"; } return ""; @@ -224,7 +224,7 @@ Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resourc void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) { if (shader->is_text_shader()) { - p_extensions->push_back("shader"); + p_extensions->push_back("gdshader"); } } } diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index cb44e059a3..a02a0e5488 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -35,7 +35,7 @@ #include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" -void Shape3D::add_vertices_to_array(Vector<Vector3> &array, const Transform &p_xform) { +void Shape3D::add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p_xform) { Vector<Vector3> toadd = get_debug_mesh_lines(); if (toadd.size()) { diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h index 0644940fd4..b8e529cd3c 100644 --- a/scene/resources/shape_3d.h +++ b/scene/resources/shape_3d.h @@ -60,7 +60,7 @@ public: /// Returns the radius of a sphere that fully enclose this shape virtual real_t get_enclosing_radius() const = 0; - void add_vertices_to_array(Vector<Vector3> &array, const Transform &p_xform); + void add_vertices_to_array(Vector<Vector3> &array, const Transform3D &p_xform); real_t get_margin() const; void set_margin(real_t p_margin); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp new file mode 100644 index 0000000000..b52a60006a --- /dev/null +++ b/scene/resources/skeleton_modification_2d.cpp @@ -0,0 +1,253 @@ +/*************************************************************************/ +/* skeleton_modification_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 "skeleton_modification_2d.h" +#include "scene/2d/skeleton_2d.h" + +#include "scene/2d/collision_object_2d.h" +#include "scene/2d/collision_shape_2d.h" +#include "scene/2d/physical_bone_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +/////////////////////////////////////// +// Modification2D +/////////////////////////////////////// + +void SkeletonModification2D::_execute(float p_delta) { + call("_execute", p_delta); + + if (!enabled) { + return; + } +} + +void SkeletonModification2D::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + if (stack) { + is_setup = true; + } else { + WARN_PRINT("Could not setup modification with name " + get_name()); + } + + call("_setup_modification", p_stack); +} + +void SkeletonModification2D::_draw_editor_gizmo() { + call("_draw_editor_gizmo"); +} + +void SkeletonModification2D::set_enabled(bool p_enabled) { + enabled = p_enabled; + +#ifdef TOOLS_ENABLED + if (editor_draw_gizmo) { + if (stack) { + stack->set_editor_gizmos_dirty(true); + } + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2D::get_enabled() { + return enabled; +} + +float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, float 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) { + float 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; +} + +void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_bone, float p_min_bound, float p_max_bound, + bool p_constraint_enabled, bool p_constraint_in_localspace, bool p_constraint_inverted) { + if (!p_operation_bone) { + return; + } + + Color bone_ik_color = Color(1.0, 0.65, 0.0, 0.4); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + } +#endif // TOOLS_ENABLED + + float arc_angle_min = p_min_bound; + float arc_angle_max = p_max_bound; + if (arc_angle_min < 0) { + arc_angle_min = (Math_PI * 2) + arc_angle_min; + } + if (arc_angle_max < 0) { + arc_angle_max = (Math_PI * 2) + arc_angle_max; + } + if (arc_angle_min > arc_angle_max) { + float tmp = arc_angle_min; + arc_angle_min = arc_angle_max; + arc_angle_max = tmp; + } + arc_angle_min += p_operation_bone->get_bone_angle(); + arc_angle_max += p_operation_bone->get_bone_angle(); + + if (p_constraint_enabled) { + if (p_constraint_in_localspace) { + Node *operation_bone_parent = p_operation_bone->get_parent(); + Bone2D *operation_bone_parent_bone = Object::cast_to<Bone2D>(operation_bone_parent); + + if (operation_bone_parent_bone) { + stack->skeleton->draw_set_transform( + stack->skeleton->get_global_transform().affine_inverse().xform(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())); + } + } else { + stack->skeleton->draw_set_transform(stack->skeleton->get_global_transform().affine_inverse().xform(p_operation_bone->get_global_position())); + } + + if (p_constraint_inverted) { + stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), + arc_angle_min + (Math_PI * 2), arc_angle_max, 32, bone_ik_color, 1.0); + } else { + stack->skeleton->draw_arc(Vector2(0, 0), p_operation_bone->get_length(), + arc_angle_min, arc_angle_max, 32, bone_ik_color, 1.0); + } + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(arc_angle_min), Math::sin(arc_angle_min)) * p_operation_bone->get_length(), bone_ik_color, 1.0); + 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_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); + } +} + +Ref<SkeletonModificationStack2D> SkeletonModification2D::get_modification_stack() { + return stack; +} + +void SkeletonModification2D::set_is_setup(bool p_setup) { + is_setup = p_setup; +} + +bool SkeletonModification2D::get_is_setup() const { + return is_setup; +} + +void SkeletonModification2D::set_execution_mode(int p_mode) { + execution_mode = p_mode; +} + +int SkeletonModification2D::get_execution_mode() const { + return execution_mode; +} + +void SkeletonModification2D::set_editor_draw_gizmo(bool p_draw_gizmo) { + editor_draw_gizmo = p_draw_gizmo; +#ifdef TOOLS_ENABLED + if (is_setup) { + if (stack) { + stack->set_editor_gizmos_dirty(true); + } + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2D::get_editor_draw_gizmo() const { + return editor_draw_gizmo; +} + +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")); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModification2D::set_enabled); + ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModification2D::get_enabled); + ClassDB::bind_method(D_METHOD("get_modification_stack"), &SkeletonModification2D::get_modification_stack); + ClassDB::bind_method(D_METHOD("set_is_setup", "is_setup"), &SkeletonModification2D::set_is_setup); + ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModification2D::get_is_setup); + ClassDB::bind_method(D_METHOD("set_execution_mode", "execution_mode"), &SkeletonModification2D::set_execution_mode); + ClassDB::bind_method(D_METHOD("get_execution_mode"), &SkeletonModification2D::get_execution_mode); + ClassDB::bind_method(D_METHOD("clamp_angle", "angle", "min", "max", "invert"), &SkeletonModification2D::clamp_angle); + ClassDB::bind_method(D_METHOD("set_editor_draw_gizmo", "draw_gizmo"), &SkeletonModification2D::set_editor_draw_gizmo); + ClassDB::bind_method(D_METHOD("get_editor_draw_gizmo"), &SkeletonModification2D::get_editor_draw_gizmo); + + 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"); +} + +SkeletonModification2D::SkeletonModification2D() { + stack = nullptr; + is_setup = false; +} diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h new file mode 100644 index 0000000000..18633e55cb --- /dev/null +++ b/scene/resources/skeleton_modification_2d.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* skeleton_modification_2d.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 SKELETONMODIFICATION2D_H +#define SKELETONMODIFICATION2D_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_stack_2d.h" + +/////////////////////////////////////// +// SkeletonModification2D +/////////////////////////////////////// + +class SkeletonModificationStack2D; +class Bone2D; + +class SkeletonModification2D : public Resource { + GDCLASS(SkeletonModification2D, Resource); + friend class Skeleton2D; + friend class Bone2D; + +protected: + static void _bind_methods(); + + SkeletonModificationStack2D *stack; + int execution_mode = 0; // 0 = process + + bool enabled = true; + bool is_setup = false; + + bool _print_execution_error(bool p_condition, String p_message); + +public: + virtual void _execute(float _delta); + virtual void _setup_modification(SkeletonModificationStack2D *p_stack); + virtual void _draw_editor_gizmo(); + + bool editor_draw_gizmo = false; + void set_editor_draw_gizmo(bool p_draw_gizmo); + bool get_editor_draw_gizmo() const; + + void set_enabled(bool p_enabled); + bool get_enabled(); + + Ref<SkeletonModificationStack2D> get_modification_stack(); + void set_is_setup(bool p_setup); + bool get_is_setup() const; + + void set_execution_mode(int p_mode); + int get_execution_mode() const; + + float clamp_angle(float p_angle, float p_min_bound, float p_max_bound, bool p_invert_clamp = false); + void editor_draw_angle_constraints(Bone2D *p_operation_bone, float p_min_bound, float p_max_bound, bool p_constraint_enabled, bool p_constraint_in_localspace, bool p_constraint_inverted); + + SkeletonModification2D(); +}; + +#endif // SKELETONMODIFICATION2D_H diff --git a/scene/resources/skeleton_modification_2d_ccdik.cpp b/scene/resources/skeleton_modification_2d_ccdik.cpp new file mode 100644 index 0000000000..7ea60e584e --- /dev/null +++ b/scene/resources/skeleton_modification_2d_ccdik.cpp @@ -0,0 +1,545 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_ccdik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +bool SkeletonModification2DCCDIK::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false); + + if (what == "bone2d_node") { + set_ccdik_joint_bone2d_node(which, p_value); + } else if (what == "bone_index") { + set_ccdik_joint_bone_index(which, p_value); + } else if (what == "rotate_from_joint") { + set_ccdik_joint_rotate_from_joint(which, p_value); + } else if (what == "enable_constraint") { + set_ccdik_joint_enable_constraint(which, p_value); + } else if (what == "constraint_angle_min") { + set_ccdik_joint_constraint_angle_min(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_max") { + set_ccdik_joint_constraint_angle_max(which, Math::deg2rad(float(p_value))); + } else if (what == "constraint_angle_invert") { + set_ccdik_joint_constraint_angle_invert(which, p_value); + } else if (what == "constraint_in_localspace") { + set_ccdik_joint_constraint_in_localspace(which, p_value); + } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + set_ccdik_joint_editor_draw_gizmo(which, p_value); + } +#endif // TOOLS_ENABLED + + return true; + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool SkeletonModification2DCCDIK::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, ccdik_data_chain.size(), false); + + if (what == "bone2d_node") { + r_ret = get_ccdik_joint_bone2d_node(which); + } else if (what == "bone_index") { + r_ret = get_ccdik_joint_bone_index(which); + } else if (what == "rotate_from_joint") { + r_ret = get_ccdik_joint_rotate_from_joint(which); + } else if (what == "enable_constraint") { + r_ret = get_ccdik_joint_enable_constraint(which); + } else if (what == "constraint_angle_min") { + r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_min(which)); + } else if (what == "constraint_angle_max") { + r_ret = Math::rad2deg(get_ccdik_joint_constraint_angle_max(which)); + } else if (what == "constraint_angle_invert") { + r_ret = get_ccdik_joint_constraint_angle_invert(which); + } else if (what == "constraint_in_localspace") { + r_ret = get_ccdik_joint_constraint_in_localspace(which); + } + +#ifdef TOOLS_ENABLED + if (what.begins_with("editor_draw_gizmo")) { + r_ret = get_ccdik_joint_editor_draw_gizmo(which); + } +#endif // TOOLS_ENABLED + + return true; + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DCCDIK::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < ccdik_data_chain.size(); i++) { + String base_string = "joint_data/" + itos(i) + "/"; + + p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "rotate_from_joint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (ccdik_data_chain[i].enable_constraint) { + p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, base_string + "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "editor_draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DCCDIK::_execute(float 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()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + if (tip_node_cache.is_null()) { + WARN_PRINT_ONCE("Tip cache is out of date. Attempting to update..."); + update_tip_cache(); + return; + } + + Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + + Node2D *tip = Object::cast_to<Node2D>(ObjectDB::get_instance(tip_node_cache)); + if (!tip || !tip->is_inside_tree()) { + ERR_PRINT_ONCE("Tip node is not in the scene tree. Cannot execute modification!"); + return; + } + + for (int i = 0; i < ccdik_data_chain.size(); i++) { + _execute_ccdik_joint(i, target, tip); + } +} + +void SkeletonModification2DCCDIK::_execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip) { + CCDIK_Joint_Data2D ccdik_data = ccdik_data_chain[p_joint_idx]; + if (ccdik_data.bone_idx < 0 || ccdik_data.bone_idx > stack->skeleton->get_bone_count()) { + ERR_PRINT_ONCE("2D CCDIK joint: bone index not found!"); + return; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data.bone_idx); + Transform2D operation_transform = operation_bone->get_global_transform(); + + 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()); + } 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()); + 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()); + + // Apply constraints in globalspace: + if (ccdik_data.enable_constraint && !ccdik_data.constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert)); + } + + // Convert from a global transform to a delta and then apply the delta to the local transform. + operation_bone->set_global_transform(operation_transform); + operation_transform = operation_bone->get_transform(); + + // Apply constraints in localspace: + if (ccdik_data.enable_constraint && ccdik_data.constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), ccdik_data.constraint_angle_min, ccdik_data.constraint_angle_max, ccdik_data.constraint_angle_invert)); + } + + // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. + stack->skeleton->set_bone_local_pose_override(ccdik_data.bone_idx, operation_transform, stack->strength, true); + operation_bone->set_transform(operation_transform); + operation_bone->notification(operation_bone->NOTIFICATION_TRANSFORM_CHANGED); +} + +void SkeletonModification2DCCDIK::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack != nullptr) { + is_setup = true; + update_target_cache(); + update_tip_cache(); + } +} + +void SkeletonModification2DCCDIK::_draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + for (int i = 0; i < ccdik_data_chain.size(); i++) { + if (!ccdik_data_chain[i].editor_draw_gizmo) { + continue; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(ccdik_data_chain[i].bone_idx); + editor_draw_angle_constraints(operation_bone, ccdik_data_chain[i].constraint_angle_min, ccdik_data_chain[i].constraint_angle_max, + ccdik_data_chain[i].enable_constraint, ccdik_data_chain[i].constraint_in_localspace, ccdik_data_chain[i].constraint_angle_invert); + } +} + +void SkeletonModification2DCCDIK::update_target_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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(); + } + } + } +} + +void SkeletonModification2DCCDIK::update_tip_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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 the scene tree!"); + tip_node_cache = node->get_instance_id(); + } + } + } +} + +void SkeletonModification2DCCDIK::ccdik_joint_update_bone2d_cache(int p_joint_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update CCDIK Bone2D cache: modification is not properly setup!"); + return; + } + + ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(ccdik_data_chain[p_joint_idx].bone2d_node)) { + Node *node = stack->skeleton->get_node(ccdik_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in the scene tree!"); + ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + ccdik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("CCDIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +void SkeletonModification2DCCDIK::set_target_node(const NodePath &p_target_node) { + target_node = p_target_node; + update_target_cache(); +} + +NodePath SkeletonModification2DCCDIK::get_target_node() const { + return target_node; +} + +void SkeletonModification2DCCDIK::set_tip_node(const NodePath &p_tip_node) { + tip_node = p_tip_node; + update_tip_cache(); +} + +NodePath SkeletonModification2DCCDIK::get_tip_node() const { + return tip_node; +} + +void SkeletonModification2DCCDIK::set_ccdik_data_chain_length(int p_length) { + ccdik_data_chain.resize(p_length); + notify_property_list_changed(); +} + +int SkeletonModification2DCCDIK::get_ccdik_data_chain_length() { + return ccdik_data_chain.size(); +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; + ccdik_joint_update_bone2d_cache(p_joint_idx); + + notify_property_list_changed(); +} + +NodePath SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), NodePath(), "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].bone2d_node; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCCDIK joint out of range!"); + ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + ccdik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + ccdik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..."); + ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the CCDIK joint " + itos(p_joint_idx) + " bone index for this modification..."); + ccdik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +int SkeletonModification2DCCDIK::get_ccdik_joint_bone_index(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), -1, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].bone_idx; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].rotate_from_joint = p_rotate_from_joint; +} + +bool SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].rotate_from_joint; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].enable_constraint = p_constraint; + notify_property_list_changed(); + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].enable_constraint; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].constraint_angle_min = p_angle_min; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].constraint_angle_min; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].constraint_angle_max = p_angle_max; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +float SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), 0.0, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].constraint_angle_max; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].constraint_angle_invert = p_invert; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].constraint_angle_invert; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].constraint_in_localspace = p_constraint_in_localspace; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DCCDIK::get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].constraint_in_localspace; +} + +void SkeletonModification2DCCDIK::set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo) { + ERR_FAIL_INDEX_MSG(p_joint_idx, ccdik_data_chain.size(), "CCDIK joint out of range!"); + ccdik_data_chain.write[p_joint_idx].editor_draw_gizmo = p_draw_gizmo; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DCCDIK::get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, ccdik_data_chain.size(), false, "CCDIK joint out of range!"); + return ccdik_data_chain[p_joint_idx].editor_draw_gizmo; +} + +void SkeletonModification2DCCDIK::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DCCDIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DCCDIK::get_target_node); + ClassDB::bind_method(D_METHOD("set_tip_node", "tip_nodepath"), &SkeletonModification2DCCDIK::set_tip_node); + ClassDB::bind_method(D_METHOD("get_tip_node"), &SkeletonModification2DCCDIK::get_tip_node); + + ClassDB::bind_method(D_METHOD("set_ccdik_data_chain_length", "length"), &SkeletonModification2DCCDIK::set_ccdik_data_chain_length); + ClassDB::bind_method(D_METHOD("get_ccdik_data_chain_length"), &SkeletonModification2DCCDIK::get_ccdik_data_chain_length); + + ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DCCDIK::set_ccdik_joint_bone_index); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_bone_index", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_bone_index); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_rotate_from_joint", "joint_idx", "rotate_from_joint"), &SkeletonModification2DCCDIK::set_ccdik_joint_rotate_from_joint); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_rotate_from_joint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_rotate_from_joint); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_enable_constraint", "joint_idx", "enable_constraint"), &SkeletonModification2DCCDIK::set_ccdik_joint_enable_constraint); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_enable_constraint", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_enable_constraint); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_min", "joint_idx", "angle_min"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_min); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_min", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_min); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_max", "joint_idx", "angle_max"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_max); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_max", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_max); + ClassDB::bind_method(D_METHOD("set_ccdik_joint_constraint_angle_invert", "joint_idx", "invert"), &SkeletonModification2DCCDIK::set_ccdik_joint_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("get_ccdik_joint_constraint_angle_invert", "joint_idx"), &SkeletonModification2DCCDIK::get_ccdik_joint_constraint_angle_invert); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "tip_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_tip_node", "get_tip_node"); + 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"); +} + +SkeletonModification2DCCDIK::SkeletonModification2DCCDIK() { + stack = nullptr; + is_setup = false; + enabled = true; + editor_draw_gizmo = true; +} + +SkeletonModification2DCCDIK::~SkeletonModification2DCCDIK() { +} diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h new file mode 100644 index 0000000000..dc48291f62 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_ccdik.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DCCDIK_H +#define SKELETONMODIFICATION2DCCDIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DCCDIK +/////////////////////////////////////// + +class SkeletonModification2DCCDIK : public SkeletonModification2D { + GDCLASS(SkeletonModification2DCCDIK, SkeletonModification2D); + +private: + struct CCDIK_Joint_Data2D { + int bone_idx = -1; + NodePath bone2d_node; + ObjectID bone2d_node_cache; + bool rotate_from_joint = false; + + bool enable_constraint = false; + float constraint_angle_min = 0; + float constraint_angle_max = (2.0 * Math_PI); + bool constraint_angle_invert = false; + bool constraint_in_localspace = true; + + bool editor_draw_gizmo = true; + }; + + Vector<CCDIK_Joint_Data2D> ccdik_data_chain; + + NodePath target_node; + ObjectID target_node_cache; + void update_target_cache(); + + NodePath tip_node; + ObjectID tip_node_cache; + void update_tip_cache(); + + void ccdik_joint_update_bone2d_cache(int p_joint_idx); + void _execute_ccdik_joint(int p_joint_idx, Node2D *p_target, Node2D *p_tip); + +protected: + static void _bind_methods(); + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() 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; + + int get_ccdik_data_chain_length(); + void set_ccdik_data_chain_length(int p_new_length); + + void set_ccdik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath get_ccdik_joint_bone2d_node(int p_joint_idx) const; + void set_ccdik_joint_bone_index(int p_joint_idx, int p_bone_idx); + int get_ccdik_joint_bone_index(int p_joint_idx) const; + + void set_ccdik_joint_rotate_from_joint(int p_joint_idx, bool p_rotate_from_joint); + bool get_ccdik_joint_rotate_from_joint(int p_joint_idx) const; + void set_ccdik_joint_enable_constraint(int p_joint_idx, bool p_constraint); + bool get_ccdik_joint_enable_constraint(int p_joint_idx) const; + void set_ccdik_joint_constraint_angle_min(int p_joint_idx, float p_angle_min); + float get_ccdik_joint_constraint_angle_min(int p_joint_idx) const; + void set_ccdik_joint_constraint_angle_max(int p_joint_idx, float p_angle_max); + float get_ccdik_joint_constraint_angle_max(int p_joint_idx) const; + void set_ccdik_joint_constraint_angle_invert(int p_joint_idx, bool p_invert); + bool get_ccdik_joint_constraint_angle_invert(int p_joint_idx) const; + void set_ccdik_joint_constraint_in_localspace(int p_joint_idx, bool p_constraint_in_localspace); + bool get_ccdik_joint_constraint_in_localspace(int p_joint_idx) const; + void set_ccdik_joint_editor_draw_gizmo(int p_joint_idx, bool p_draw_gizmo); + bool get_ccdik_joint_editor_draw_gizmo(int p_joint_idx) const; + + SkeletonModification2DCCDIK(); + ~SkeletonModification2DCCDIK(); +}; + +#endif // SKELETONMODIFICATION2DCCDIK_H diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp new file mode 100644 index 0000000000..aef852f7e4 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -0,0 +1,444 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_fabrik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +bool SkeletonModification2DFABRIK::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); + + if (what == "bone2d_node") { + set_fabrik_joint_bone2d_node(which, p_value); + } else if (what == "bone_index") { + set_fabrik_joint_bone_index(which, p_value); + } else if (what == "magnet_position") { + set_fabrik_joint_magnet_position(which, p_value); + } else if (what == "use_target_rotation") { + set_fabrik_joint_use_target_rotation(which, p_value); + } + } + + return true; +} + +bool SkeletonModification2DFABRIK::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, fabrik_data_chain.size(), false); + + if (what == "bone2d_node") { + r_ret = get_fabrik_joint_bone2d_node(which); + } else if (what == "bone_index") { + r_ret = get_fabrik_joint_bone_index(which); + } else if (what == "magnet_position") { + r_ret = get_fabrik_joint_magnet_position(which); + } else if (what == "use_target_rotation") { + r_ret = get_fabrik_joint_use_target_rotation(which); + } + return true; + } + return true; +} + +void SkeletonModification2DFABRIK::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < fabrik_data_chain.size(); i++) { + String base_string = "joint_data/" + itos(i) + "/"; + + p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT)); + + if (i > 0) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, base_string + "magnet_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } + if (i == fabrik_data_chain.size() - 1) { + p_list->push_back(PropertyInfo(Variant::BOOL, base_string + "use_target_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } + } +} + +void SkeletonModification2DFABRIK::_execute(float 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()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (fabrik_data_chain.size() <= 1) { + ERR_PRINT_ONCE("FABRIK requires at least two joints to operate! Cannot execute modification!"); + return; + } + + Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + target_global_pose = target->get_global_transform(); + + if (fabrik_data_chain[0].bone2d_node_cache.is_null() && !fabrik_data_chain[0].bone2d_node.is_empty()) { + fabrik_joint_update_bone2d_cache(0); + WARN_PRINT("Bone2D cache for origin joint is out of date. Updating..."); + } + + Bone2D *origin_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[0].bone2d_node_cache)); + if (!origin_bone2d_node || !origin_bone2d_node->is_inside_tree()) { + ERR_PRINT_ONCE("Origin joint's Bone2D node is not in the scene tree. Cannot execute modification!"); + return; + } + + origin_global_pose = origin_bone2d_node->get_global_transform(); + + if (fabrik_transform_chain.size() != fabrik_data_chain.size()) { + fabrik_transform_chain.resize(fabrik_data_chain.size()); + } + + for (int i = 0; i < fabrik_data_chain.size(); i++) { + // Update the transform chain + if (fabrik_data_chain[i].bone2d_node_cache.is_null() && !fabrik_data_chain[i].bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Bone2D cache for joint " + itos(i) + " is out of date.. Attempting to update..."); + fabrik_joint_update_bone2d_cache(i); + } + Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (!joint_bone2d_node) { + ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set! Cannot execute modification!"); + 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(); + 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()); + chain_iterations = 0; + + while (target_distance > chain_tolarance) { + chain_backwards(); + chain_forwards(); + + final_bone2d_angle = final_bone2d_node->get_global_transform().get_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()); + + chain_iterations += 1; + if (chain_iterations >= chain_max_iterations) { + break; + } + } + + // Apply all of the saved transforms to the Bone2D nodes + for (int i = 0; i < fabrik_data_chain.size(); i++) { + Bone2D *joint_bone2d_node = Object::cast_to<Bone2D>(ObjectDB::get_instance(fabrik_data_chain[i].bone2d_node_cache)); + if (!joint_bone2d_node) { + ERR_PRINT_ONCE("FABRIK Joint " + itos(i) + " does not have a Bone2D node set!"); + continue; + } + Transform2D chain_trans = fabrik_transform_chain[i]; + + // Apply rotation + if (i + 1 < fabrik_data_chain.size()) { + chain_trans = chain_trans.looking_at(fabrik_transform_chain[i + 1].get_origin()); + } else { + if (fabrik_data_chain[i].use_target_rotation) { + chain_trans.set_rotation(target_global_pose.get_rotation()); + } else { + chain_trans = chain_trans.looking_at(target_global_pose.get_origin()); + } + } + // Adjust for the bone angle + 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()); + + // Apply to the bone, and to the override + joint_bone2d_node->set_global_transform(chain_trans); + stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, joint_bone2d_node->get_transform(), stack->strength, true); + } +} + +void SkeletonModification2DFABRIK::chain_backwards() { + int final_joint_index = fabrik_data_chain.size() - 1; + 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]; + + // Set the rotation of the tip bone + final_bone2d_trans = final_bone2d_trans.looking_at(target_global_pose.get_origin()); + + // Set the position of the tip bone + float final_bone2d_angle = final_bone2d_trans.get_rotation(); + if (fabrik_data_chain[final_joint_index].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); + final_bone2d_trans.set_origin(target_global_pose.get_origin() - (final_bone2d_direction * final_bone2d_length)); + + // Save the transform + fabrik_transform_chain.write[final_joint_index] = final_bone2d_trans; + + int i = final_joint_index; + while (i >= 1) { + Transform2D previous_pose = fabrik_transform_chain[i]; + i -= 1; + 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]; + + 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); + current_pose.set_origin(finish_position); + + // Save the transform + fabrik_transform_chain.write[i] = current_pose; + } +} + +void SkeletonModification2DFABRIK::chain_forwards() { + Transform2D origin_bone2d_trans = fabrik_transform_chain[0]; + origin_bone2d_trans.set_origin(origin_global_pose.get_origin()); + // Save the position + fabrik_transform_chain.write[0] = origin_bone2d_trans; + + for (int i = 0; i < fabrik_data_chain.size() - 1; i++) { + 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]; + Transform2D next_pose = fabrik_transform_chain[i + 1]; + + 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 / (current_pose.get_origin() - next_pose.get_origin()).length(); + Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); + current_pose.set_origin(finish_position); + + // Apply to the bone + fabrik_transform_chain.write[i + 1] = current_pose; + } +} + +void SkeletonModification2DFABRIK::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack != nullptr) { + is_setup = true; + update_target_cache(); + } +} + +void SkeletonModification2DFABRIK::update_target_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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(); + } + } + } +} + +void SkeletonModification2DFABRIK::fabrik_joint_update_bone2d_cache(int p_joint_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update FABRIK Bone2D cache: modification is not properly setup!"); + return; + } + + fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(fabrik_data_chain[p_joint_idx].bone2d_node)) { + Node *node = stack->skeleton->get_node(fabrik_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!"); + fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + fabrik_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("FABRIK joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +void SkeletonModification2DFABRIK::set_target_node(const NodePath &p_target_node) { + target_node = p_target_node; + update_target_cache(); +} + +NodePath SkeletonModification2DFABRIK::get_target_node() const { + return target_node; +} + +void SkeletonModification2DFABRIK::set_fabrik_data_chain_length(int p_length) { + fabrik_data_chain.resize(p_length); + notify_property_list_changed(); +} + +int SkeletonModification2DFABRIK::get_fabrik_data_chain_length() { + return fabrik_data_chain.size(); +} + +void SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].bone2d_node = p_target_node; + fabrik_joint_update_bone2d_cache(p_joint_idx); + + notify_property_list_changed(); +} + +NodePath SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), NodePath(), "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].bone2d_node; +} + +void SkeletonModification2DFABRIK::set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + fabrik_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + fabrik_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..."); + fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the FABRIK joint " + itos(p_joint_idx) + " bone index for this modification..."); + fabrik_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +int SkeletonModification2DFABRIK::get_fabrik_joint_bone_index(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), -1, "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].bone_idx; +} + +void SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].magnet_position = p_magnet_position; +} + +Vector2 SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), Vector2(), "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].magnet_position; +} + +void SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation) { + ERR_FAIL_INDEX_MSG(p_joint_idx, fabrik_data_chain.size(), "FABRIK joint out of range!"); + fabrik_data_chain.write[p_joint_idx].use_target_rotation = p_use_target_rotation; +} + +bool SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, fabrik_data_chain.size(), false, "FABRIK joint out of range!"); + return fabrik_data_chain[p_joint_idx].use_target_rotation; +} + +void SkeletonModification2DFABRIK::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DFABRIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DFABRIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_fabrik_data_chain_length", "length"), &SkeletonModification2DFABRIK::set_fabrik_data_chain_length); + ClassDB::bind_method(D_METHOD("get_fabrik_data_chain_length"), &SkeletonModification2DFABRIK::get_fabrik_data_chain_length); + + ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone2d_node", "joint_idx", "bone2d_nodepath"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone2d_node", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DFABRIK::set_fabrik_joint_bone_index); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_bone_index", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_bone_index); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_magnet_position", "joint_idx", "magnet_position"), &SkeletonModification2DFABRIK::set_fabrik_joint_magnet_position); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_magnet_position", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_magnet_position); + ClassDB::bind_method(D_METHOD("set_fabrik_joint_use_target_rotation", "joint_idx", "use_target_rotation"), &SkeletonModification2DFABRIK::set_fabrik_joint_use_target_rotation); + ClassDB::bind_method(D_METHOD("get_fabrik_joint_use_target_rotation", "joint_idx"), &SkeletonModification2DFABRIK::get_fabrik_joint_use_target_rotation); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "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"); +} + +SkeletonModification2DFABRIK::SkeletonModification2DFABRIK() { + stack = nullptr; + is_setup = false; + enabled = true; + editor_draw_gizmo = false; +} + +SkeletonModification2DFABRIK::~SkeletonModification2DFABRIK() { +} diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h new file mode 100644 index 0000000000..79e0106e26 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DFABRIK_H +#define SKELETONMODIFICATION2DFABRIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DFABRIK +/////////////////////////////////////// + +class SkeletonModification2DFABRIK : public SkeletonModification2D { + GDCLASS(SkeletonModification2DFABRIK, SkeletonModification2D); + +private: + struct FABRIK_Joint_Data2D { + int bone_idx = -1; + NodePath bone2d_node; + ObjectID bone2d_node_cache; + + Vector2 magnet_position = Vector2(0, 0); + bool use_target_rotation = false; + + bool editor_draw_gizmo = true; + }; + + Vector<FABRIK_Joint_Data2D> fabrik_data_chain; + + // Unlike in 3D, we need a vector of Transform2D objects to perform FABRIK. + // This is because FABRIK (unlike CCDIK) needs to operate on transforms that are NOT + // affected by each other, making the transforms stored in Bone2D unusable, as well as those in Skeleton2D. + // For this reason, this modification stores a vector of Transform2Ds used for the calculations, which are then applied at the end. + Vector<Transform2D> fabrik_transform_chain; + + NodePath target_node; + ObjectID target_node_cache; + void update_target_cache(); + + float chain_tolarance = 0.01; + int chain_max_iterations = 10; + int chain_iterations = 0; + Transform2D target_global_pose = Transform2D(); + Transform2D origin_global_pose = Transform2D(); + + void fabrik_joint_update_bone2d_cache(int p_joint_idx); + void chain_backwards(); + void chain_forwards(); + +protected: + static void _bind_methods(); + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *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); + + void set_fabrik_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath get_fabrik_joint_bone2d_node(int p_joint_idx) const; + void set_fabrik_joint_bone_index(int p_joint_idx, int p_bone_idx); + int get_fabrik_joint_bone_index(int p_joint_idx) const; + + void set_fabrik_joint_magnet_position(int p_joint_idx, Vector2 p_magnet_position); + Vector2 get_fabrik_joint_magnet_position(int p_joint_idx) const; + void set_fabrik_joint_use_target_rotation(int p_joint_idx, bool p_use_target_rotation); + bool get_fabrik_joint_use_target_rotation(int p_joint_idx) const; + + SkeletonModification2DFABRIK(); + ~SkeletonModification2DFABRIK(); +}; + +#endif // SKELETONMODIFICATION2DFABRIK_H diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp new file mode 100644 index 0000000000..2547083336 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_jiggle.cpp @@ -0,0 +1,564 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_jiggle.h" +#include "scene/2d/skeleton_2d.h" + +bool SkeletonModification2DJiggle::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false); + + if (what == "bone2d_node") { + set_jiggle_joint_bone2d_node(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); + } + return true; + } else { + if (path == "use_colliders") { + set_use_colliders(p_value); + } else if (path == "collision_mask") { + set_collision_mask(p_value); + } + } + return true; +} + +bool SkeletonModification2DJiggle::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("joint_data/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, jiggle_data_chain.size(), false); + + if (what == "bone2d_node") { + r_ret = get_jiggle_joint_bone2d_node(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); + } + return true; + } else { + if (path == "use_colliders") { + r_ret = get_use_colliders(); + } else if (path == "collision_mask") { + r_ret = get_collision_mask(); + } + } + return true; +} + +void SkeletonModification2DJiggle::_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_2D_PHYSICS, "", PROPERTY_USAGE_DEFAULT)); + } + + for (int i = 0; i < jiggle_data_chain.size(); i++) { + String base_string = "joint_data/" + itos(i) + "/"; + + p_list->push_back(PropertyInfo(Variant::INT, base_string + "bone_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", 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::VECTOR2, base_string + "gravity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } + } + } +} + +void SkeletonModification2DJiggle::_execute(float 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()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + + for (int i = 0; i < jiggle_data_chain.size(); i++) { + _execute_jiggle_joint(i, target, p_delta); + } +} + +void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta) { + // Adopted from: https://wiki.unity3d.com/index.php/JiggleBone + // With modifications by TwistedTwigleg. + + if (jiggle_data_chain[p_joint_idx].bone_idx <= -1 || jiggle_data_chain[p_joint_idx].bone_idx > stack->skeleton->get_bone_count()) { + ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " bone index is invalid. Cannot execute modification on joint..."); + return; + } + + if (jiggle_data_chain[p_joint_idx].bone2d_node_cache.is_null() && !jiggle_data_chain[p_joint_idx].bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Bone2D cache for joint " + itos(p_joint_idx) + " is out of date. Updating..."); + jiggle_joint_update_bone2d_cache(p_joint_idx); + } + + Bone2D *operation_bone = stack->skeleton->get_bone(jiggle_data_chain[p_joint_idx].bone_idx); + if (!operation_bone) { + ERR_PRINT_ONCE("Jiggle joint " + itos(p_joint_idx) + " does not have a Bone2D node or it cannot be found!"); + return; + } + + Transform2D operation_bone_trans = operation_bone->get_global_transform(); + Vector2 target_position = p_target->get_global_transform().get_origin(); + + 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; + + if (jiggle_data_chain[p_joint_idx].use_gravity) { + jiggle_data_chain.write[p_joint_idx].force += jiggle_data_chain[p_joint_idx].gravity * p_delta; + } + + jiggle_data_chain.write[p_joint_idx].acceleration = jiggle_data_chain[p_joint_idx].force / jiggle_data_chain[p_joint_idx].mass; + jiggle_data_chain.write[p_joint_idx].velocity += jiggle_data_chain[p_joint_idx].acceleration * (1 - jiggle_data_chain[p_joint_idx].damping); + + jiggle_data_chain.write[p_joint_idx].dynamic_position += jiggle_data_chain[p_joint_idx].velocity + jiggle_data_chain[p_joint_idx].force; + jiggle_data_chain.write[p_joint_idx].dynamic_position += operation_bone_trans.get_origin() - jiggle_data_chain[p_joint_idx].last_position; + jiggle_data_chain.write[p_joint_idx].last_position = operation_bone_trans.get_origin(); + + // Collision detection/response + if (use_colliders) { + if (execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process) { + Ref<World2D> world_2d = stack->skeleton->get_world_2d(); + ERR_FAIL_COND(world_2d.is_null()); + PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); + PhysicsDirectSpaceState2D::RayResult ray_result; + + // Add exception support? + bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position, + ray_result, Set<RID>(), collision_mask); + + if (ray_hit) { + jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position; + jiggle_data_chain.write[p_joint_idx].acceleration = Vector2(0, 0); + jiggle_data_chain.write[p_joint_idx].velocity = Vector2(0, 0); + } else { + jiggle_data_chain.write[p_joint_idx].last_noncollision_position = jiggle_data_chain[p_joint_idx].dynamic_position; + } + } else { + WARN_PRINT_ONCE("Jiggle 2D modifier: You cannot detect colliders without the stack mode being set to _physics_process!"); + } + } + + // Rotate the bone using the dynamic position! + operation_bone_trans = operation_bone_trans.looking_at(jiggle_data_chain[p_joint_idx].dynamic_position); + 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->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); +} + +void SkeletonModification2DJiggle::_update_jiggle_joint_data() { + for (int 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 SkeletonModification2DJiggle::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack) { + is_setup = true; + + if (stack->skeleton) { + for (int 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()) { + Bone2D *bone2d_node = stack->skeleton->get_bone(bone_idx); + jiggle_data_chain.write[i].dynamic_position = bone2d_node->get_global_transform().get_origin(); + } + } + } + + update_target_cache(); + } +} + +void SkeletonModification2DJiggle::update_target_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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(); + } + } + } +} + +void SkeletonModification2DJiggle::jiggle_joint_update_bone2d_cache(int p_joint_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Cannot update bone2d cache: joint index out of range!"); + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update Jiggle " + itos(p_joint_idx) + " Bone2D cache: modification is not properly setup!"); + return; + } + + jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(jiggle_data_chain[p_joint_idx].bone2d_node)) { + Node *node = stack->skeleton->get_node(jiggle_data_chain[p_joint_idx].bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: node is not in scene tree!"); + jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + jiggle_data_chain.write[p_joint_idx].bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Jiggle joint " + itos(p_joint_idx) + " Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +void SkeletonModification2DJiggle::set_target_node(const NodePath &p_target_node) { + target_node = p_target_node; + update_target_cache(); +} + +NodePath SkeletonModification2DJiggle::get_target_node() const { + return target_node; +} + +void SkeletonModification2DJiggle::set_stiffness(float p_stiffness) { + ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); + stiffness = p_stiffness; + _update_jiggle_joint_data(); +} + +float SkeletonModification2DJiggle::get_stiffness() const { + return stiffness; +} + +void SkeletonModification2DJiggle::set_mass(float p_mass) { + ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); + mass = p_mass; + _update_jiggle_joint_data(); +} + +float SkeletonModification2DJiggle::get_mass() const { + return mass; +} + +void SkeletonModification2DJiggle::set_damping(float 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(); +} + +float SkeletonModification2DJiggle::get_damping() const { + return damping; +} + +void SkeletonModification2DJiggle::set_use_gravity(bool p_use_gravity) { + use_gravity = p_use_gravity; + _update_jiggle_joint_data(); +} + +bool SkeletonModification2DJiggle::get_use_gravity() const { + return use_gravity; +} + +void SkeletonModification2DJiggle::set_gravity(Vector2 p_gravity) { + gravity = p_gravity; + _update_jiggle_joint_data(); +} + +Vector2 SkeletonModification2DJiggle::get_gravity() const { + return gravity; +} + +void SkeletonModification2DJiggle::set_use_colliders(bool p_use_colliders) { + use_colliders = p_use_colliders; + notify_property_list_changed(); +} + +bool SkeletonModification2DJiggle::get_use_colliders() const { + return use_colliders; +} + +void SkeletonModification2DJiggle::set_collision_mask(int p_mask) { + collision_mask = p_mask; +} + +int SkeletonModification2DJiggle::get_collision_mask() const { + return collision_mask; +} + +// Jiggle joint data functions +int SkeletonModification2DJiggle::get_jiggle_data_chain_length() { + return jiggle_data_chain.size(); +} + +void SkeletonModification2DJiggle::set_jiggle_data_chain_length(int p_length) { + ERR_FAIL_COND(p_length < 0); + jiggle_data_chain.resize(p_length); + notify_property_list_changed(); +} + +void SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node(int p_joint_idx, const NodePath &p_target_node) { + ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!"); + jiggle_data_chain.write[p_joint_idx].bone2d_node = p_target_node; + jiggle_joint_update_bone2d_cache(p_joint_idx); + + notify_property_list_changed(); +} + +NodePath SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), NodePath(), "Jiggle joint out of range!"); + return jiggle_data_chain[p_joint_idx].bone2d_node; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, jiggle_data_chain.size(), "Jiggle joint out of range!"); + ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + jiggle_data_chain.write[p_joint_idx].bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + jiggle_data_chain.write[p_joint_idx].bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification..."); + jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the Jiggle joint " + itos(p_joint_idx) + " bone index for this modification..."); + jiggle_data_chain.write[p_joint_idx].bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +int SkeletonModification2DJiggle::get_jiggle_joint_bone_index(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, jiggle_data_chain.size(), -1, "Jiggle joint out of range!"); + return jiggle_data_chain[p_joint_idx].bone_idx; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_override(int p_joint_idx, bool p_override) { + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].override_defaults = p_override; + _update_jiggle_joint_data(); + notify_property_list_changed(); +} + +bool SkeletonModification2DJiggle::get_jiggle_joint_override(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[p_joint_idx].override_defaults; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_stiffness(int p_joint_idx, float p_stiffness) { + ERR_FAIL_COND_MSG(p_stiffness < 0, "Stiffness cannot be set to a negative value!"); + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].stiffness = p_stiffness; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_stiffness(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[p_joint_idx].stiffness; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_mass(int p_joint_idx, float p_mass) { + ERR_FAIL_COND_MSG(p_mass < 0, "Mass cannot be set to a negative value!"); + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].mass = p_mass; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_mass(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[p_joint_idx].mass; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_damping(int p_joint_idx, float p_damping) { + ERR_FAIL_COND_MSG(p_damping < 0, "Damping cannot be set to a negative value!"); + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].damping = p_damping; +} + +float SkeletonModification2DJiggle::get_jiggle_joint_damping(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), -1); + return jiggle_data_chain[p_joint_idx].damping; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_use_gravity(int p_joint_idx, bool p_use_gravity) { + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].use_gravity = p_use_gravity; + notify_property_list_changed(); +} + +bool SkeletonModification2DJiggle::get_jiggle_joint_use_gravity(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), false); + return jiggle_data_chain[p_joint_idx].use_gravity; +} + +void SkeletonModification2DJiggle::set_jiggle_joint_gravity(int p_joint_idx, Vector2 p_gravity) { + ERR_FAIL_INDEX(p_joint_idx, jiggle_data_chain.size()); + jiggle_data_chain.write[p_joint_idx].gravity = p_gravity; +} + +Vector2 SkeletonModification2DJiggle::get_jiggle_joint_gravity(int p_joint_idx) const { + ERR_FAIL_INDEX_V(p_joint_idx, jiggle_data_chain.size(), Vector2(0, 0)); + return jiggle_data_chain[p_joint_idx].gravity; +} + +void SkeletonModification2DJiggle::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DJiggle::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DJiggle::get_target_node); + + ClassDB::bind_method(D_METHOD("set_jiggle_data_chain_length", "length"), &SkeletonModification2DJiggle::set_jiggle_data_chain_length); + ClassDB::bind_method(D_METHOD("get_jiggle_data_chain_length"), &SkeletonModification2DJiggle::get_jiggle_data_chain_length); + + ClassDB::bind_method(D_METHOD("set_stiffness", "stiffness"), &SkeletonModification2DJiggle::set_stiffness); + ClassDB::bind_method(D_METHOD("get_stiffness"), &SkeletonModification2DJiggle::get_stiffness); + ClassDB::bind_method(D_METHOD("set_mass", "mass"), &SkeletonModification2DJiggle::set_mass); + ClassDB::bind_method(D_METHOD("get_mass"), &SkeletonModification2DJiggle::get_mass); + ClassDB::bind_method(D_METHOD("set_damping", "damping"), &SkeletonModification2DJiggle::set_damping); + ClassDB::bind_method(D_METHOD("get_damping"), &SkeletonModification2DJiggle::get_damping); + ClassDB::bind_method(D_METHOD("set_use_gravity", "use_gravity"), &SkeletonModification2DJiggle::set_use_gravity); + ClassDB::bind_method(D_METHOD("get_use_gravity"), &SkeletonModification2DJiggle::get_use_gravity); + ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &SkeletonModification2DJiggle::set_gravity); + ClassDB::bind_method(D_METHOD("get_gravity"), &SkeletonModification2DJiggle::get_gravity); + + ClassDB::bind_method(D_METHOD("set_use_colliders", "use_colliders"), &SkeletonModification2DJiggle::set_use_colliders); + ClassDB::bind_method(D_METHOD("get_use_colliders"), &SkeletonModification2DJiggle::get_use_colliders); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SkeletonModification2DJiggle::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SkeletonModification2DJiggle::get_collision_mask); + + // Jiggle joint data functions + ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone2d_node", "joint_idx", "bone2d_node"), &SkeletonModification2DJiggle::set_jiggle_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone2d_node", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone2d_node); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_bone_index", "joint_idx", "bone_idx"), &SkeletonModification2DJiggle::set_jiggle_joint_bone_index); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_bone_index", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_bone_index); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_override", "joint_idx", "override"), &SkeletonModification2DJiggle::set_jiggle_joint_override); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_override", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_override); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_stiffness", "joint_idx", "stiffness"), &SkeletonModification2DJiggle::set_jiggle_joint_stiffness); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_stiffness", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_stiffness); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_mass", "joint_idx", "mass"), &SkeletonModification2DJiggle::set_jiggle_joint_mass); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_mass", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_mass); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_damping", "joint_idx", "damping"), &SkeletonModification2DJiggle::set_jiggle_joint_damping); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_damping", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_damping); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_use_gravity", "joint_idx", "use_gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_use_gravity); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_use_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_use_gravity); + ClassDB::bind_method(D_METHOD("set_jiggle_joint_gravity", "joint_idx", "gravity"), &SkeletonModification2DJiggle::set_jiggle_joint_gravity); + ClassDB::bind_method(D_METHOD("get_jiggle_joint_gravity", "joint_idx"), &SkeletonModification2DJiggle::get_jiggle_joint_gravity); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "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::VECTOR2, "gravity"), "set_gravity", "get_gravity"); + ADD_GROUP("", ""); +} + +SkeletonModification2DJiggle::SkeletonModification2DJiggle() { + stack = nullptr; + is_setup = false; + jiggle_data_chain = Vector<Jiggle_Joint_Data2D>(); + stiffness = 3; + mass = 0.75; + damping = 0.75; + use_gravity = false; + gravity = Vector2(0, 6.0); + enabled = true; + editor_draw_gizmo = false; // Nothing to really show in a gizmo right now. +} + +SkeletonModification2DJiggle::~SkeletonModification2DJiggle() { +} diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h new file mode 100644 index 0000000000..e24038a1db --- /dev/null +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -0,0 +1,139 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DJIGGLE_H +#define SKELETONMODIFICATION2DJIGGLE_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DJIGGLE +/////////////////////////////////////// + +class SkeletonModification2DJiggle : public SkeletonModification2D { + GDCLASS(SkeletonModification2DJiggle, SkeletonModification2D); + +private: + struct Jiggle_Joint_Data2D { + int bone_idx = -1; + NodePath bone2d_node; + ObjectID bone2d_node_cache; + + bool override_defaults = false; + float stiffness = 3; + float mass = 0.75; + float damping = 0.75; + bool use_gravity = false; + Vector2 gravity = Vector2(0, 6.0); + + Vector2 force = Vector2(0, 0); + Vector2 acceleration = Vector2(0, 0); + Vector2 velocity = Vector2(0, 0); + Vector2 last_position = Vector2(0, 0); + Vector2 dynamic_position = Vector2(0, 0); + + Vector2 last_noncollision_position = Vector2(0, 0); + }; + + Vector<Jiggle_Joint_Data2D> jiggle_data_chain; + + NodePath target_node; + ObjectID target_node_cache; + void update_target_cache(); + + float stiffness = 3; + float mass = 0.75; + float damping = 0.75; + bool use_gravity = false; + Vector2 gravity = Vector2(0, 6); + + bool use_colliders = false; + uint32_t collision_mask = 1; + + void jiggle_joint_update_bone2d_cache(int p_joint_idx); + void _execute_jiggle_joint(int p_joint_idx, Node2D *p_target, float p_delta); + void _update_jiggle_joint_data(); + +protected: + static void _bind_methods(); + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + + void set_target_node(const NodePath &p_target_node); + NodePath get_target_node() const; + + void set_stiffness(float p_stiffness); + float get_stiffness() const; + void set_mass(float p_mass); + float get_mass() const; + void set_damping(float p_damping); + float get_damping() const; + void set_use_gravity(bool p_use_gravity); + bool get_use_gravity() const; + void set_gravity(Vector2 p_gravity); + Vector2 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_bone2d_node(int p_joint_idx, const NodePath &p_target_node); + NodePath get_jiggle_joint_bone2d_node(int p_joint_idx) const; + void set_jiggle_joint_bone_index(int p_joint_idx, int p_bone_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, float p_stiffness); + float get_jiggle_joint_stiffness(int p_joint_idx) const; + void set_jiggle_joint_mass(int p_joint_idx, float p_mass); + float get_jiggle_joint_mass(int p_joint_idx) const; + void set_jiggle_joint_damping(int p_joint_idx, float p_damping); + float 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, Vector2 p_gravity); + Vector2 get_jiggle_joint_gravity(int p_joint_idx) const; + + SkeletonModification2DJiggle(); + ~SkeletonModification2DJiggle(); +}; + +#endif // SKELETONMODIFICATION2DJIGGLE_H diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp new file mode 100644 index 0000000000..fd5c8c7cc2 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_lookat.cpp @@ -0,0 +1,407 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_lookat.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +bool SkeletonModification2DLookAt::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path.begins_with("enable_constraint")) { + set_enable_constraint(p_value); + } else if (path.begins_with("constraint_angle_min")) { + set_constraint_angle_min(Math::deg2rad(float(p_value))); + } else if (path.begins_with("constraint_angle_max")) { + set_constraint_angle_max(Math::deg2rad(float(p_value))); + } else if (path.begins_with("constraint_angle_invert")) { + set_constraint_angle_invert(p_value); + } else if (path.begins_with("constraint_in_localspace")) { + set_constraint_in_localspace(p_value); + } else if (path.begins_with("additional_rotation")) { + set_additional_rotation(Math::deg2rad(float(p_value))); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool SkeletonModification2DLookAt::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("enable_constraint")) { + r_ret = get_enable_constraint(); + } else if (path.begins_with("constraint_angle_min")) { + r_ret = Math::rad2deg(get_constraint_angle_min()); + } else if (path.begins_with("constraint_angle_max")) { + r_ret = Math::rad2deg(get_constraint_angle_max()); + } else if (path.begins_with("constraint_angle_invert")) { + r_ret = get_constraint_angle_invert(); + } else if (path.begins_with("constraint_in_localspace")) { + r_ret = get_constraint_in_localspace(); + } else if (path.begins_with("additional_rotation")) { + r_ret = Math::rad2deg(get_additional_rotation()); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DLookAt::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::BOOL, "enable_constraint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + if (enable_constraint) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_min", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "constraint_angle_max", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_angle_invert", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "constraint_in_localspace", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } + p_list->push_back(PropertyInfo(Variant::FLOAT, "additional_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DLookAt::_execute(float 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()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (bone2d_node_cache.is_null() && !bone2d_node.is_empty()) { + update_bone2d_cache(); + WARN_PRINT_ONCE("Bone2D node cache is out of date. Attempting to update..."); + return; + } + + if (target_node_reference == nullptr) { + target_node_reference = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + } + if (!target_node_reference || !target_node_reference->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + if (bone_idx <= -1) { + ERR_PRINT_ONCE("Bone index is invalid. Cannot execute modification!"); + return; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + if (operation_bone == nullptr) { + ERR_PRINT_ONCE("bone_idx for modification does not point to a valid bone! Cannot execute modification"); + return; + } + + Transform2D operation_transform = operation_bone->get_global_transform(); + Transform2D target_trans = target_node_reference->get_global_transform(); + + // 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()); + + // Account for the direction the bone faces in: + operation_transform.set_rotation(operation_transform.get_rotation() - operation_bone->get_bone_angle()); + + // Apply additional rotation + operation_transform.set_rotation(operation_transform.get_rotation() + additional_rotation); + + // Apply constraints in globalspace: + if (enable_constraint && !constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); + } + + // Convert from a global transform to a local transform via the Bone2D node + operation_bone->set_global_transform(operation_transform); + operation_transform = operation_bone->get_transform(); + + // Apply constraints in localspace: + if (enable_constraint && constraint_in_localspace) { + operation_transform.set_rotation(clamp_angle(operation_transform.get_rotation(), constraint_angle_min, constraint_angle_max, constraint_angle_invert)); + } + + // Set the local pose override, and to make sure child bones are also updated, set the transform of the bone. + stack->skeleton->set_bone_local_pose_override(bone_idx, operation_transform, stack->strength, true); + operation_bone->set_transform(operation_transform); +} + +void SkeletonModification2DLookAt::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack != nullptr) { + is_setup = true; + update_target_cache(); + update_bone2d_cache(); + } +} + +void SkeletonModification2DLookAt::_draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + Bone2D *operation_bone = stack->skeleton->get_bone(bone_idx); + editor_draw_angle_constraints(operation_bone, constraint_angle_min, constraint_angle_max, + enable_constraint, constraint_in_localspace, constraint_angle_invert); +} + +void SkeletonModification2DLookAt::update_bone2d_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update Bone2D cache: modification is not properly setup!"); + return; + } + + bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(bone2d_node)) { + Node *node = stack->skeleton->get_node(bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Bone2D cache: node is not in the scene tree!"); + bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("Error Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + + // Set this to null so we update it + target_node_reference = nullptr; + } + } + } +} + +void SkeletonModification2DLookAt::set_bone2d_node(const NodePath &p_target_node) { + bone2d_node = p_target_node; + update_bone2d_cache(); +} + +NodePath SkeletonModification2DLookAt::get_bone2d_node() const { + return bone2d_node; +} + +int SkeletonModification2DLookAt::get_bone_index() const { + return bone_idx; +} + +void SkeletonModification2DLookAt::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!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + bone_idx = p_bone_idx; + bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("Cannot verify the bone index for this modification..."); + bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("Cannot verify the bone index for this modification..."); + bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +void SkeletonModification2DLookAt::update_target_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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(); + } + } + } +} + +void SkeletonModification2DLookAt::set_target_node(const NodePath &p_target_node) { + target_node = p_target_node; + update_target_cache(); +} + +NodePath SkeletonModification2DLookAt::get_target_node() const { + return target_node; +} + +float SkeletonModification2DLookAt::get_additional_rotation() const { + return additional_rotation; +} + +void SkeletonModification2DLookAt::set_additional_rotation(float p_rotation) { + additional_rotation = p_rotation; +} + +void SkeletonModification2DLookAt::set_enable_constraint(bool p_constraint) { + enable_constraint = p_constraint; + notify_property_list_changed(); +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DLookAt::get_enable_constraint() const { + return enable_constraint; +} + +void SkeletonModification2DLookAt::set_constraint_angle_min(float p_angle_min) { + constraint_angle_min = p_angle_min; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +float SkeletonModification2DLookAt::get_constraint_angle_min() const { + return constraint_angle_min; +} + +void SkeletonModification2DLookAt::set_constraint_angle_max(float p_angle_max) { + constraint_angle_max = p_angle_max; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +float SkeletonModification2DLookAt::get_constraint_angle_max() const { + return constraint_angle_max; +} + +void SkeletonModification2DLookAt::set_constraint_angle_invert(bool p_invert) { + constraint_angle_invert = p_invert; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DLookAt::get_constraint_angle_invert() const { + return constraint_angle_invert; +} + +void SkeletonModification2DLookAt::set_constraint_in_localspace(bool p_constraint_in_localspace) { + constraint_in_localspace = p_constraint_in_localspace; +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DLookAt::get_constraint_in_localspace() const { + return constraint_in_localspace; +} + +void SkeletonModification2DLookAt::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_bone2d_node", "bone2d_nodepath"), &SkeletonModification2DLookAt::set_bone2d_node); + ClassDB::bind_method(D_METHOD("get_bone2d_node"), &SkeletonModification2DLookAt::get_bone2d_node); + ClassDB::bind_method(D_METHOD("set_bone_index", "bone_idx"), &SkeletonModification2DLookAt::set_bone_index); + ClassDB::bind_method(D_METHOD("get_bone_index"), &SkeletonModification2DLookAt::get_bone_index); + + ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DLookAt::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DLookAt::get_target_node); + + ClassDB::bind_method(D_METHOD("set_additional_rotation", "rotation"), &SkeletonModification2DLookAt::set_additional_rotation); + ClassDB::bind_method(D_METHOD("get_additional_rotation"), &SkeletonModification2DLookAt::get_additional_rotation); + + ClassDB::bind_method(D_METHOD("set_enable_constraint", "enable_constraint"), &SkeletonModification2DLookAt::set_enable_constraint); + ClassDB::bind_method(D_METHOD("get_enable_constraint"), &SkeletonModification2DLookAt::get_enable_constraint); + ClassDB::bind_method(D_METHOD("set_constraint_angle_min", "angle_min"), &SkeletonModification2DLookAt::set_constraint_angle_min); + ClassDB::bind_method(D_METHOD("get_constraint_angle_min"), &SkeletonModification2DLookAt::get_constraint_angle_min); + ClassDB::bind_method(D_METHOD("set_constraint_angle_max", "angle_max"), &SkeletonModification2DLookAt::set_constraint_angle_max); + ClassDB::bind_method(D_METHOD("get_constraint_angle_max"), &SkeletonModification2DLookAt::get_constraint_angle_max); + ClassDB::bind_method(D_METHOD("set_constraint_angle_invert", "invert"), &SkeletonModification2DLookAt::set_constraint_angle_invert); + ClassDB::bind_method(D_METHOD("get_constraint_angle_invert"), &SkeletonModification2DLookAt::get_constraint_angle_invert); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_index"), "set_bone_index", "get_bone_index"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_node", "get_bone2d_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node"); +} + +SkeletonModification2DLookAt::SkeletonModification2DLookAt() { + stack = nullptr; + is_setup = false; + bone_idx = -1; + additional_rotation = 0; + enable_constraint = false; + constraint_angle_min = 0; + constraint_angle_max = Math_PI * 2; + constraint_angle_invert = false; + enabled = true; + + editor_draw_gizmo = true; +} + +SkeletonModification2DLookAt::~SkeletonModification2DLookAt() { +} diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h new file mode 100644 index 0000000000..6aff30b826 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* skeleton_modification_2d_lookat.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 SKELETONMODIFICATION2DLOOKAT_H +#define SKELETONMODIFICATION2DLOOKAT_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DLookAt +/////////////////////////////////////// + +class SkeletonModification2DLookAt : public SkeletonModification2D { + GDCLASS(SkeletonModification2DLookAt, SkeletonModification2D); + +private: + int bone_idx = -1; + NodePath bone2d_node; + ObjectID bone2d_node_cache; + + NodePath target_node; + ObjectID target_node_cache; + Node2D *target_node_reference = nullptr; + + float additional_rotation = 0; + bool enable_constraint = false; + float constraint_angle_min = 0; + float constraint_angle_max = (2.0 * Math_PI); + bool constraint_angle_invert = false; + bool constraint_in_localspace = true; + + void update_bone2d_cache(); + void update_target_cache(); + +protected: + static void _bind_methods(); + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + +public: + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() override; + + void set_bone2d_node(const NodePath &p_target_node); + NodePath get_bone2d_node() 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(float p_rotation); + float get_additional_rotation() const; + + void set_enable_constraint(bool p_constraint); + bool get_enable_constraint() const; + void set_constraint_angle_min(float p_angle_min); + float get_constraint_angle_min() const; + void set_constraint_angle_max(float p_angle_max); + float get_constraint_angle_max() const; + void set_constraint_angle_invert(bool p_invert); + bool get_constraint_angle_invert() const; + void set_constraint_in_localspace(bool p_constraint_in_localspace); + bool get_constraint_in_localspace() const; + + SkeletonModification2DLookAt(); + ~SkeletonModification2DLookAt(); +}; + +#endif // SKELETONMODIFICATION2DLOOKAT_H diff --git a/scene/resources/skeleton_modification_2d_physicalbones.cpp b/scene/resources/skeleton_modification_2d_physicalbones.cpp new file mode 100644 index 0000000000..9dedb93f36 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_physicalbones.cpp @@ -0,0 +1,297 @@ +/*************************************************************************/ +/* skeleton_modification_2d_physicalbones.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_2d_physicalbones.h" +#include "scene/2d/physical_bone_2d.h" +#include "scene/2d/skeleton_2d.h" + +bool SkeletonModification2DPhysicalBones::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + +#ifdef TOOLS_ENABLED + // Exposes a way to fetch the PhysicalBone2D nodes from the Godot editor. + if (is_setup) { + if (Engine::get_singleton()->is_editor_hint()) { + if (path.begins_with("fetch_bones")) { + fetch_physical_bones(); + notify_property_list_changed(); + return true; + } + } + } +#endif //TOOLS_ENABLED + + if (path.begins_with("joint_")) { + int which = path.get_slicec('_', 1).to_int(); + String what = path.get_slicec('_', 2); + ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false); + + if (what == "nodepath") { + set_physical_bone_node(which, p_value); + } + return true; + } + return true; +} + +bool SkeletonModification2DPhysicalBones::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (path.begins_with("fetch_bones")) { + return true; // Do nothing! + } + } +#endif //TOOLS_ENABLED + + if (path.begins_with("joint_")) { + int which = path.get_slicec('_', 1).to_int(); + String what = path.get_slicec('_', 2); + ERR_FAIL_INDEX_V(which, physical_bone_chain.size(), false); + + if (what == "nodepath") { + r_ret = get_physical_bone_node(which); + } + return true; + } + return true; +} + +void SkeletonModification2DPhysicalBones::_get_property_list(List<PropertyInfo> *p_list) const { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "fetch_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif //TOOLS_ENABLED + + for (int i = 0; i < physical_bone_chain.size(); i++) { + String base_string = "joint_" + itos(i) + "_"; + + p_list->push_back(PropertyInfo(Variant::NODE_PATH, base_string + "nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicalBone2D", PROPERTY_USAGE_DEFAULT)); + } +} + +void SkeletonModification2DPhysicalBones::_execute(float p_delta) { + ERR_FAIL_COND_MSG(!stack || !is_setup || stack->skeleton == nullptr, + "Modification is not setup and therefore cannot execute!"); + if (!enabled) { + return; + } + + if (_simulation_state_dirty) { + _update_simulation_state(); + } + + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone_Data2D bone_data = physical_bone_chain[i]; + if (bone_data.physical_bone_node_cache.is_null()) { + WARN_PRINT_ONCE("PhysicalBone2D cache " + itos(i) + " is out of date. Attempting to update..."); + _physical_bone_update_cache(i); + continue; + } + + PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(ObjectDB::get_instance(bone_data.physical_bone_node_cache)); + if (!physical_bone) { + ERR_PRINT_ONCE("PhysicalBone2D not found at index " + itos(i) + "!"); + return; + } + if (physical_bone->get_bone2d_index() < 0 || physical_bone->get_bone2d_index() > stack->skeleton->get_bone_count()) { + ERR_PRINT_ONCE("PhysicalBone2D at index " + itos(i) + " has invalid Bone2D!"); + return; + } + Bone2D *bone_2d = stack->skeleton->get_bone(physical_bone->get_bone2d_index()); + + if (physical_bone->get_simulate_physics() && !physical_bone->get_follow_bone_when_simulating()) { + bone_2d->set_global_transform(physical_bone->get_global_transform()); + stack->skeleton->set_bone_local_pose_override(physical_bone->get_bone2d_index(), bone_2d->get_transform(), stack->strength, true); + } + } +} + +void SkeletonModification2DPhysicalBones::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack) { + is_setup = true; + + if (stack->skeleton) { + for (int i = 0; i < physical_bone_chain.size(); i++) { + _physical_bone_update_cache(i); + } + } + } +} + +void SkeletonModification2DPhysicalBones::_physical_bone_update_cache(int p_joint_idx) { + ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Cannot update PhysicalBone2D cache: joint index out of range!"); + if (!is_setup || !stack) { + if (!stack) { + ERR_PRINT_ONCE("Cannot update PhysicalBone2D cache: modification is not properly setup!"); + } + return; + } + + physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(physical_bone_chain[p_joint_idx].physical_bone_node)) { + Node *node = stack->skeleton->get_node(physical_bone_chain[p_joint_idx].physical_bone_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update Physical Bone2D " + itos(p_joint_idx) + " cache: node is not in scene tree!"); + physical_bone_chain.write[p_joint_idx].physical_bone_node_cache = node->get_instance_id(); + } + } + } +} + +int SkeletonModification2DPhysicalBones::get_physical_bone_chain_length() { + return physical_bone_chain.size(); +} + +void SkeletonModification2DPhysicalBones::set_physical_bone_chain_length(int p_length) { + ERR_FAIL_COND(p_length < 0); + physical_bone_chain.resize(p_length); + notify_property_list_changed(); +} + +void SkeletonModification2DPhysicalBones::fetch_physical_bones() { + ERR_FAIL_COND_MSG(!stack, "No modification stack found! Cannot fetch physical bones!"); + ERR_FAIL_COND_MSG(!stack->skeleton, "No skeleton found! Cannot fetch physical bones!"); + + physical_bone_chain.clear(); + + List<Node *> node_queue = List<Node *>(); + node_queue.push_back(stack->skeleton); + + while (node_queue.size() > 0) { + Node *node_to_process = node_queue[0]; + node_queue.pop_front(); + + if (node_to_process != nullptr) { + PhysicalBone2D *potential_bone = Object::cast_to<PhysicalBone2D>(node_to_process); + if (potential_bone) { + PhysicalBone_Data2D new_data = PhysicalBone_Data2D(); + new_data.physical_bone_node = stack->skeleton->get_path_to(potential_bone); + new_data.physical_bone_node_cache = potential_bone->get_instance_id(); + physical_bone_chain.push_back(new_data); + } + for (int i = 0; i < node_to_process->get_child_count(); i++) { + node_queue.push_back(node_to_process->get_child(i)); + } + } + } +} + +void SkeletonModification2DPhysicalBones::start_simulation(const TypedArray<StringName> &p_bones) { + _simulation_state_dirty = true; + _simulation_state_dirty_names = p_bones; + _simulation_state_dirty_process = true; + + if (is_setup) { + _update_simulation_state(); + } +} + +void SkeletonModification2DPhysicalBones::stop_simulation(const TypedArray<StringName> &p_bones) { + _simulation_state_dirty = true; + _simulation_state_dirty_names = p_bones; + _simulation_state_dirty_process = false; + + if (is_setup) { + _update_simulation_state(); + } +} + +void SkeletonModification2DPhysicalBones::_update_simulation_state() { + if (!_simulation_state_dirty) { + return; + } + _simulation_state_dirty = false; + + if (_simulation_state_dirty_names.size() <= 0) { + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(stack->skeleton->get_node(physical_bone_chain[i].physical_bone_node)); + if (!physical_bone) { + continue; + } + + physical_bone->set_simulate_physics(_simulation_state_dirty_process); + } + } else { + for (int i = 0; i < physical_bone_chain.size(); i++) { + PhysicalBone2D *physical_bone = Object::cast_to<PhysicalBone2D>(ObjectDB::get_instance(physical_bone_chain[i].physical_bone_node_cache)); + if (!physical_bone) { + continue; + } + if (_simulation_state_dirty_names.has(physical_bone->get_name())) { + physical_bone->set_simulate_physics(_simulation_state_dirty_process); + } + } + } +} + +void SkeletonModification2DPhysicalBones::set_physical_bone_node(int p_joint_idx, const NodePath &p_nodepath) { + ERR_FAIL_INDEX_MSG(p_joint_idx, physical_bone_chain.size(), "Joint index out of range!"); + physical_bone_chain.write[p_joint_idx].physical_bone_node = p_nodepath; + _physical_bone_update_cache(p_joint_idx); +} + +NodePath SkeletonModification2DPhysicalBones::get_physical_bone_node(int p_joint_idx) const { + ERR_FAIL_INDEX_V_MSG(p_joint_idx, physical_bone_chain.size(), NodePath(), "Joint index out of range!"); + return physical_bone_chain[p_joint_idx].physical_bone_node; +} + +void SkeletonModification2DPhysicalBones::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_physical_bone_chain_length", "length"), &SkeletonModification2DPhysicalBones::set_physical_bone_chain_length); + ClassDB::bind_method(D_METHOD("get_physical_bone_chain_length"), &SkeletonModification2DPhysicalBones::get_physical_bone_chain_length); + + ClassDB::bind_method(D_METHOD("set_physical_bone_node", "joint_idx", "physicalbone2d_node"), &SkeletonModification2DPhysicalBones::set_physical_bone_node); + ClassDB::bind_method(D_METHOD("get_physical_bone_node", "joint_idx"), &SkeletonModification2DPhysicalBones::get_physical_bone_node); + + ClassDB::bind_method(D_METHOD("fetch_physical_bones"), &SkeletonModification2DPhysicalBones::fetch_physical_bones); + ClassDB::bind_method(D_METHOD("start_simulation", "bones"), &SkeletonModification2DPhysicalBones::start_simulation, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("stop_simulation", "bones"), &SkeletonModification2DPhysicalBones::stop_simulation, DEFVAL(Array())); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_bone_chain_length", PROPERTY_HINT_RANGE, "0,100,1"), "set_physical_bone_chain_length", "get_physical_bone_chain_length"); +} + +SkeletonModification2DPhysicalBones::SkeletonModification2DPhysicalBones() { + stack = nullptr; + is_setup = false; + physical_bone_chain = Vector<PhysicalBone_Data2D>(); + enabled = true; + editor_draw_gizmo = false; // Nothing to really show in a gizmo right now. +} + +SkeletonModification2DPhysicalBones::~SkeletonModification2DPhysicalBones() { +} diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h new file mode 100644 index 0000000000..cdf6a5f570 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* skeleton_modification_2d_physicalbones.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 SKELETONMODIFICATION2DPHYSICALBONES_H +#define SKELETONMODIFICATION2DPHYSICALBONES_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DJIGGLE +/////////////////////////////////////// + +class SkeletonModification2DPhysicalBones : public SkeletonModification2D { + GDCLASS(SkeletonModification2DPhysicalBones, SkeletonModification2D); + +private: + struct PhysicalBone_Data2D { + NodePath physical_bone_node; + ObjectID physical_bone_node_cache; + }; + Vector<PhysicalBone_Data2D> physical_bone_chain; + + void _physical_bone_update_cache(int p_joint_idx); + + bool _simulation_state_dirty = false; + TypedArray<StringName> _simulation_state_dirty_names; + bool _simulation_state_dirty_process; + void _update_simulation_state(); + +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 _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + + int get_physical_bone_chain_length(); + void set_physical_bone_chain_length(int p_new_length); + + void set_physical_bone_node(int p_joint_idx, const NodePath &p_path); + NodePath get_physical_bone_node(int p_joint_idx) const; + + void fetch_physical_bones(); + void start_simulation(const TypedArray<StringName> &p_bones); + void stop_simulation(const TypedArray<StringName> &p_bones); + + SkeletonModification2DPhysicalBones(); + ~SkeletonModification2DPhysicalBones(); +}; + +#endif // SKELETONMODIFICATION2DPHYSICALBONES_H diff --git a/scene/resources/skeleton_modification_2d_stackholder.cpp b/scene/resources/skeleton_modification_2d_stackholder.cpp new file mode 100644 index 0000000000..9436092cd9 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_stackholder.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_stackholder.h" +#include "scene/2d/skeleton_2d.h" + +bool SkeletonModification2DStackHolder::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path == "held_modification_stack") { + set_held_modification_stack(p_value); + } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + set_editor_draw_gizmo(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool SkeletonModification2DStackHolder::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path == "held_modification_stack") { + r_ret = get_held_modification_stack(); + } + +#ifdef TOOLS_ENABLED + if (path == "editor/draw_gizmo") { + r_ret = get_editor_draw_gizmo(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DStackHolder::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::OBJECT, "held_modification_stack", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DStackHolder::_execute(float 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 SkeletonModification2DStackHolder::_setup_modification(SkeletonModificationStack2D *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 SkeletonModification2DStackHolder::_draw_editor_gizmo() { + if (stack) { + if (held_modification_stack.is_valid()) { + held_modification_stack->draw_editor_gizmos(); + } + } +} + +void SkeletonModification2DStackHolder::set_held_modification_stack(Ref<SkeletonModificationStack2D> 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<SkeletonModificationStack2D> SkeletonModification2DStackHolder::get_held_modification_stack() const { + return held_modification_stack; +} + +void SkeletonModification2DStackHolder::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_held_modification_stack", "held_modification_stack"), &SkeletonModification2DStackHolder::set_held_modification_stack); + ClassDB::bind_method(D_METHOD("get_held_modification_stack"), &SkeletonModification2DStackHolder::get_held_modification_stack); +} + +SkeletonModification2DStackHolder::SkeletonModification2DStackHolder() { + stack = nullptr; + is_setup = false; + enabled = true; +} + +SkeletonModification2DStackHolder::~SkeletonModification2DStackHolder() { +} diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h new file mode 100644 index 0000000000..9cc38e3942 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* skeleton_modification_2d_stackholder.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 SKELETONMODIFICATION2DSTACKHOLDER_H +#define SKELETONMODIFICATION2DSTACKHOLDER_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DJIGGLE +/////////////////////////////////////// + +class SkeletonModification2DStackHolder : public SkeletonModification2D { + GDCLASS(SkeletonModification2DStackHolder, SkeletonModification2D); + +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: + Ref<SkeletonModificationStack2D> held_modification_stack; + + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() override; + + void set_held_modification_stack(Ref<SkeletonModificationStack2D> p_held_stack); + Ref<SkeletonModificationStack2D> get_held_modification_stack() const; + + SkeletonModification2DStackHolder(); + ~SkeletonModification2DStackHolder(); +}; + +#endif // SKELETONMODIFICATION2DSTACKHOLDER_H diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp new file mode 100644 index 0000000000..0a91290015 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -0,0 +1,481 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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 "skeleton_modification_2d_twoboneik.h" +#include "scene/2d/skeleton_2d.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + +bool SkeletonModification2DTwoBoneIK::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + + if (path == "joint_one_bone_idx") { + set_joint_one_bone_idx(p_value); + } else if (path == "joint_one_bone2d_node") { + set_joint_one_bone2d_node(p_value); + } else if (path == "joint_two_bone_idx") { + set_joint_two_bone_idx(p_value); + } else if (path == "joint_two_bone2d_node") { + set_joint_two_bone2d_node(p_value); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + set_editor_draw_gizmo(p_value); + } else if (path.begins_with("editor/draw_min_max")) { + set_editor_draw_min_max(p_value); + } +#endif // TOOLS_ENABLED + + return true; +} + +bool SkeletonModification2DTwoBoneIK::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path == "joint_one_bone_idx") { + r_ret = get_joint_one_bone_idx(); + } else if (path == "joint_one_bone2d_node") { + r_ret = get_joint_one_bone2d_node(); + } else if (path == "joint_two_bone_idx") { + r_ret = get_joint_two_bone_idx(); + } else if (path == "joint_two_bone2d_node") { + r_ret = get_joint_two_bone2d_node(); + } + +#ifdef TOOLS_ENABLED + if (path.begins_with("editor/draw_gizmo")) { + r_ret = get_editor_draw_gizmo(); + } else if (path.begins_with("editor/draw_min_max")) { + r_ret = get_editor_draw_min_max(); + } +#endif // TOOLS_ENABLED + + return true; +} + +void SkeletonModification2DTwoBoneIK::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::INT, "joint_one_bone_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "joint_one_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", 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::NODE_PATH, "joint_two_bone2d_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D", PROPERTY_USAGE_DEFAULT)); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, "editor/draw_min_max", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DTwoBoneIK::_execute(float 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()) { + WARN_PRINT_ONCE("Target cache is out of date. Attempting to update..."); + update_target_cache(); + return; + } + + if (joint_one_bone2d_node_cache.is_null() && !joint_one_bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Joint one Bone2D node cache is out of date. Attempting to update..."); + update_joint_one_bone2d_cache(); + } + if (joint_two_bone2d_node_cache.is_null() && !joint_two_bone2d_node.is_empty()) { + WARN_PRINT_ONCE("Joint two Bone2D node cache is out of date. Attempting to update..."); + update_joint_two_bone2d_cache(); + } + + Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + if (!target || !target->is_inside_tree()) { + ERR_PRINT_ONCE("Target node is not in the scene tree. Cannot execute modification!"); + return; + } + + Bone2D *joint_one_bone = stack->skeleton->get_bone(joint_one_bone_idx); + if (joint_one_bone == nullptr) { + ERR_PRINT_ONCE("Joint one bone_idx does not point to a valid bone! Cannot execute modification!"); + return; + } + + Bone2D *joint_two_bone = stack->skeleton->get_bone(joint_two_bone_idx); + if (joint_two_bone == nullptr) { + ERR_PRINT_ONCE("Joint two bone_idx does not point to a valid bone! Cannot execute modification!"); + return; + } + + // 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 + Vector2 target_difference = target->get_global_transform().get_origin() - joint_one_bone->get_global_transform().get_origin(); + float joint_one_to_target = target_difference.length(); + float angle_atan = Math::atan2(target_difference.y, target_difference.x); + + float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); + float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); + bool override_angles_due_to_out_of_range = false; + + if (joint_one_to_target < target_minimum_distance) { + joint_one_to_target = target_minimum_distance; + } + if (joint_one_to_target > target_maximum_distance && target_maximum_distance > 0.0) { + joint_one_to_target = target_maximum_distance; + } + + if (bone_one_length + bone_two_length < joint_one_to_target) { + override_angles_due_to_out_of_range = true; + } + + if (!override_angles_due_to_out_of_range) { + float angle_0 = Math::acos(((joint_one_to_target * joint_one_to_target) + (bone_one_length * bone_one_length) - (bone_two_length * bone_two_length)) / (2.0 * joint_one_to_target * bone_one_length)); + float angle_1 = Math::acos(((bone_two_length * bone_two_length) + (bone_one_length * bone_one_length) - (joint_one_to_target * joint_one_to_target)) / (2.0 * bone_two_length * bone_one_length)); + + if (flip_bend_direction) { + angle_0 = -angle_0; + angle_1 = -angle_1; + } + + if (isnan(angle_0) || isnan(angle_1)) { + // We cannot solve for this angle! Do nothing to avoid setting the rotation (and scale) to NaN. + } else { + joint_one_bone->set_global_rotation(angle_atan - angle_0 - joint_one_bone->get_bone_angle()); + joint_two_bone->set_rotation(-Math_PI - angle_1 - joint_two_bone->get_bone_angle() + joint_one_bone->get_bone_angle()); + } + } else { + joint_one_bone->set_global_rotation(angle_atan - joint_one_bone->get_bone_angle()); + joint_two_bone->set_global_rotation(angle_atan - joint_two_bone->get_bone_angle()); + } + + stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, joint_one_bone->get_transform(), stack->strength, true); + stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, joint_two_bone->get_transform(), stack->strength, true); +} + +void SkeletonModification2DTwoBoneIK::_setup_modification(SkeletonModificationStack2D *p_stack) { + stack = p_stack; + + if (stack) { + is_setup = true; + update_target_cache(); + update_joint_one_bone2d_cache(); + update_joint_two_bone2d_cache(); + } +} + +void SkeletonModification2DTwoBoneIK::_draw_editor_gizmo() { + if (!enabled || !is_setup) { + return; + } + + Bone2D *operation_bone_one = stack->skeleton->get_bone(joint_one_bone_idx); + if (!operation_bone_one) { + return; + } + stack->skeleton->draw_set_transform( + stack->skeleton->get_global_transform().affine_inverse().xform(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); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + } +#endif // TOOLS_ENABLED + + if (flip_bend_direction) { + float angle = -(Math_PI * 0.5) + operation_bone_one->get_bone_angle(); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + } else { + float angle = (Math_PI * 0.5) + operation_bone_one->get_bone_angle(); + stack->skeleton->draw_line(Vector2(0, 0), Vector2(Math::cos(angle), sin(angle)) * (operation_bone_one->get_length() * 0.5), bone_ik_color, 2.0); + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (editor_draw_min_max) { + if (target_maximum_distance != 0.0 || target_minimum_distance != 0.0) { + Vector2 target_direction = Vector2(0, 1); + if (target_node_cache.is_valid()) { + stack->skeleton->draw_set_transform(Vector2(0, 0), 0.0); + Node2D *target = Object::cast_to<Node2D>(ObjectDB::get_instance(target_node_cache)); + target_direction = operation_bone_one->get_global_position().direction_to(target->get_global_position()); + } + + stack->skeleton->draw_circle(target_direction * target_minimum_distance, 8, bone_ik_color); + stack->skeleton->draw_circle(target_direction * target_maximum_distance, 8, bone_ik_color); + stack->skeleton->draw_line(target_direction * target_minimum_distance, target_direction * target_maximum_distance, bone_ik_color, 2.0); + } + } + } +#endif // TOOLS_ENABLED +} + +void SkeletonModification2DTwoBoneIK::update_target_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("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(); + } + } + } +} + +void SkeletonModification2DTwoBoneIK::update_joint_one_bone2d_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update joint one Bone2D cache: modification is not properly setup!"); + return; + } + + joint_one_bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(joint_one_bone2d_node)) { + Node *node = stack->skeleton->get_node(joint_one_bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update update joint one Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint one Bone2D cache: node is not in the scene tree!"); + joint_one_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + joint_one_bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("update joint one Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +void SkeletonModification2DTwoBoneIK::update_joint_two_bone2d_cache() { + if (!is_setup || !stack) { + ERR_PRINT_ONCE("Cannot update joint two Bone2D cache: modification is not properly setup!"); + return; + } + + joint_two_bone2d_node_cache = ObjectID(); + if (stack->skeleton) { + if (stack->skeleton->is_inside_tree()) { + if (stack->skeleton->has_node(joint_two_bone2d_node)) { + Node *node = stack->skeleton->get_node(joint_two_bone2d_node); + ERR_FAIL_COND_MSG(!node || stack->skeleton == node, + "Cannot update update joint two Bone2D cache: node is this modification's skeleton or cannot be found!"); + ERR_FAIL_COND_MSG(!node->is_inside_tree(), + "Cannot update update joint two Bone2D cache: node is not in scene tree!"); + joint_two_bone2d_node_cache = node->get_instance_id(); + + Bone2D *bone = Object::cast_to<Bone2D>(node); + if (bone) { + joint_two_bone_idx = bone->get_index_in_skeleton(); + } else { + ERR_FAIL_MSG("update joint two Bone2D cache: Nodepath to Bone2D is not a Bone2D node!"); + } + } + } + } +} + +void SkeletonModification2DTwoBoneIK::set_target_node(const NodePath &p_target_node) { + target_node = p_target_node; + update_target_cache(); +} + +NodePath SkeletonModification2DTwoBoneIK::get_target_node() const { + return target_node; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node(const NodePath &p_target_node) { + joint_one_bone2d_node = p_target_node; + update_joint_one_bone2d_cache(); + notify_property_list_changed(); +} + +void SkeletonModification2DTwoBoneIK::set_target_minimum_distance(float p_distance) { + ERR_FAIL_COND_MSG(p_distance < 0, "Target minimum distance cannot be less than zero!"); + target_minimum_distance = p_distance; +} + +float SkeletonModification2DTwoBoneIK::get_target_minimum_distance() const { + return target_minimum_distance; +} + +void SkeletonModification2DTwoBoneIK::set_target_maximum_distance(float p_distance) { + ERR_FAIL_COND_MSG(p_distance < 0, "Target maximum distance cannot be less than zero!"); + target_maximum_distance = p_distance; +} + +float SkeletonModification2DTwoBoneIK::get_target_maximum_distance() const { + return target_maximum_distance; +} + +void SkeletonModification2DTwoBoneIK::set_flip_bend_direction(bool p_flip_direction) { + flip_bend_direction = p_flip_direction; + +#ifdef TOOLS_ENABLED + if (stack && is_setup) { + stack->set_editor_gizmos_dirty(true); + } +#endif // TOOLS_ENABLED +} + +bool SkeletonModification2DTwoBoneIK::get_flip_bend_direction() const { + return flip_bend_direction; +} + +NodePath SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node() const { + return joint_one_bone2d_node; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node(const NodePath &p_target_node) { + joint_two_bone2d_node = p_target_node; + update_joint_two_bone2d_cache(); + notify_property_list_changed(); +} + +NodePath SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node() const { + return joint_two_bone2d_node; +} + +void SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx(int p_bone_idx) { + ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + joint_one_bone_idx = p_bone_idx; + joint_one_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + joint_one_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); + joint_one_bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint one..."); + joint_one_bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +int SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx() const { + return joint_one_bone_idx; +} + +void SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx(int p_bone_idx) { + ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!"); + + if (is_setup) { + if (stack->skeleton) { + ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!"); + joint_two_bone_idx = p_bone_idx; + joint_two_bone2d_node_cache = stack->skeleton->get_bone(p_bone_idx)->get_instance_id(); + joint_two_bone2d_node = stack->skeleton->get_path_to(stack->skeleton->get_bone(p_bone_idx)); + } else { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); + joint_two_bone_idx = p_bone_idx; + } + } else { + WARN_PRINT("TwoBoneIK: Cannot verify the joint bone index for joint two..."); + joint_two_bone_idx = p_bone_idx; + } + + notify_property_list_changed(); +} + +int SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx() const { + return joint_two_bone_idx; +} + +#ifdef TOOLS_ENABLED +void SkeletonModification2DTwoBoneIK::set_editor_draw_min_max(bool p_draw) { + editor_draw_min_max = p_draw; +} + +bool SkeletonModification2DTwoBoneIK::get_editor_draw_min_max() const { + return editor_draw_min_max; +} +#endif // TOOLS_ENABLED + +void SkeletonModification2DTwoBoneIK::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_target_node", "target_nodepath"), &SkeletonModification2DTwoBoneIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonModification2DTwoBoneIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_target_minimum_distance", "minimum_distance"), &SkeletonModification2DTwoBoneIK::set_target_minimum_distance); + ClassDB::bind_method(D_METHOD("get_target_minimum_distance"), &SkeletonModification2DTwoBoneIK::get_target_minimum_distance); + ClassDB::bind_method(D_METHOD("set_target_maximum_distance", "maximum_distance"), &SkeletonModification2DTwoBoneIK::set_target_maximum_distance); + ClassDB::bind_method(D_METHOD("get_target_maximum_distance"), &SkeletonModification2DTwoBoneIK::get_target_maximum_distance); + ClassDB::bind_method(D_METHOD("set_flip_bend_direction", "flip_direction"), &SkeletonModification2DTwoBoneIK::set_flip_bend_direction); + ClassDB::bind_method(D_METHOD("get_flip_bend_direction"), &SkeletonModification2DTwoBoneIK::get_flip_bend_direction); + + ClassDB::bind_method(D_METHOD("set_joint_one_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone2d_node); + ClassDB::bind_method(D_METHOD("get_joint_one_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone2d_node); + ClassDB::bind_method(D_METHOD("set_joint_one_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_one_bone_idx); + ClassDB::bind_method(D_METHOD("get_joint_one_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_one_bone_idx); + + ClassDB::bind_method(D_METHOD("set_joint_two_bone2d_node", "bone2d_node"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone2d_node); + ClassDB::bind_method(D_METHOD("get_joint_two_bone2d_node"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone2d_node); + ClassDB::bind_method(D_METHOD("set_joint_two_bone_idx", "bone_idx"), &SkeletonModification2DTwoBoneIK::set_joint_two_bone_idx); + ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0, 100000000, 0.01"), "set_target_minimum_distance", "get_target_minimum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0, 100000000, 0.01"), "set_target_maximum_distance", "get_target_maximum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction", PROPERTY_HINT_NONE, ""), "set_flip_bend_direction", "get_flip_bend_direction"); + ADD_GROUP("", ""); +} + +SkeletonModification2DTwoBoneIK::SkeletonModification2DTwoBoneIK() { + stack = nullptr; + is_setup = false; + enabled = true; + editor_draw_gizmo = true; +} + +SkeletonModification2DTwoBoneIK::~SkeletonModification2DTwoBoneIK() { +} diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h new file mode 100644 index 0000000000..c7e545a488 --- /dev/null +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* skeleton_modification_2d_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. */ +/*************************************************************************/ + +#ifndef SKELETONMODIFICATION2DTWOBONEIK_H +#define SKELETONMODIFICATION2DTWOBONEIK_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModification2DJIGGLE +/////////////////////////////////////// + +class SkeletonModification2DTwoBoneIK : public SkeletonModification2D { + GDCLASS(SkeletonModification2DTwoBoneIK, SkeletonModification2D); + +private: + NodePath target_node; + ObjectID target_node_cache; + float target_minimum_distance = 0; + float target_maximum_distance = 0; + bool flip_bend_direction = false; + + NodePath joint_one_bone2d_node; + ObjectID joint_one_bone2d_node_cache; + int joint_one_bone_idx = -1; + + NodePath joint_two_bone2d_node; + ObjectID joint_two_bone2d_node_cache; + int joint_two_bone_idx = -1; + +#ifdef TOOLS_ENABLED + bool editor_draw_min_max = false; +#endif // TOOLS_ENABLED + + void update_target_cache(); + void update_joint_one_bone2d_cache(); + void update_joint_two_bone2d_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: + void _execute(float p_delta) override; + void _setup_modification(SkeletonModificationStack2D *p_stack) override; + void _draw_editor_gizmo() override; + + void set_target_node(const NodePath &p_target_node); + NodePath get_target_node() const; + + void set_target_minimum_distance(float p_minimum_distance); + float get_target_minimum_distance() const; + void set_target_maximum_distance(float p_maximum_distance); + float get_target_maximum_distance() const; + void set_flip_bend_direction(bool p_flip_direction); + bool get_flip_bend_direction() const; + + void set_joint_one_bone2d_node(const NodePath &p_node); + NodePath get_joint_one_bone2d_node() const; + void set_joint_one_bone_idx(int p_bone_idx); + int get_joint_one_bone_idx() const; + + void set_joint_two_bone2d_node(const NodePath &p_node); + NodePath get_joint_two_bone2d_node() const; + void set_joint_two_bone_idx(int p_bone_idx); + int get_joint_two_bone_idx() const; + +#ifdef TOOLS_ENABLED + void set_editor_draw_min_max(bool p_draw); + bool get_editor_draw_min_max() const; +#endif // TOOLS_ENABLED + + SkeletonModification2DTwoBoneIK(); + ~SkeletonModification2DTwoBoneIK(); +}; + +#endif // SKELETONMODIFICATION2DTWOBONEIK_H diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp new file mode 100644 index 0000000000..72c1c330ef --- /dev/null +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -0,0 +1,269 @@ +/*************************************************************************/ +/* skeleton_modification_stack_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 "skeleton_modification_stack_2d.h" +#include "scene/2d/skeleton_2d.h" + +void SkeletonModificationStack2D::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < modifications.size(); i++) { + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modifications/" + itos(i), + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModification2D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + } +} + +bool SkeletonModificationStack2D::_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 SkeletonModificationStack2D::_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 SkeletonModificationStack2D::setup() { + if (is_setup) { + return; + } + + if (skeleton != nullptr) { + is_setup = true; + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->_setup_modification(this); + } + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED + + } else { + WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!"); + } +} + +void SkeletonModificationStack2D::execute(float 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 (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + + if (modifications[i]->get_execution_mode() == p_execution_mode) { + modifications.get(i)->_execute(p_delta); + } + } +} + +void SkeletonModificationStack2D::draw_editor_gizmos() { + if (!is_setup) { + return; + } + + if (editor_gizmo_dirty) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + + if (modifications[i]->editor_draw_gizmo) { + modifications.get(i)->_draw_editor_gizmo(); + } + } + skeleton->draw_set_transform(Vector2(0, 0)); + editor_gizmo_dirty = false; + } +} + +void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) { + if (!is_setup) { + return; + } + + if (!editor_gizmo_dirty && p_dirty) { + editor_gizmo_dirty = p_dirty; + if (skeleton) { + skeleton->update(); + } + } else { + editor_gizmo_dirty = p_dirty; + } +} + +void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) { + for (int i = 0; i < modifications.size(); i++) { + if (!modifications[i].is_valid()) { + continue; + } + modifications.get(i)->set_enabled(p_enabled); + } +} + +Ref<SkeletonModification2D> SkeletonModificationStack2D::get_modification(int p_mod_idx) const { + ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr); + return modifications[p_mod_idx]; +} + +void SkeletonModificationStack2D::add_modification(Ref<SkeletonModification2D> p_mod) { + ERR_FAIL_COND(!p_mod.is_valid()); + + p_mod->_setup_modification(this); + modifications.push_back(p_mod); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +void SkeletonModificationStack2D::delete_modification(int p_mod_idx) { + ERR_FAIL_INDEX(p_mod_idx, modifications.size()); + modifications.remove(p_mod_idx); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod) { + ERR_FAIL_INDEX(p_mod_idx, modifications.size()); + + if (p_mod == nullptr) { + modifications.insert(p_mod_idx, nullptr); + } else { + p_mod->_setup_modification(this); + modifications.insert(p_mod_idx, p_mod); + } + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +void SkeletonModificationStack2D::set_modification_count(int p_count) { + modifications.resize(p_count); + notify_property_list_changed(); + +#ifdef TOOLS_ENABLED + set_editor_gizmos_dirty(true); +#endif // TOOLS_ENABLED +} + +int SkeletonModificationStack2D::get_modification_count() const { + return modifications.size(); +} + +void SkeletonModificationStack2D::set_skeleton(Skeleton2D *p_skeleton) { + skeleton = p_skeleton; +} + +Skeleton2D *SkeletonModificationStack2D::get_skeleton() const { + return skeleton; +} + +bool SkeletonModificationStack2D::get_is_setup() const { + return is_setup; +} + +void SkeletonModificationStack2D::set_enabled(bool p_enabled) { + enabled = p_enabled; +} + +bool SkeletonModificationStack2D::get_enabled() const { + return enabled; +} + +void SkeletonModificationStack2D::set_strength(float 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; +} + +float SkeletonModificationStack2D::get_strength() const { + return strength; +} + +void SkeletonModificationStack2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup); + ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack2D::execute); + + ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack2D::enable_all_modifications); + ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack2D::get_modification); + ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack2D::add_modification); + ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification); + ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification); + + ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count); + ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count); + + ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled); + ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack2D::get_enabled); + + ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack2D::set_strength); + ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack2D::get_strength); + + ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack2D::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"); +} + +SkeletonModificationStack2D::SkeletonModificationStack2D() { +} diff --git a/scene/resources/skeleton_modification_stack_2d.h b/scene/resources/skeleton_modification_stack_2d.h new file mode 100644 index 0000000000..58855701a1 --- /dev/null +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* skeleton_modification_stack_2d.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 SKELETONMODIFICATIONSTACK2D_H +#define SKELETONMODIFICATIONSTACK2D_H + +#include "scene/2d/skeleton_2d.h" +#include "scene/resources/skeleton_modification_2d.h" + +/////////////////////////////////////// +// SkeletonModificationStack2D +/////////////////////////////////////// + +class Skeleton2D; +class SkeletonModification2D; +class Bone2D; + +class SkeletonModificationStack2D : public Resource { + GDCLASS(SkeletonModificationStack2D, Resource); + friend class Skeleton2D; + friend class SkeletonModification2D; + +protected: + static void _bind_methods(); + void _get_property_list(List<PropertyInfo> *p_list) const; + bool _set(const StringName &p_path, const Variant &p_value); + bool _get(const StringName &p_path, Variant &r_ret) const; + +public: + Skeleton2D *skeleton = nullptr; + bool is_setup = false; + bool enabled = false; + float strength = 1.0; + + enum EXECUTION_MODE { + execution_mode_process, + execution_mode_physics_process + }; + + Vector<Ref<SkeletonModification2D>> modifications = Vector<Ref<SkeletonModification2D>>(); + + void setup(); + void execute(float p_delta, int p_execution_mode); + + bool editor_gizmo_dirty = false; + void draw_editor_gizmos(); + void set_editor_gizmos_dirty(bool p_dirty); + + void enable_all_modifications(bool p_enable); + Ref<SkeletonModification2D> get_modification(int p_mod_idx) const; + void add_modification(Ref<SkeletonModification2D> p_mod); + void delete_modification(int p_mod_idx); + void set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod); + + void set_modification_count(int p_count); + int get_modification_count() const; + + void set_skeleton(Skeleton2D *p_skeleton); + Skeleton2D *get_skeleton() const; + + bool get_is_setup() const; + + void set_enabled(bool p_enabled); + bool get_enabled() const; + + void set_strength(float p_strength); + float get_strength() const; + + SkeletonModificationStack2D(); +}; + +#endif // SKELETONMODIFICATION2D_H diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index fee8fdbde2..710612ae05 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -38,14 +38,14 @@ void Skin::set_bind_count(int p_size) { emit_changed(); } -void Skin::add_bind(int p_bone, const Transform &p_pose) { +void Skin::add_bind(int p_bone, const Transform3D &p_pose) { uint32_t index = bind_count; set_bind_count(bind_count + 1); set_bind_bone(index, p_bone); set_bind_pose(index, p_pose); } -void Skin::add_named_bind(const String &p_name, const Transform &p_pose) { +void Skin::add_named_bind(const String &p_name, const Transform3D &p_pose) { uint32_t index = bind_count; set_bind_count(bind_count + 1); set_bind_name(index, p_name); @@ -68,7 +68,7 @@ void Skin::set_bind_bone(int p_index, int p_bone) { emit_changed(); } -void Skin::set_bind_pose(int p_index, const Transform &p_pose) { +void Skin::set_bind_pose(int p_index, const Transform3D &p_pose) { ERR_FAIL_INDEX(p_index, bind_count); binds_ptr[p_index].pose = p_pose; emit_changed(); @@ -134,7 +134,7 @@ void Skin::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < get_bind_count(); i++) { p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name")); p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose")); } } diff --git a/scene/resources/skin.h b/scene/resources/skin.h index f5d64f96aa..6857bf743a 100644 --- a/scene/resources/skin.h +++ b/scene/resources/skin.h @@ -39,7 +39,7 @@ class Skin : public Resource { struct Bind { int bone = -1; StringName name; - Transform pose; + Transform3D pose; }; Vector<Bind> binds; @@ -59,11 +59,11 @@ public: void set_bind_count(int p_size); inline int get_bind_count() const { return bind_count; } - void add_bind(int p_bone, const Transform &p_pose); - void add_named_bind(const String &p_name, const Transform &p_pose); + void add_bind(int p_bone, const Transform3D &p_pose); + void add_named_bind(const String &p_name, const Transform3D &p_pose); void set_bind_bone(int p_index, int p_bone); - void set_bind_pose(int p_index, const Transform &p_pose); + void set_bind_pose(int p_index, const Transform3D &p_pose); void set_bind_name(int p_index, const StringName &p_name); inline int get_bind_bone(int p_index) const { @@ -80,9 +80,9 @@ public: return binds_ptr[p_index].name; } - inline Transform get_bind_pose(int p_index) const { + inline Transform3D get_bind_pose(int p_index) const { #ifdef DEBUG_ENABLED - ERR_FAIL_INDEX_V(p_index, bind_count, Transform()); + ERR_FAIL_INDEX_V(p_index, bind_count, Transform3D()); #endif return binds_ptr[p_index].pose; } diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index f50ee9c4c8..b34d3feb47 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -181,69 +181,75 @@ void ProceduralSkyMaterial::_bind_methods() { } ProceduralSkyMaterial::ProceduralSkyMaterial() { - String code = "shader_type sky;\n\n"; - - code += "uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0);\n"; - code += "uniform vec4 sky_horizon_color : hint_color = vec4(0.55, 0.69, 0.81, 1.0);\n"; - code += "uniform float sky_curve : hint_range(0, 1) = 0.09;\n"; - code += "uniform float sky_energy = 1.0;\n\n"; - code += "uniform vec4 ground_bottom_color : hint_color = vec4(0.12, 0.12, 0.13, 1.0);\n"; - code += "uniform vec4 ground_horizon_color : hint_color = vec4(0.37, 0.33, 0.31, 1.0);\n"; - code += "uniform float ground_curve : hint_range(0, 1) = 0.02;\n"; - code += "uniform float ground_energy = 1.0;\n\n"; - code += "uniform float sun_angle_max = 1.74;\n"; - code += "uniform float sun_curve : hint_range(0, 1) = 0.05;\n\n"; - code += "const float PI = 3.1415926535897932384626433833;\n\n"; - code += "void sky() {\n"; - code += "\tfloat v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));\n"; - code += "\tfloat c = (1.0 - v_angle / (PI * 0.5));\n"; - code += "\tvec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0));\n"; - code += "\tsky *= sky_energy;\n"; - code += "\tif (LIGHT0_ENABLED) {\n"; - code += "\t\tfloat sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR));\n"; - code += "\t\tif (sun_angle < LIGHT0_SIZE) {\n"; - code += "\t\t\tsky = LIGHT0_COLOR * LIGHT0_ENERGY;\n"; - code += "\t\t} else if (sun_angle < sun_angle_max) {\n"; - code += "\t\t\tfloat c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE);\n"; - code += "\t\t\tsky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n"; - code += "\t\t}\n"; - code += "\t}\n"; - code += "\tif (LIGHT1_ENABLED) {\n"; - code += "\t\tfloat sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR));\n"; - code += "\t\tif (sun_angle < LIGHT1_SIZE) {\n"; - code += "\t\t\tsky = LIGHT1_COLOR * LIGHT1_ENERGY;\n"; - code += "\t\t} else if (sun_angle < sun_angle_max) {\n"; - code += "\t\t\tfloat c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE);\n"; - code += "\t\t\tsky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n"; - code += "\t\t}\n"; - code += "\t}\n"; - code += "\tif (LIGHT2_ENABLED) {\n"; - code += "\t\tfloat sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR));\n"; - code += "\t\tif (sun_angle < LIGHT2_SIZE) {\n"; - code += "\t\t\tsky = LIGHT2_COLOR * LIGHT2_ENERGY;\n"; - code += "\t\t} else if (sun_angle < sun_angle_max) {\n"; - code += "\t\t\tfloat c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE);\n"; - code += "\t\t\tsky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n"; - code += "\t\t}\n"; - code += "\t}\n"; - code += "\tif (LIGHT3_ENABLED) {\n"; - code += "\t\tfloat sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR));\n"; - code += "\t\tif (sun_angle < LIGHT3_SIZE) {\n"; - code += "\t\t\tsky = LIGHT3_COLOR * LIGHT3_ENERGY;\n"; - code += "\t\t} else if (sun_angle < sun_angle_max) {\n"; - code += "\t\t\tfloat c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE);\n"; - code += "\t\t\tsky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0));\n"; - code += "\t\t}\n"; - code += "\t}\n"; - code += "\tc = (v_angle - (PI * 0.5)) / (PI * 0.5);\n"; - code += "\tvec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0));\n"; - code += "\tground *= ground_energy;\n"; - code += "\tCOLOR = mix(ground, sky, step(0.0, EYEDIR.y));\n"; - code += "}\n"; - shader = RS::get_singleton()->shader_create(); - RS::get_singleton()->shader_set_code(shader, code); + RS::get_singleton()->shader_set_code(shader, R"( +shader_type sky; + +uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0); +uniform vec4 sky_horizon_color : hint_color = vec4(0.55, 0.69, 0.81, 1.0); +uniform float sky_curve : hint_range(0, 1) = 0.09; +uniform float sky_energy = 1.0; +uniform vec4 ground_bottom_color : hint_color = vec4(0.12, 0.12, 0.13, 1.0); +uniform vec4 ground_horizon_color : hint_color = vec4(0.37, 0.33, 0.31, 1.0); +uniform float ground_curve : hint_range(0, 1) = 0.02; +uniform float ground_energy = 1.0; +uniform float sun_angle_max = 1.74; +uniform float sun_curve : hint_range(0, 1) = 0.05; + +void sky() { + float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); + float c = (1.0 - v_angle / (PI * 0.5)); + vec3 sky = mix(sky_horizon_color.rgb, sky_top_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / sky_curve), 0.0, 1.0)); + sky *= sky_energy; + + if (LIGHT0_ENABLED) { + float sun_angle = acos(dot(LIGHT0_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT0_SIZE) { + sky = LIGHT0_COLOR * LIGHT0_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT0_SIZE) / (sun_angle_max - LIGHT0_SIZE); + sky = mix(LIGHT0_COLOR * LIGHT0_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT1_ENABLED) { + float sun_angle = acos(dot(LIGHT1_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT1_SIZE) { + sky = LIGHT1_COLOR * LIGHT1_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT1_SIZE) / (sun_angle_max - LIGHT1_SIZE); + sky = mix(LIGHT1_COLOR * LIGHT1_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT2_ENABLED) { + float sun_angle = acos(dot(LIGHT2_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT2_SIZE) { + sky = LIGHT2_COLOR * LIGHT2_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT2_SIZE) / (sun_angle_max - LIGHT2_SIZE); + sky = mix(LIGHT2_COLOR * LIGHT2_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + if (LIGHT3_ENABLED) { + float sun_angle = acos(dot(LIGHT3_DIRECTION, EYEDIR)); + if (sun_angle < LIGHT3_SIZE) { + sky = LIGHT3_COLOR * LIGHT3_ENERGY; + } else if (sun_angle < sun_angle_max) { + float c2 = (sun_angle - LIGHT3_SIZE) / (sun_angle_max - LIGHT3_SIZE); + sky = mix(LIGHT3_COLOR * LIGHT3_ENERGY, sky, clamp(1.0 - pow(1.0 - c2, 1.0 / sun_curve), 0.0, 1.0)); + } + } + + c = (v_angle - (PI * 0.5)) / (PI * 0.5); + vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0)); + ground *= ground_energy; + + COLOR = mix(ground, sky, step(0.0, EYEDIR.y)); +} +)"); RS::get_singleton()->material_set_shader(_get_material(), shader); @@ -271,7 +277,8 @@ ProceduralSkyMaterial::~ProceduralSkyMaterial() { void PanoramaSkyMaterial::set_panorama(const Ref<Texture2D> &p_panorama) { panorama = p_panorama; - RS::get_singleton()->material_set_param(_get_material(), "source_panorama", panorama); + RID tex_rid = p_panorama.is_valid() ? p_panorama->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "source_panorama", tex_rid); } Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const { @@ -298,16 +305,17 @@ void PanoramaSkyMaterial::_bind_methods() { } PanoramaSkyMaterial::PanoramaSkyMaterial() { - String code = "shader_type sky;\n\n"; + shader = RS::get_singleton()->shader_create(); - code += "uniform sampler2D source_panorama : filter_linear;\n"; - code += "void sky() {\n"; - code += "\tCOLOR = texture(source_panorama, SKY_COORDS).rgb;\n"; - code += "}"; + RS::get_singleton()->shader_set_code(shader, R"( +shader_type sky; - shader = RS::get_singleton()->shader_create(); +uniform sampler2D source_panorama : filter_linear; - RS::get_singleton()->shader_set_code(shader, code); +void sky() { + COLOR = texture(source_panorama, SKY_COORDS).rgb; +} +)"); RS::get_singleton()->material_set_shader(_get_material(), shader); } @@ -412,7 +420,8 @@ float PhysicalSkyMaterial::get_dither_strength() const { void PhysicalSkyMaterial::set_night_sky(const Ref<Texture2D> &p_night_sky) { night_sky = p_night_sky; - RS::get_singleton()->material_set_param(_get_material(), "night_sky", night_sky); + RID tex_rid = p_night_sky.is_valid() ? p_night_sky->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "night_sky", tex_rid); } Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const { @@ -483,103 +492,102 @@ void PhysicalSkyMaterial::_bind_methods() { } PhysicalSkyMaterial::PhysicalSkyMaterial() { - String code = "shader_type sky;\n\n"; - - code += "uniform float rayleigh : hint_range(0, 64) = 2.0;\n"; - code += "uniform vec4 rayleigh_color : hint_color = vec4(0.056, 0.14, 0.3, 1.0);\n"; - code += "uniform float mie : hint_range(0, 1) = 0.005;\n"; - code += "uniform float mie_eccentricity : hint_range(-1, 1) = 0.8;\n"; - code += "uniform vec4 mie_color : hint_color = vec4(0.36, 0.56, 0.82, 1.0);\n\n"; - - code += "uniform float turbidity : hint_range(0, 1000) = 10.0;\n"; - code += "uniform float sun_disk_scale : hint_range(0, 360) = 1.0;\n"; - code += "uniform vec4 ground_color : hint_color = vec4(1.0);\n"; - code += "uniform float exposure : hint_range(0, 128) = 0.1;\n"; - code += "uniform float dither_strength : hint_range(0, 10) = 1.0;\n\n"; - - code += "uniform sampler2D night_sky : hint_black;"; - - code += "const float PI = 3.141592653589793238462643383279502884197169;\n"; - code += "const vec3 UP = vec3( 0.0, 1.0, 0.0 );\n\n"; - - code += "// Sun constants\n"; - code += "const float SUN_ENERGY = 1000.0;\n\n"; - - code += "// optical length at zenith for molecules\n"; - code += "const float rayleigh_zenith_size = 8.4e3;\n"; - code += "const float mie_zenith_size = 1.25e3;\n\n"; - - code += "float henyey_greenstein(float cos_theta, float g) {\n"; - code += "\tconst float k = 0.0795774715459;\n"; - code += "\treturn k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5));\n"; - code += "}\n\n"; - - code += "// From: https://www.shadertoy.com/view/4sfGzS credit to iq\n"; - code += "float hash(vec3 p) {\n"; - code += "\tp = fract( p * 0.3183099 + 0.1 );\n"; - code += "\tp *= 17.0;\n"; - code += "\treturn fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n"; - code += "}\n\n"; - - code += "void sky() {\n"; - code += "\tif (LIGHT0_ENABLED) {\n"; - code += "\t\tfloat zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 );\n"; - code += "\t\tfloat sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY;\n"; - code += "\t\tfloat sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0);\n\n"; - - code += "\t\t// rayleigh coefficients\n"; - code += "\t\tfloat rayleigh_coefficient = rayleigh - ( 1.0 * ( 1.0 - sun_fade ) );\n"; - code += "\t\tvec3 rayleigh_beta = rayleigh_coefficient * rayleigh_color.rgb * 0.0001;\n"; - code += "\t\t// mie coefficients from Preetham\n"; - code += "\t\tvec3 mie_beta = turbidity * mie * mie_color.rgb * 0.000434;\n\n"; - - code += "\t\t// optical length\n"; - code += "\t\tfloat zenith = acos(max(0.0, dot(UP, EYEDIR)));\n"; - code += "\t\tfloat optical_mass = 1.0 / (cos(zenith) + 0.15 * pow(93.885 - degrees(zenith), -1.253));\n"; - code += "\t\tfloat rayleigh_scatter = rayleigh_zenith_size * optical_mass;\n"; - code += "\t\tfloat mie_scatter = mie_zenith_size * optical_mass;\n\n"; - - code += "\t\t// light extinction based on thickness of atmosphere\n"; - code += "\t\tvec3 extinction = exp(-(rayleigh_beta * rayleigh_scatter + mie_beta * mie_scatter));\n\n"; - - code += "\t\t// in scattering\n"; - code += "\t\tfloat cos_theta = dot(EYEDIR, normalize(LIGHT0_DIRECTION));\n\n"; - - code += "\t\tfloat rayleigh_phase = (3.0 / (16.0 * PI)) * (1.0 + pow(cos_theta * 0.5 + 0.5, 2.0));\n"; - code += "\t\tvec3 betaRTheta = rayleigh_beta * rayleigh_phase;\n\n"; - - code += "\t\tfloat mie_phase = henyey_greenstein(cos_theta, mie_eccentricity);\n"; - code += "\t\tvec3 betaMTheta = mie_beta * mie_phase;\n\n"; - - code += "\t\tvec3 Lin = pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * (1.0 - extinction), vec3(1.5));\n"; - code += "\t\t// Hack from https://github.com/mrdoob/three.js/blob/master/examples/jsm/objects/Sky.js\n"; - code += "\t\tLin *= mix(vec3(1.0), pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * extinction, vec3(0.5)), clamp(pow(1.0 - zenith_angle, 5.0), 0.0, 1.0));\n\n"; - - code += "\t\t// Hack in the ground color\n"; - code += "\t\tLin *= mix(ground_color.rgb, vec3(1.0), smoothstep(-0.1, 0.1, dot(UP, EYEDIR)));\n\n"; - - code += "\t\t// Solar disk and out-scattering\n"; - code += "\t\tfloat sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale);\n"; - code += "\t\tfloat sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5);\n"; - code += "\t\tfloat sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta);\n"; - code += "\t\tvec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR;\n"; - code += "\t\tL0 += texture(night_sky, SKY_COORDS).xyz * extinction;\n\n"; - - code += "\t\tvec3 color = (Lin + L0) * 0.04;\n"; - code += "\t\tCOLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade))));\n"; - code += "\t\tCOLOR *= exposure;\n"; - code += "\t\t// Make optional, eliminates banding\n"; - code += "\t\tCOLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength;\n"; - code += "\t} else {\n"; - code += "\t\t// There is no sun, so display night_sky and nothing else\n"; - code += "\t\tCOLOR = texture(night_sky, SKY_COORDS).xyz * 0.04;\n"; - code += "\t\tCOLOR *= exposure;\n"; - code += "\t}\n"; - code += "}\n"; - shader = RS::get_singleton()->shader_create(); - RS::get_singleton()->shader_set_code(shader, code); + RS::get_singleton()->shader_set_code(shader, R"( +shader_type sky; + +uniform float rayleigh : hint_range(0, 64) = 2.0; +uniform vec4 rayleigh_color : hint_color = vec4(0.056, 0.14, 0.3, 1.0); +uniform float mie : hint_range(0, 1) = 0.005; +uniform float mie_eccentricity : hint_range(-1, 1) = 0.8; +uniform vec4 mie_color : hint_color = vec4(0.36, 0.56, 0.82, 1.0); + +uniform float turbidity : hint_range(0, 1000) = 10.0; +uniform float sun_disk_scale : hint_range(0, 360) = 1.0; +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; + +const vec3 UP = vec3( 0.0, 1.0, 0.0 ); + +// Sun constants +const float SUN_ENERGY = 1000.0; + +// Optical length at zenith for molecules. +const float rayleigh_zenith_size = 8.4e3; +const float mie_zenith_size = 1.25e3; + +float henyey_greenstein(float cos_theta, float g) { + const float k = 0.0795774715459; + return k * (1.0 - g * g) / (pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5)); +} + +// From: https://www.shadertoy.com/view/4sfGzS credit to iq +float hash(vec3 p) { + p = fract( p * 0.3183099 + 0.1 ); + p *= 17.0; + return fract(p.x * p.y * p.z * (p.x + p.y + p.z)); +} + +void sky() { + if (LIGHT0_ENABLED) { + float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 ); + float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY; + float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0); + + // Rayleigh coefficients. + float rayleigh_coefficient = rayleigh - ( 1.0 * ( 1.0 - sun_fade ) ); + vec3 rayleigh_beta = rayleigh_coefficient * rayleigh_color.rgb * 0.0001; + // mie coefficients from Preetham + vec3 mie_beta = turbidity * mie * mie_color.rgb * 0.000434; + + // Optical length. + float zenith = acos(max(0.0, dot(UP, EYEDIR))); + float optical_mass = 1.0 / (cos(zenith) + 0.15 * pow(93.885 - degrees(zenith), -1.253)); + float rayleigh_scatter = rayleigh_zenith_size * optical_mass; + float mie_scatter = mie_zenith_size * optical_mass; + + // Light extinction based on thickness of atmosphere. + vec3 extinction = exp(-(rayleigh_beta * rayleigh_scatter + mie_beta * mie_scatter)); + + // In scattering. + float cos_theta = dot(EYEDIR, normalize(LIGHT0_DIRECTION)); + + float rayleigh_phase = (3.0 / (16.0 * PI)) * (1.0 + pow(cos_theta * 0.5 + 0.5, 2.0)); + vec3 betaRTheta = rayleigh_beta * rayleigh_phase; + + float mie_phase = henyey_greenstein(cos_theta, mie_eccentricity); + vec3 betaMTheta = mie_beta * mie_phase; + + vec3 Lin = pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * (1.0 - extinction), vec3(1.5)); + // Hack from https://github.com/mrdoob/three.js/blob/master/examples/jsm/objects/Sky.js + Lin *= mix(vec3(1.0), pow(sun_energy * ((betaRTheta + betaMTheta) / (rayleigh_beta + mie_beta)) * extinction, vec3(0.5)), clamp(pow(1.0 - zenith_angle, 5.0), 0.0, 1.0)); + + // Hack in the ground color. + Lin *= mix(ground_color.rgb, vec3(1.0), smoothstep(-0.1, 0.1, dot(UP, EYEDIR))); + + // Solar disk and out-scattering. + float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale); + float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5); + float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta); + vec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR; + L0 += texture(night_sky, SKY_COORDS).xyz * extinction; + + vec3 color = (Lin + L0) * 0.04; + COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade)))); + COLOR *= exposure; + // Make optional, eliminates banding. + COLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength; + } else { + // There is no sun, so display night_sky and nothing else. + COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04; + COLOR *= exposure; + } +} +)"); RS::get_singleton()->material_set_shader(_get_material(), shader); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 90c702081b..df80084c5c 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -228,7 +228,7 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_frames"), &SpriteFrames::_set_frames); ClassDB::bind_method(D_METHOD("_get_frames"), &SpriteFrames::_get_frames); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", 0), "_set_frames", "_get_frames"); //compatibility + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "frames", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_frames", "_get_frames"); //compatibility ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 2159f1bc97..87371224e0 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -121,7 +121,6 @@ void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) { } else { region_rect = Rect2(Point2(), texture->get_size()); } - emit_signal("texture_changed"); emit_changed(); } @@ -285,8 +284,6 @@ void StyleBoxTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode); ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode); - ADD_SIGNAL(MethodInfo("texture_changed")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_GROUP("Margin", "margin_"); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index ff682a40f4..9f8c35b668 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -35,6 +35,7 @@ SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr; SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr; +SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr; SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr; SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr; @@ -369,13 +370,13 @@ Array SurfaceTool::commit_to_arrays() { for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - w[idx + 0] = v.tangent.x; - w[idx + 1] = v.tangent.y; - w[idx + 2] = v.tangent.z; + w[idx * 4 + 0] = v.tangent.x; + w[idx * 4 + 1] = v.tangent.y; + w[idx * 4 + 2] = v.tangent.z; //float d = v.tangent.dot(v.binormal,v.normal); float d = v.binormal.dot(v.normal.cross(v.tangent)); - w[idx + 3] = d < 0 ? -1 : 1; + w[idx * 4 + 3] = d < 0 ? -1 : 1; } a[i] = array; @@ -536,12 +537,16 @@ Array SurfaceTool::commit_to_arrays() { int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; Vector<int> array; array.resize(varr_len * count); + array.fill(0); int *w = array.ptrw(); for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - ERR_CONTINUE(v.bones.size() != count); + if (v.bones.size() > count) { + ERR_PRINT_ONCE(vformat("Invalid bones size %d vs count %d", v.bones.size(), count)); + continue; + } for (int j = 0; j < count; j++) { w[idx * count + j] = v.bones[j]; @@ -556,12 +561,16 @@ Array SurfaceTool::commit_to_arrays() { int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; array.resize(varr_len * count); + array.fill(0.0f); float *w = array.ptrw(); for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - ERR_CONTINUE(v.weights.size() != count); + if (v.weights.size() > count) { + ERR_PRINT_ONCE(vformat("Invalid weight size %d vs count %d", v.weights.size(), count)); + continue; + } for (int j = 0; j < count; j++) { w[idx * count + j] = v.weights[j]; @@ -598,7 +607,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_ if (p_existing.is_valid()) { mesh = p_existing; } else { - mesh.instance(); + mesh.instantiate(); } int varr_len = vertex_array.size(); @@ -856,7 +865,7 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur _create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format); } -void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) { +void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform) { ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::append_from() must be a valid object of type Mesh"); if (vertex_array.size() == 0) { diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 28addf2245..bde6702759 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -35,8 +35,8 @@ #include "scene/resources/mesh.h" #include "thirdparty/misc/mikktspace.h" -class SurfaceTool : public Reference { - GDCLASS(SurfaceTool, Reference); +class SurfaceTool : public RefCounted { + GDCLASS(SurfaceTool, RefCounted); public: struct Vertex { @@ -78,6 +78,8 @@ public: static OptimizeVertexCacheFunc optimize_vertex_cache_func; typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *r_error); static SimplifyFunc simplify_func; + typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float *result_error, const float *attributes, const float *attribute_weights, size_t attribute_count); + static SimplifyWithAttribFunc simplify_with_attrib_func; typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride); static SimplifyScaleFunc simplify_scale_func; typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error); @@ -184,7 +186,7 @@ public: Array commit_to_arrays(); void create_from(const Ref<Mesh> &p_existing, int p_surface); void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name); - void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform); + void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform); Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = 0); SurfaceTool(); diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index 9dd00849f4..bf889d7a1c 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -405,7 +405,7 @@ void CodeHighlighter::_clear_highlighting_cache() { } void CodeHighlighter::_update_cache() { - font_color = text_edit->get_theme_color("font_color"); + font_color = text_edit->get_theme_color(SNAME("font_color")); } void CodeHighlighter::add_keyword_color(const String &p_keyword, const Color &p_color) { diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp index b71909b6bb..33bb0a83e9 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -30,7 +30,7 @@ #include "text_file.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" bool TextFile::has_text() const { return text != ""; @@ -55,7 +55,7 @@ Error TextFile::load_text(const String &p_path) { ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'."); - uint64_t len = f->get_len(); + uint64_t len = f->get_length(); sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); uint64_t r = f->get_buffer(w, len); diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index 925867a1f2..f1eff6e84f 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -74,7 +74,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags); ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab"), "set_flags", "get_flags"); + 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("get_objects"), &TextLine::get_objects); ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect); diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 74d4f2c32c..1b5c1a3123 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -36,8 +36,8 @@ /*************************************************************************/ -class TextLine : public Reference { - GDCLASS(TextLine, Reference); +class TextLine : public RefCounted { + GDCLASS(TextLine, RefCounted); RID rid; int spacing_top = 0; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 341f5abd80..958c94fe31 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -72,7 +72,7 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags); ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab,Break mandatory,Break words,Break graphemes"), "set_flags", "get_flags"); + 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_width", "width"), &TextParagraph::set_width); ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width); diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 4396b07130..a34e745090 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -36,8 +36,8 @@ /*************************************************************************/ -class TextParagraph : public Reference { - GDCLASS(TextParagraph, Reference); +class TextParagraph : public RefCounted { + GDCLASS(TextParagraph, RefCounted); RID dropcap_rid; int dropcap_lines = 0; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 1b2176d30a..38042d84fd 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -88,7 +88,7 @@ void ImageTexture::reload_from_file() { } Ref<Image> img; - img.instance(); + img.instantiate(); if (ImageLoader::load_image(path, img) == OK) { create_from_image(img); @@ -138,7 +138,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) { } Ref<Image> img; - img.instance(); + img.instantiate(); Error err = ImageLoader::load_image(path, img); ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'."); @@ -173,7 +173,7 @@ Image::Format ImageTexture::get_format() const { return format; } -void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) { +void ImageTexture::update(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image"); ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized."); ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h, @@ -183,11 +183,7 @@ void ImageTexture::update(const Ref<Image> &p_image, bool p_immediate) { ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(), "The new image mipmaps configuration must match the texture's image mipmaps configuration"); - if (p_immediate) { - RenderingServer::get_singleton()->texture_2d_update_immediate(texture, p_image); - } else { - RenderingServer::get_singleton()->texture_2d_update(texture, p_image); - } + RenderingServer::get_singleton()->texture_2d_update(texture, p_image); notify_property_list_changed(); emit_changed(); @@ -258,7 +254,7 @@ bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { decom->decompress(); img = decom; } - alpha_cache.instance(); + alpha_cache.instantiate(); alpha_cache->create_from_image_alpha(img); } } @@ -305,7 +301,7 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image); ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format); - ClassDB::bind_method(D_METHOD("update", "image", "immediate"), &ImageTexture::update, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update); ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook); } @@ -327,7 +323,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit uint32_t mipmaps = f->get_32(); Image::Format format = Image::Format(f->get_32()); - if (data_format == DATA_FORMAT_LOSSLESS || data_format == DATA_FORMAT_LOSSY || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { //look for a PNG or WEBP file inside int sw = w; @@ -360,10 +356,10 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit Ref<Image> img; if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) { img = Image::basis_universal_unpacker(pv); - } else if (data_format == DATA_FORMAT_LOSSLESS) { - img = Image::lossless_unpacker(pv); + } else if (data_format == DATA_FORMAT_PNG) { + img = Image::png_unpacker(pv); } else { - img = Image::lossy_unpacker(pv); + img = Image::webp_unpacker(pv); } if (img.is_null() || img->is_empty()) { @@ -390,7 +386,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit //print_line("mipmap read total: " + itos(mipmap_images.size())); Ref<Image> image; - image.instance(); + image.instantiate(); if (mipmap_images.size() == 1) { //only one image (which will most likely be the case anyway for this format) @@ -442,7 +438,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit } Ref<Image> image; - image.instance(); + image.instantiate(); image->create(tw, th, mipmaps - i ? true : false, format, data); @@ -490,7 +486,7 @@ Image::Format StreamTexture2D::get_format() const { return format; } -Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) { +Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) { alpha_cache.unref(); ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER); @@ -511,8 +507,8 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); } - tw_custom = f->get_32(); - th_custom = f->get_32(); + r_width = f->get_32(); + r_height = f->get_32(); uint32_t df = f->get_32(); //data format //skip reserved @@ -551,16 +547,16 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t } Error StreamTexture2D::load(const String &p_path) { - int lw, lh, lwc, lhc; + int lw, lh; Ref<Image> image; - image.instance(); + image.instantiate(); bool request_3d; bool request_normal; bool request_roughness; int mipmap_limit; - Error err = _load_data(p_path, lw, lh, lwc, lhc, image, request_3d, request_normal, request_roughness, mipmap_limit); + Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit); if (err) { return err; } @@ -571,12 +567,12 @@ Error StreamTexture2D::load(const String &p_path) { } else { texture = RS::get_singleton()->texture_2d_create(image); } - if (lwc || lhc) { - RS::get_singleton()->texture_set_size_override(texture, lwc, lhc); + if (lw || lh) { + RS::get_singleton()->texture_set_size_override(texture, lw, lh); } - w = lwc ? lwc : lw; - h = lhc ? lhc : lh; + w = lw; + h = lh; path_to_file = p_path; format = image->get_format(); @@ -679,7 +675,7 @@ bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const { img = decom; } - alpha_cache.instance(); + alpha_cache.instantiate(); alpha_cache->create_from_image_alpha(img); } } @@ -738,7 +734,7 @@ StreamTexture2D::~StreamTexture2D() { RES ResourceFormatLoaderStreamTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<StreamTexture2D> st; - st.instance(); + st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; @@ -1036,7 +1032,7 @@ StreamTexture3D::~StreamTexture3D() { RES ResourceFormatLoaderStreamTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<StreamTexture3D> st; - st.instance(); + st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; @@ -1411,14 +1407,26 @@ void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve); + ClassDB::bind_method(D_METHOD("set_texture_mode", "texture_mode"), &CurveTexture::set_texture_mode); + ClassDB::bind_method(D_METHOD("get_texture_mode"), &CurveTexture::get_texture_mode); + ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "RGB,Red"), "set_texture_mode", "get_texture_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + + BIND_ENUM_CONSTANT(TEXTURE_MODE_RGB); + BIND_ENUM_CONSTANT(TEXTURE_MODE_RED); } void CurveTexture::set_width(int p_width) { ERR_FAIL_COND(p_width < 32 || p_width > 4096); + + if (_width == p_width) { + return; + } + _width = p_width; _update(); } @@ -1454,7 +1462,7 @@ void CurveTexture::set_curve(Ref<Curve> p_curve) { void CurveTexture::_update() { Vector<uint8_t> data; - data.resize(_width * sizeof(float)); + data.resize(_width * sizeof(float) * (texture_mode == TEXTURE_MODE_RGB ? 3 : 1)); // The array is locked in that scope { @@ -1465,24 +1473,42 @@ void CurveTexture::_update() { Curve &curve = **_curve; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i] = curve.interpolate_baked(t); + if (texture_mode == TEXTURE_MODE_RGB) { + wd[i * 3 + 0] = curve.interpolate_baked(t); + wd[i * 3 + 1] = wd[i * 3 + 0]; + wd[i * 3 + 2] = wd[i * 3 + 0]; + } else { + wd[i] = curve.interpolate_baked(t); + } } } else { for (int i = 0; i < _width; ++i) { - wd[i] = 0; + if (texture_mode == TEXTURE_MODE_RGB) { + wd[i * 3 + 0] = 0; + wd[i * 3 + 1] = 0; + wd[i * 3 + 2] = 0; + } else { + wd[i] = 0; + } } } } - Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data)); + Ref<Image> image = memnew(Image(_width, 1, false, texture_mode == TEXTURE_MODE_RGB ? Image::FORMAT_RGBF : Image::FORMAT_RF, data)); if (_texture.is_valid()) { - RID new_texture = RS::get_singleton()->texture_2d_create(image); - RS::get_singleton()->texture_replace(_texture, new_texture); + if (_current_texture_mode != texture_mode || _current_width != _width) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(_texture, new_texture); + } else { + RS::get_singleton()->texture_2d_update(_texture, image); + } } else { _texture = RS::get_singleton()->texture_2d_create(image); } + _current_texture_mode = texture_mode; + _current_width = _width; emit_changed(); } @@ -1491,6 +1517,17 @@ Ref<Curve> CurveTexture::get_curve() const { return _curve; } +void CurveTexture::set_texture_mode(TextureMode p_mode) { + if (texture_mode == p_mode) { + return; + } + texture_mode = p_mode; + _update(); +} +CurveTexture::TextureMode CurveTexture::get_texture_mode() const { + return texture_mode; +} + RID CurveTexture::get_rid() const { if (!_texture.is_valid()) { _texture = RS::get_singleton()->texture_2d_placeholder_create(); @@ -1508,6 +1545,204 @@ CurveTexture::~CurveTexture() { ////////////////// +void CurveXYZTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveXYZTexture::set_width); + + ClassDB::bind_method(D_METHOD("set_curve_x", "curve"), &CurveXYZTexture::set_curve_x); + ClassDB::bind_method(D_METHOD("get_curve_x"), &CurveXYZTexture::get_curve_x); + + ClassDB::bind_method(D_METHOD("set_curve_y", "curve"), &CurveXYZTexture::set_curve_y); + ClassDB::bind_method(D_METHOD("get_curve_y"), &CurveXYZTexture::get_curve_y); + + ClassDB::bind_method(D_METHOD("set_curve_z", "curve"), &CurveXYZTexture::set_curve_z); + ClassDB::bind_method(D_METHOD("get_curve_z"), &CurveXYZTexture::get_curve_z); + + ClassDB::bind_method(D_METHOD("_update"), &CurveXYZTexture::_update); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_x", "get_curve_x"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_y", "get_curve_y"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_z", "get_curve_z"); +} + +void CurveXYZTexture::set_width(int p_width) { + ERR_FAIL_COND(p_width < 32 || p_width > 4096); + + if (_width == p_width) { + return; + } + + _width = p_width; + _update(); +} + +int CurveXYZTexture::get_width() const { + return _width; +} + +void CurveXYZTexture::ensure_default_setup(float p_min, float p_max) { + if (_curve_x.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_x(curve); + } + + if (_curve_y.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_y(curve); + } + + if (_curve_z.is_null()) { + Ref<Curve> curve = Ref<Curve>(memnew(Curve)); + curve->add_point(Vector2(0, 1)); + curve->add_point(Vector2(1, 1)); + curve->set_min_value(p_min); + curve->set_max_value(p_max); + set_curve_z(curve); + } +} + +void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) { + if (_curve_x != p_curve) { + if (_curve_x.is_valid()) { + _curve_x->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + } + _curve_x = p_curve; + if (_curve_x.is_valid()) { + _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) { + if (_curve_y != p_curve) { + if (_curve_y.is_valid()) { + _curve_y->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + } + _curve_y = p_curve; + if (_curve_y.is_valid()) { + _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) { + if (_curve_z != p_curve) { + if (_curve_z.is_valid()) { + _curve_z->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update)); + } + _curve_z = p_curve; + if (_curve_z.is_valid()) { + _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), varray(), CONNECT_REFERENCE_COUNTED); + } + _update(); + } +} + +void CurveXYZTexture::_update() { + Vector<uint8_t> data; + data.resize(_width * sizeof(float) * 3); + + // The array is locked in that scope + { + uint8_t *wd8 = data.ptrw(); + float *wd = (float *)wd8; + + if (_curve_x.is_valid()) { + Curve &curve_x = **_curve_x; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 0] = curve_x.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 0] = 0; + } + } + + if (_curve_y.is_valid()) { + Curve &curve_y = **_curve_y; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 1] = curve_y.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 1] = 0; + } + } + + if (_curve_z.is_valid()) { + Curve &curve_z = **_curve_z; + for (int i = 0; i < _width; ++i) { + float t = i / static_cast<float>(_width); + wd[i * 3 + 2] = curve_z.interpolate_baked(t); + } + + } else { + for (int i = 0; i < _width; ++i) { + wd[i * 3 + 2] = 0; + } + } + } + + Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RGBF, data)); + + if (_texture.is_valid()) { + if (_current_width != _width) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(_texture, new_texture); + } else { + RS::get_singleton()->texture_2d_update(_texture, image); + } + } else { + _texture = RS::get_singleton()->texture_2d_create(image); + } + _current_width = _width; + + emit_changed(); +} + +Ref<Curve> CurveXYZTexture::get_curve_x() const { + return _curve_x; +} + +Ref<Curve> CurveXYZTexture::get_curve_y() const { + return _curve_y; +} + +Ref<Curve> CurveXYZTexture::get_curve_z() const { + return _curve_z; +} + +RID CurveXYZTexture::get_rid() const { + if (!_texture.is_valid()) { + _texture = RS::get_singleton()->texture_2d_placeholder_create(); + } + return _texture; +} + +CurveXYZTexture::CurveXYZTexture() {} + +CurveXYZTexture::~CurveXYZTexture() { + if (_texture.is_valid()) { + RS::get_singleton()->free(_texture); + } +} + +////////////////// + GradientTexture::GradientTexture() { _queue_update(); } @@ -1527,7 +1762,7 @@ void GradientTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); } void GradientTexture::set_gradient(Ref<Gradient> p_gradient) { @@ -1555,7 +1790,7 @@ void GradientTexture::_queue_update() { } update_pending = true; - call_deferred("_update"); + call_deferred(SNAME("_update")); } void GradientTexture::_update() { @@ -1595,6 +1830,7 @@ void GradientTexture::_update() { } void GradientTexture::set_width(int p_width) { + ERR_FAIL_COND(p_width <= 0); width = p_width; _queue_update(); } @@ -1876,7 +2112,7 @@ void AnimatedTexture::_validate_property(PropertyInfo &property) const { if (prop.begins_with("frame_")) { int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); if (frame >= frame_count) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } } @@ -1904,7 +2140,7 @@ void AnimatedTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay); ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", 0), "set_current_frame", "get_current_frame"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "oneshot"), "set_oneshot", "get_oneshot"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); @@ -2257,15 +2493,15 @@ RES ResourceFormatLoaderStreamTextureLayered::load(const String &p_path, const S Ref<StreamTextureLayered> st; if (p_path.get_extension().to_lower() == "stexarray") { Ref<StreamTexture2DArray> s; - s.instance(); + s.instantiate(); st = s; } else if (p_path.get_extension().to_lower() == "scube") { Ref<StreamCubemap> s; - s.instance(); + s.instantiate(); st = s; } else if (p_path.get_extension().to_lower() == "scubearray") { Ref<StreamCubemapArray> s; - s.instance(); + s.instantiate(); st = s; } else { if (r_error) { diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 264d85d187..98aa61138d 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -31,10 +31,10 @@ #ifndef TEXTURE_H #define TEXTURE_H +#include "core/io/file_access.h" #include "core/io/resource.h" #include "core/io/resource_loader.h" #include "core/math/rect2.h" -#include "core/os/file_access.h" #include "core/os/mutex.h" #include "core/os/rw_lock.h" #include "core/os/thread_safe.h" @@ -107,7 +107,7 @@ public: Image::Format get_format() const; - void update(const Ref<Image> &p_image, bool p_immediate = false); + void update(const Ref<Image> &p_image); Ref<Image> get_image() const override; int get_width() const override; @@ -136,8 +136,8 @@ class StreamTexture2D : public Texture2D { public: enum DataFormat { DATA_FORMAT_IMAGE, - DATA_FORMAT_LOSSLESS, - DATA_FORMAT_LOSSY, + DATA_FORMAT_PNG, + DATA_FORMAT_WEBP, DATA_FORMAT_BASIS_UNIVERSAL, }; @@ -146,9 +146,6 @@ public: }; enum FormatBits { - FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1, - FORMAT_BIT_LOSSLESS = 1 << 20, - FORMAT_BIT_LOSSY = 1 << 21, FORMAT_BIT_STREAM = 1 << 22, FORMAT_BIT_HAS_MIPMAPS = 1 << 23, FORMAT_BIT_DETECT_3D = 1 << 24, @@ -158,7 +155,7 @@ public: }; private: - Error _load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0); + Error _load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0); String path_to_file; mutable RID texture; Image::Format format = Image::FORMAT_MAX; @@ -389,8 +386,8 @@ class StreamTextureLayered : public TextureLayered { public: enum DataFormat { DATA_FORMAT_IMAGE, - DATA_FORMAT_LOSSLESS, - DATA_FORMAT_LOSSY, + DATA_FORMAT_PNG, + DATA_FORMAT_WEBP, DATA_FORMAT_BASIS_UNIVERSAL, }; @@ -399,9 +396,6 @@ public: }; enum FormatBits { - FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1, - FORMAT_BIT_LOSSLESS = 1 << 20, - FORMAT_BIT_LOSSY = 1 << 21, FORMAT_BIT_STREAM = 1 << 22, FORMAT_BIT_HAS_MIPMAPS = 1 << 23, }; @@ -532,8 +526,8 @@ class StreamTexture3D : public Texture3D { public: enum DataFormat { DATA_FORMAT_IMAGE, - DATA_FORMAT_LOSSLESS, - DATA_FORMAT_LOSSY, + DATA_FORMAT_PNG, + DATA_FORMAT_WEBP, DATA_FORMAT_BASIS_UNIVERSAL, }; @@ -542,9 +536,6 @@ public: }; enum FormatBits { - FORMAT_MASK_IMAGE_FORMAT = (1 << 20) - 1, - FORMAT_BIT_LOSSLESS = 1 << 20, - FORMAT_BIT_LOSSY = 1 << 21, FORMAT_BIT_STREAM = 1 << 22, FORMAT_BIT_HAS_MIPMAPS = 1 << 23, }; @@ -595,11 +586,19 @@ public: class CurveTexture : public Texture2D { GDCLASS(CurveTexture, Texture2D); RES_BASE_EXTENSION("curvetex") +public: + enum TextureMode { + TEXTURE_MODE_RGB, + TEXTURE_MODE_RED, + }; private: mutable RID _texture; Ref<Curve> _curve; int _width = 2048; + int _current_width = 0; + TextureMode texture_mode = TEXTURE_MODE_RGB; + TextureMode _current_texture_mode = TEXTURE_MODE_RGB; void _update(); @@ -610,6 +609,9 @@ public: void set_width(int p_width); int get_width() const override; + void set_texture_mode(TextureMode p_mode); + TextureMode get_texture_mode() const; + void ensure_default_setup(float p_min = 0, float p_max = 1); void set_curve(Ref<Curve> p_curve); @@ -623,18 +625,49 @@ public: CurveTexture(); ~CurveTexture(); }; -/* - enum CubeMapSide { - CUBEMAP_LEFT, - CUBEMAP_RIGHT, - CUBEMAP_BOTTOM, - CUBEMAP_TOP, - CUBEMAP_FRONT, - CUBEMAP_BACK, - }; -*/ -//VARIANT_ENUM_CAST( Texture::CubeMapSide ); +VARIANT_ENUM_CAST(CurveTexture::TextureMode) + +class CurveXYZTexture : public Texture2D { + GDCLASS(CurveXYZTexture, Texture2D); + RES_BASE_EXTENSION("curvetex") + +private: + mutable RID _texture; + Ref<Curve> _curve_x; + Ref<Curve> _curve_y; + Ref<Curve> _curve_z; + int _width = 2048; + int _current_width = 0; + + void _update(); + +protected: + static void _bind_methods(); + +public: + void set_width(int p_width); + int get_width() const override; + + void ensure_default_setup(float p_min = 0, float p_max = 1); + + void set_curve_x(Ref<Curve> p_curve); + Ref<Curve> get_curve_x() const; + + void set_curve_y(Ref<Curve> p_curve); + Ref<Curve> get_curve_y() const; + + void set_curve_z(Ref<Curve> p_curve); + Ref<Curve> get_curve_z() const; + + virtual RID get_rid() const override; + + virtual int get_height() const override { return 1; } + virtual bool has_alpha() const override { return false; } + + CurveXYZTexture(); + ~CurveXYZTexture(); +}; class GradientTexture : public Texture2D { GDCLASS(GradientTexture, Texture2D); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 786a96501a..303bbf38f4 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -29,10 +29,15 @@ /*************************************************************************/ #include "theme.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/string/print_string.h" void Theme::_emit_theme_changed() { + if (no_change_propagation) { + return; + } + + notify_property_list_changed(); emit_changed(); } @@ -258,6 +263,21 @@ Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const { return Vector<String>(); } +Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const { + Vector<String> ilret; + List<StringName> il; + + get_type_variation_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + Vector<String> Theme::_get_type_list() const { Vector<String> ilret; List<StringName> il; @@ -287,10 +307,14 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) { set_stylebox(name, theme_type, p_value); } else if (type == "fonts") { set_font(name, theme_type, p_value); + } else if (type == "font_sizes") { + set_font_size(name, theme_type, p_value); } else if (type == "colors") { set_color(name, theme_type, p_value); } else if (type == "constants") { set_constant(name, theme_type, p_value); + } else if (type == "base_type") { + set_type_variation(theme_type, p_value); } else { return false; } @@ -327,10 +351,14 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const { } else { r_ret = get_font(name, theme_type); } + } else if (type == "font_sizes") { + r_ret = get_font_size(name, theme_type); } else if (type == "colors") { r_ret = get_color(name, theme_type); } else if (type == "constants") { r_ret = get_constant(name, theme_type); + } else if (type == "base_type") { + r_ret = get_type_variation_base(theme_type); } else { return false; } @@ -346,6 +374,14 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { const StringName *key = nullptr; + // Type variations. + while ((key = variation_map.next(key))) { + list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type")); + } + + key = nullptr; + + // Icons. while ((key = icon_map.next(key))) { const StringName *key2 = nullptr; @@ -356,6 +392,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { key = nullptr; + // Styles. while ((key = style_map.next(key))) { const StringName *key2 = nullptr; @@ -366,6 +403,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { key = nullptr; + // Fonts. while ((key = font_map.next(key))) { const StringName *key2 = nullptr; @@ -376,6 +414,18 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { key = nullptr; + // Font sizes. + while ((key = font_size_map.next(key))) { + const StringName *key2 = nullptr; + + while ((key2 = font_size_map[*key].next(key2))) { + list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2)); + } + } + + key = nullptr; + + // Colors. while ((key = color_map.next(key))) { const StringName *key2 = nullptr; @@ -386,6 +436,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { key = nullptr; + // Constants. while ((key = constant_map.next(key))) { const StringName *key2 = nullptr; @@ -394,6 +445,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } } + // Sort and store properties. list.sort(); for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { p_list->push_back(E->get()); @@ -415,8 +467,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) { default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } Ref<Font> Theme::get_default_theme_font() const { @@ -430,8 +481,7 @@ void Theme::set_default_theme_font_size(int p_font_size) { default_theme_font_size = p_font_size; - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } int Theme::get_default_theme_font_size() const { @@ -478,8 +528,6 @@ void Theme::set_default_font_size(int p_font_size) { } void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { - bool new_value = !icon_map.has(p_theme_type) || !icon_map[p_theme_type].has(p_name); - if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -490,10 +538,7 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const { @@ -520,8 +565,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; icon_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) { @@ -534,8 +578,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) icon_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -553,6 +596,9 @@ void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) con } void Theme::add_icon_type(const StringName &p_theme_type) { + if (icon_map.has(p_theme_type)) { + return; + } icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>(); } @@ -566,8 +612,6 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const { } void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { - bool new_value = !style_map.has(p_theme_type) || !style_map[p_theme_type].has(p_name); - if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -578,10 +622,7 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - } - emit_changed(); + _emit_theme_changed(); } Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { @@ -608,8 +649,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name]; style_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) { @@ -622,8 +662,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t style_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -641,6 +680,9 @@ void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) } void Theme::add_stylebox_type(const StringName &p_theme_type) { + if (style_map.has(p_theme_type)) { + return; + } style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>(); } @@ -654,8 +696,6 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const { } void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { - bool new_value = !font_map.has(p_theme_type) || !font_map[p_theme_type].has(p_name); - if (font_map[p_theme_type][p_name].is_valid()) { font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -666,10 +706,7 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const { @@ -698,8 +735,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name]; font_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) { @@ -711,8 +747,8 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) } font_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -730,6 +766,9 @@ void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) con } void Theme::add_font_type(const StringName &p_theme_type) { + if (font_map.has(p_theme_type)) { + return; + } font_map[p_theme_type] = HashMap<StringName, Ref<Font>>(); } @@ -743,14 +782,9 @@ void Theme::get_font_type_list(List<StringName> *p_list) const { } void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { - bool new_value = !font_size_map.has(p_theme_type) || !font_size_map[p_theme_type].has(p_name); - font_size_map[p_theme_type][p_name] = p_font_size; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const { @@ -779,8 +813,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name]; font_size_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) { @@ -788,8 +821,8 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_ ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_name), "Cannot clear the font size '" + String(p_name) + "' because it does not exist."); font_size_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -807,6 +840,9 @@ void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list } void Theme::add_font_size_type(const StringName &p_theme_type) { + if (font_size_map.has(p_theme_type)) { + return; + } font_size_map[p_theme_type] = HashMap<StringName, int>(); } @@ -820,14 +856,9 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const { } void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { - bool new_value = !color_map.has(p_theme_type) || !color_map[p_theme_type].has(p_name); - color_map[p_theme_type][p_name] = p_color; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const { @@ -854,8 +885,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name]; color_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) { @@ -863,8 +893,8 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist."); color_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -882,6 +912,9 @@ void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) co } void Theme::add_color_type(const StringName &p_theme_type) { + if (color_map.has(p_theme_type)) { + return; + } color_map[p_theme_type] = HashMap<StringName, Color>(); } @@ -895,13 +928,9 @@ void Theme::get_color_type_list(List<StringName> *p_list) const { } void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { - bool new_value = !constant_map.has(p_theme_type) || !constant_map[p_theme_type].has(p_name); constant_map[p_theme_type][p_name] = p_constant; - if (new_value) { - notify_property_list_changed(); - emit_changed(); - } + _emit_theme_changed(); } int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -928,8 +957,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name]; constant_map[p_theme_type].erase(p_old_name); - notify_property_list_changed(); - emit_changed(); + _emit_theme_changed(); } void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) { @@ -937,8 +965,8 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist."); constant_map[p_theme_type].erase(p_name); - notify_property_list_changed(); - emit_changed(); + + _emit_theme_changed(); } void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { @@ -956,6 +984,9 @@ void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) } void Theme::add_constant_type(const StringName &p_theme_type) { + if (constant_map.has(p_theme_type)) { + return; + } constant_map[p_theme_type] = HashMap<StringName, int>(); } @@ -1199,8 +1230,74 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l } } +void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) { + ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type."); + ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type."); + ERR_FAIL_COND_MSG(p_base_type == StringName(), "An empty theme type cannot be the base type of a variation. Use clear_type_variation() instead if you want to unmark '" + String(p_theme_type) + "' as a variation."); + + if (variation_map.has(p_theme_type)) { + StringName old_base = variation_map[p_theme_type]; + variation_base_map[old_base].erase(p_theme_type); + } + + variation_map[p_theme_type] = p_base_type; + variation_base_map[p_base_type].push_back(p_theme_type); + + _emit_theme_changed(); +} + +bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const { + return (variation_map.has(p_theme_type) && variation_map[p_theme_type] == p_base_type); +} + +void Theme::clear_type_variation(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!variation_map.has(p_theme_type), "Cannot clear the type variation '" + String(p_theme_type) + "' because it does not exist."); + + StringName base_type = variation_map[p_theme_type]; + variation_base_map[base_type].erase(p_theme_type); + variation_map.erase(p_theme_type); + + _emit_theme_changed(); +} + +StringName Theme::get_type_variation_base(const StringName &p_theme_type) const { + if (!variation_map.has(p_theme_type)) { + return StringName(); + } + + return variation_map[p_theme_type]; +} + +void Theme::get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const { + ERR_FAIL_NULL(p_list); + + if (!variation_base_map.has(p_base_type)) { + return; + } + + for (const List<StringName>::Element *E = variation_base_map[p_base_type].front(); E; E = E->next()) { + // Prevent infinite loops if variants were set to be cross-dependent (that's still invalid usage, but handling for stability sake). + if (p_list->find(E->get())) { + continue; + } + + p_list->push_back(E->get()); + // Continue looking for sub-variations. + get_type_variation_list(E->get(), p_list); + } +} + +void Theme::_freeze_change_propagation() { + no_change_propagation = true; +} + +void Theme::_unfreeze_and_propagate_changes() { + no_change_propagation = false; + _emit_theme_changed(); +} + void Theme::clear() { - //these need disconnecting + // These items need disconnecting. { const StringName *K = nullptr; while ((K = icon_map.next(K))) { @@ -1243,11 +1340,14 @@ void Theme::clear() { icon_map.clear(); style_map.clear(); font_map.clear(); + font_size_map.clear(); color_map.clear(); constant_map.clear(); - notify_property_list_changed(); - emit_changed(); + variation_map.clear(); + variation_base_map.clear(); + + _emit_theme_changed(); } void Theme::copy_default_theme() { @@ -1261,6 +1361,8 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { return; } + _freeze_change_propagation(); + // These items need reconnecting, so add them normally. { const StringName *K = nullptr; @@ -1297,8 +1399,10 @@ void Theme::copy_theme(const Ref<Theme> &p_other) { color_map = p_other->color_map; constant_map = p_other->constant_map; - notify_property_list_changed(); - emit_changed(); + variation_map = p_other->variation_map; + variation_base_map = p_other->variation_base_map; + + _unfreeze_and_propagate_changes(); } void Theme::get_type_list(List<StringName> *p_list) const { @@ -1307,30 +1411,42 @@ void Theme::get_type_list(List<StringName> *p_list) const { Set<StringName> types; const StringName *key = nullptr; + // Icons. while ((key = icon_map.next(key))) { types.insert(*key); } key = nullptr; + // StyleBoxes. while ((key = style_map.next(key))) { types.insert(*key); } key = nullptr; + // Fonts. while ((key = font_map.next(key))) { types.insert(*key); } key = nullptr; + // Font sizes. + while ((key = font_size_map.next(key))) { + types.insert(*key); + } + + key = nullptr; + + // Colors. while ((key = color_map.next(key))) { types.insert(*key); } key = nullptr; + // Constants. while ((key = constant_map.next(key))) { types.insert(*key); } @@ -1340,10 +1456,25 @@ void Theme::get_type_list(List<StringName> *p_list) const { } } -void Theme::get_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) { +void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { ERR_FAIL_NULL(p_list); - StringName class_name = p_theme_type; + // Build the dependency chain for type variations. + if (p_type_variation != StringName()) { + StringName variation_name = p_type_variation; + while (variation_name != StringName()) { + p_list->push_back(variation_name); + variation_name = get_type_variation_base(variation_name); + + // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme). + if (variation_name == p_base_type) { + break; + } + } + } + + // Continue building the chain using native class hierarchy. + StringName class_name = p_base_type; while (class_name != StringName()) { p_list->push_back(class_name); class_name = ClassDB::get_parent_class_nocheck(class_name); @@ -1353,6 +1484,7 @@ void Theme::get_type_dependencies(const StringName &p_theme_type, List<StringNam void Theme::reset_state() { clear(); } + void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("set_icon", "name", "theme_type", "texture"), &Theme::set_icon); ClassDB::bind_method(D_METHOD("get_icon", "name", "theme_type"), &Theme::get_icon); @@ -1418,6 +1550,12 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "theme_type"), &Theme::_get_theme_item_list); ClassDB::bind_method(D_METHOD("get_theme_item_type_list", "data_type"), &Theme::_get_theme_item_type_list); + ClassDB::bind_method(D_METHOD("set_type_variation", "theme_type", "base_type"), &Theme::set_type_variation); + ClassDB::bind_method(D_METHOD("is_type_variation", "theme_type", "base_type"), &Theme::is_type_variation); + ClassDB::bind_method(D_METHOD("clear_type_variation", "theme_type"), &Theme::clear_type_variation); + ClassDB::bind_method(D_METHOD("get_type_variation_base", "theme_type"), &Theme::get_type_variation_base); + ClassDB::bind_method(D_METHOD("get_type_variation_list", "base_type"), &Theme::_get_type_variation_list); + ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list); ClassDB::bind_method("copy_default_theme", &Theme::copy_default_theme); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 4de1f065e1..8a8fc28be1 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -41,6 +41,12 @@ class Theme : public Resource { GDCLASS(Theme, Resource); RES_BASE_EXTENSION("theme"); +#ifdef TOOLS_ENABLED + friend class ThemeItemImportTree; + friend class ThemeItemEditorDialog; + friend class ThemeTypeEditor; +#endif + public: enum DataType { DATA_TYPE_COLOR, @@ -53,6 +59,8 @@ public: }; private: + bool no_change_propagation = false; + void _emit_theme_changed(); HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; @@ -61,6 +69,8 @@ private: HashMap<StringName, HashMap<StringName, int>> font_size_map; HashMap<StringName, HashMap<StringName, Color>> color_map; HashMap<StringName, HashMap<StringName, int>> constant_map; + HashMap<StringName, StringName> variation_map; + HashMap<StringName, List<StringName>> variation_base_map; Vector<String> _get_icon_list(const String &p_theme_type) const; Vector<String> _get_icon_type_list() const; @@ -77,6 +87,8 @@ private: Vector<String> _get_theme_item_list(DataType p_data_type, const String &p_theme_type) const; Vector<String> _get_theme_item_type_list(DataType p_data_type) const; + + Vector<String> _get_type_variation_list(const StringName &p_theme_type) const; Vector<String> _get_type_list() const; protected: @@ -96,6 +108,9 @@ protected: static void _bind_methods(); + void _freeze_change_propagation(); + void _unfreeze_and_propagate_changes(); + virtual void reset_state() override; public: @@ -186,8 +201,14 @@ public: void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type); void get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const; + void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type); + bool is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const; + void clear_type_variation(const StringName &p_theme_type); + StringName get_type_variation_base(const StringName &p_theme_type) const; + void get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const; + void get_type_list(List<StringName> *p_list) const; - static void get_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list); + 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); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 9db7acb263..deb5a50eb2 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,7 +30,10 @@ #include "tile_set.h" +#include "core/core_string_names.h" #include "core/math/geometry_2d.h" +#include "core/templates/local_vector.h" + #include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -38,6 +41,25 @@ /////////////////////////////// TileSet ////////////////////////////////////// +const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { + "right_side", + "right_corner", + "bottom_right_side", + "bottom_right_corner", + "bottom_side", + "bottom_corner", + "bottom_left_side", + "bottom_left_corner", + "left_side", + "left_corner", + "top_left_side", + "top_left_corner", + "top_side", + "top_corner", + "top_right_side", + "top_right_corner" +}; + // --- Plugins --- Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { return tile_set_plugins_vector; @@ -51,6 +73,8 @@ void TileSet::set_tile_shape(TileSet::TileShape p_shape) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -72,6 +96,8 @@ void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { @@ -81,20 +107,14 @@ TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { void TileSet::set_tile_size(Size2i p_size) { ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); tile_size = p_size; + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } Size2i TileSet::get_tile_size() const { return tile_size; } -void TileSet::set_tile_skew(Vector2 p_skew) { - emit_changed(); - tile_skew = p_skew; -} -Vector2 TileSet::get_tile_skew() const { - return tile_skew; -} - int TileSet::get_next_source_id() const { return next_source_id; } @@ -117,7 +137,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source p_tile_set_source->set_tile_set(this); _compute_next_source_id(); - sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); emit_changed(); @@ -127,7 +147,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source void TileSet::remove_source(int p_source_id) { ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); - sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); sources[p_source_id]->set_tile_set(nullptr); sources.erase(p_source_id); @@ -240,80 +260,6 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { - // TODO: optimize this with 2D meshes when they work again. - if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { - if (p_filled && p_texture.is_valid()) { - p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color); - } else { - p_canvas_item->draw_rect(p_region, p_color, p_filled); - } - } else { - float overlap = 0.0; - switch (get_tile_shape()) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - - Vector<Vector2> uvs; - uvs.append(Vector2(0.5, 0.0)); - uvs.append(Vector2(0.0, overlap)); - uvs.append(Vector2(0.0, 1.0 - overlap)); - uvs.append(Vector2(0.5, 1.0)); - uvs.append(Vector2(1.0, 1.0 - overlap)); - uvs.append(Vector2(1.0, overlap)); - uvs.append(Vector2(0.5, 0.0)); - if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); - } - } - - Vector<Vector2> points; - for (int i = 0; i < uvs.size(); i++) { - points.append(p_region.position + uvs[i] * p_region.size); - } - - if (p_filled) { - // This does hurt performances a lot. We should use a mesh if possible instead. - p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture); - - // Should improve performances, but does not work as draw_primitive does not work with textures :/ : - /*for (int i = 0; i < 6; i += 3) { - Vector<Vector2> quad; - quad.append(points[i]); - quad.append(points[(i + 1) % points.size()]); - quad.append(points[(i + 2) % points.size()]); - quad.append(points[(i + 3) % points.size()]); - - Vector<Vector2> uv_quad; - uv_quad.append(uvs[i]); - uv_quad.append(uvs[(i + 1) % uvs.size()]); - uv_quad.append(uvs[(i + 2) % uvs.size()]); - uv_quad.append(uvs[(i + 3) % uvs.size()]); - - p_control->draw_primitive(quad, Vector<Color>(), uv_quad, p_texture); - }*/ - - } else { - // This does hurt performances a lot. We should use a mesh if possible instead. - // tile_shape_grid->draw_polyline(points, p_color); - for (int i = 0; i < points.size() - 1; i++) { - p_canvas_item->draw_line(points[i], points[i + 1], p_color); - } - } - } -} - // Physics void TileSet::set_physics_layers_count(int p_physics_layers_count) { ERR_FAIL_COND(p_physics_layers_count < 0); @@ -459,14 +405,9 @@ Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const { return terrain_sets[p_terrain_set].terrains[p_terrain_index].color; } -bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { - if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { - return false; - } - - TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); +bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || @@ -474,7 +415,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || @@ -483,7 +424,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || @@ -491,7 +432,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || @@ -501,7 +442,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } else { if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -511,7 +452,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -522,7 +463,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -532,7 +473,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -547,6 +488,15 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return false; } +bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { + if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { + return false; + } + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit); +} + // Navigation void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { ERR_FAIL_COND(p_navigation_layers_count < 0); @@ -657,9 +607,926 @@ Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { return custom_data_layers[p_layer_id].type; } +Vector<Vector2> TileSet::get_tile_shape_polygon() { + Vector<Vector2> points; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + points.append(Vector2(0.0, 0.0)); + points.append(Vector2(1.0, 0.0)); + points.append(Vector2(1.0, 1.0)); + points.append(Vector2(0.0, 1.0)); + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + + points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, overlap)); + points.append(Vector2(0.0, 1.0 - overlap)); + points.append(Vector2(0.5, 1.0)); + points.append(Vector2(1.0, 1.0 - overlap)); + points.append(Vector2(1.0, overlap)); + points.append(Vector2(0.5, 0.0)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < points.size(); i++) { + points.write[i] = Vector2(points[i].y, points[i].x); + } + } + } + for (int i = 0; i < points.size(); i++) { + points.write[i] = points[i] * tile_size - tile_size / 2; + } + return points; +} + +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { + if (tile_meshes_dirty) { + Vector<Vector2> uvs = get_tile_shape_polygon(); + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + } + + Vector<Color> colors; + colors.resize(uvs.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + + // Filled mesh. + tile_filled_mesh->clear_surfaces(); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + // Lines mesh. + tile_lines_mesh->clear_surfaces(); + a.clear(); + a.resize(Mesh::ARRAY_MAX); + // Add the first point again when drawing lines. + uvs.push_back(uvs[0]); + colors.push_back(colors[0]); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + tile_meshes_dirty = false; + } + + Transform2D xform; + xform.scale(p_region.size); + xform.set_origin(p_region.get_position()); + if (p_filled) { + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + } else { + p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color); + } +} + +Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { + ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>()); + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_square_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_square_side_terrain_bit_polygon(tile_size, p_bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } + } +} + +#define TERRAIN_ALPHA 0.6 + +void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data) { + ERR_FAIL_COND(!p_tile_data); + + if (terrain_bits_meshes_dirty) { + // Recompute the meshes. + terrain_bits_meshes.clear(); + + for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) { + TerrainMode terrain_mode = TerrainMode(terrain_mode_index); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + + if (is_valid_peering_bit_for_mode(terrain_mode, bit)) { + Vector<Vector2> polygon; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_square_side_terrain_bit_polygon(tile_size, bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } + } + + Ref<ArrayMesh> mesh; + mesh.instantiate(); + Vector<Vector2> uvs; + uvs.resize(polygon.size()); + Vector<Color> colors; + colors.resize(polygon.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = polygon; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + terrain_bits_meshes[terrain_mode][bit] = mesh; + } + } + } + terrain_bits_meshes_dirty = false; + } + + int terrain_set = p_tile_data->get_terrain_set(); + if (terrain_set < 0) { + return; + } + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + if (is_valid_peering_bit_terrain(terrain_set, bit)) { + int terrain_id = p_tile_data->get_peering_bit_terrain(bit); + if (terrain_id >= 0) { + Color color = get_terrain_color(terrain_set, terrain_id); + color.a = TERRAIN_ALPHA; + p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color); + } + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { + // Counts the number of matching terrain tiles and find the best matching icon. + struct Count { + int count = 0; + float probability = 0.0; + Ref<Texture2D> texture; + Rect2i region; + }; + Vector<Vector<Ref<Texture2D>>> output; + LocalVector<LocalVector<Count>> counts; + output.resize(get_terrain_sets_count()); + counts.resize(get_terrain_sets_count()); + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + output.write[terrain_set].resize(get_terrains_count(terrain_set)); + counts[terrain_set].resize(get_terrains_count(terrain_set)); + } + + for (int source_index = 0; source_index < get_source_count(); source_index++) { + int source_id = get_source_id(source_index); + Ref<TileSetSource> source = get_source(source_id); + + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) { + Vector2i tile_id = source->get_tile_id(tile_index); + for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) { + int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index); + + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id)); + int terrain_set = tile_data->get_terrain_set(); + if (terrain_set >= 0) { + ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>()); + + LocalVector<int> bit_counts; + bit_counts.resize(get_terrains_count(terrain_set)); + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + bit_counts[terrain] = 0; + } + for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) { + TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit); + if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { + int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); + if (terrain >= 0) { + bit_counts[terrain] += 1; + } + } + } + + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + if ((bit_counts[terrain] > counts[terrain_set][terrain].count) || (bit_counts[terrain] == counts[terrain_set][terrain].count && tile_data->get_probability() > counts[terrain_set][terrain].probability)) { + counts[terrain_set][terrain].count = bit_counts[terrain]; + counts[terrain_set][terrain].probability = tile_data->get_probability(); + counts[terrain_set][terrain].texture = atlas_source->get_texture(); + counts[terrain_set][terrain].region = atlas_source->get_tile_texture_region(tile_id); + } + } + } + } + } + } + } + + // Generate the icons. + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + Ref<Image> image; + image.instantiate(); + if (counts[terrain_set][terrain].count > 0) { + // Get the best tile. + Ref<Texture2D> texture = counts[terrain_set][terrain].texture; + Rect2 region = counts[terrain_set][terrain].region; + image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->blit_rect(texture->get_image(), region, Point2()); + image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); + } else { + image->create(1, 1, false, Image::FORMAT_RGBA8); + image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); + } + Ref<ImageTexture> icon; + icon.instantiate(); + icon->create_from_image(image); + icon->set_size_override(p_size); + + output.write[terrain_set].write[terrain] = icon; + } + } + return output; +} + void TileSet::_source_changed() { emit_changed(); - notify_property_list_changed(); +} + +Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Rect2 bit_rect; + bit_rect.size = Vector2(p_size) / 3; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit_rect.position = Vector2(1, -1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit_rect.position = Vector2(1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit_rect.position = Vector2(-1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit_rect.position = Vector2(-3, 1); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit_rect.position = Vector2(-3, -1); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit_rect.position = Vector2(-3, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit_rect.position = Vector2(-1, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit_rect.position = Vector2(1, -3); + break; + default: + break; + } + bit_rect.position *= Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + polygon.push_back(bit_rect.position); + polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y)); + polygon.push_back(bit_rect.get_end()); + polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y)); + return polygon; +} + +Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(1, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(1, 2) * unit); + polygon.push_back(Vector2(2, 1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1, 2) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1, -2) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(1, -2) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, 0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; } void TileSet::reset_state() { @@ -747,12 +1614,18 @@ void TileSet::compatibility_conversion() { for (int k = 0; k < ctd->shapes.size(); k++) { CompatibilityShapeData csd = ctd->shapes[k]; if (csd.autotile_coords == coords) { - tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1); - int index = tile_data->get_collision_shapes_count(0) - 1; - tile_data->set_collision_shape_one_way(0, index, csd.one_way); - tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin); - tile_data->set_collision_shape_shape(0, index, csd.shape); - // Ignores transform for now. + Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor + if (convex_shape.is_valid()) { + Vector<Vector2> polygon = convex_shape->get_points(); + for (int point_index = 0; point_index < polygon.size(); point_index++) { + polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + } + tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); + int index = tile_data->get_collision_polygons_count(0) - 1; + tile_data->set_collision_polygon_one_way(0, index, csd.one_way); + tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_polygon_points(0, index, polygon); + } } } @@ -801,7 +1674,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { #ifndef DISABLE_DEPRECATED // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE // This should be moved to a dedicated conversion system - if (components.size() >= 1 && components[0].is_valid_integer()) { + if (components.size() >= 1 && components[0].is_valid_int()) { int id = components[0].to_int(); // Get or create the compatibility object @@ -966,7 +1839,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { #endif // DISABLE_DEPRECATED // This is now a new property. - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(index < 0, false); @@ -985,7 +1858,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { set_occlusion_layer_sdf_collision(index, p_value); return true; } - } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. int index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(index < 0, false); @@ -1012,7 +1885,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { set_physics_layer_physics_material(index, physics_material); return true; } - } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) { + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { // Terrains. int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); ERR_FAIL_COND_V(terrain_set_index < 0, false); @@ -1029,7 +1902,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { } set_terrains_count(terrain_set_index, p_value); return true; - } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) { + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); ERR_FAIL_COND_V(terrain_index < 0, false); if (components[2] == "name") { @@ -1054,7 +1927,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { return true; } } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // Navigation layers. int index = components[0].trim_prefix("navigation_layer_").to_int(); ERR_FAIL_COND_V(index < 0, false); @@ -1066,7 +1939,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { set_navigation_layer_layers(index, p_value); return true; } - } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { // Custom data layers. int index = components[0].trim_prefix("custom_data_layer_").to_int(); ERR_FAIL_COND_V(index < 0, false); @@ -1085,7 +1958,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { set_custom_data_type(index, Variant::Type(int(p_value))); return true; } - } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) { + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { // Create source only if it does not exists. int source_id = components[1].to_int(); @@ -1105,7 +1978,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int index = components[0].trim_prefix("occlusion_layer_").to_int(); if (index < 0 || index >= occlusion_layers.size()) { @@ -1118,7 +1991,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_occlusion_layer_sdf_collision(index); return true; } - } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. int index = components[0].trim_prefix("physics_layer_").to_int(); if (index < 0 || index >= physics_layers.size()) { @@ -1134,7 +2007,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_physics_layer_physics_material(index); return true; } - } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) { + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { // Terrains. int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) { @@ -1146,7 +2019,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "terrains_count") { r_ret = get_terrains_count(terrain_set_index); return true; - } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) { + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { return false; @@ -1159,7 +2032,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { return true; } } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // navigation layers. int index = components[0].trim_prefix("navigation_layer_").to_int(); if (index < 0 || index >= navigation_layers.size()) { @@ -1169,7 +2042,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_navigation_layer_layers(index); return true; } - } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { // Custom data layers. int index = components[0].trim_prefix("custom_data_layer_").to_int(); if (index < 0 || index >= custom_data_layers.size()) { @@ -1182,7 +2055,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_custom_data_type(index); return true; } - } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) { + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { // Atlases data. int source_id = components[1].to_int(); @@ -1287,14 +2160,11 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); - ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew); - ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-offset square,Hexagon"), "set_tile_shape", "get_tile_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew"); // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); @@ -1375,22 +2245,22 @@ void TileSet::_bind_methods() { BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL); BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_LEFT_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_CORNER); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_SIDE); + BIND_ENUM_CONSTANT(CELL_NEIGHBOR_TOP_RIGHT_CORNER); BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS); @@ -1398,18 +2268,23 @@ void TileSet::_bind_methods() { } TileSet::TileSet() { - // Instanciatie and list all plugins. + // Instantiate the tile meshes. + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); + + // Instanciate and list all plugins. tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasTerrain)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); } TileSet::~TileSet() { +#ifndef DISABLE_DEPRECATED for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { memdelete(E->get()); } +#endif // DISABLE_DEPRECATED while (!source_ids.is_empty()) { remove_source(source_ids[0]); } @@ -1532,7 +2407,7 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) // Compute the vector2i if we have coordinates. Vector<String> coords_split = components[0].split(":"); Vector2i coords = TileSetSource::INVALID_ATLAS_COORDS; - if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) { + if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); } @@ -1548,7 +2423,7 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) move_tile_in_atlas(coords, coords, p_value); } else if (components[1] == "next_alternative_id") { tiles[coords].next_alternative_id = p_value; - } else if (components[1].is_valid_integer()) { + } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { // Create the alternative if needed ? @@ -1582,7 +2457,7 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { // Properties. Vector<String> coords_split = components[0].split(":"); - if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) { + if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); if (tiles.has(coords)) { if (components.size() >= 2) { @@ -1593,7 +2468,7 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "next_alternative_id") { r_ret = tiles[coords].next_alternative_id; return true; - } else if (components[1].is_valid_integer()) { + } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { if (components.size() >= 3) { @@ -1694,7 +2569,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector } } - emit_signal("changed"); + emit_signal(SNAME("changed")); } void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { @@ -1720,7 +2595,7 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { tiles_ids.erase(p_atlas_coords); tiles_ids.sort(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const { @@ -1848,7 +2723,7 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ } } - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileSetAtlasSource::has_tiles_outside_texture() { @@ -1893,7 +2768,7 @@ int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, i tiles[p_atlas_coords].alternatives_ids.sort(); _compute_next_alternative_id(p_atlas_coords); - emit_signal("changed"); + emit_signal(SNAME("changed")); return new_alternative_id; } @@ -1908,7 +2783,7 @@ void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); tiles[p_atlas_coords].alternatives_ids.sort(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) { @@ -1925,7 +2800,7 @@ void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); tiles[p_atlas_coords].alternatives_ids.sort(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { @@ -2083,7 +2958,7 @@ int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_s set_scene_tile_scene(new_scene_id, p_packed_scene); _compute_next_alternative_id(); - emit_signal("changed"); + emit_signal(SNAME("changed")); return new_scene_id; } @@ -2103,7 +2978,7 @@ void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { scenes.erase(p_id); scenes_ids.erase(p_id); - emit_signal("changed"); + emit_signal(SNAME("changed")); } void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene) { @@ -2122,7 +2997,7 @@ void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedSce } else { scenes[p_id].scene = Ref<PackedScene>(); } - emit_signal("changed"); + emit_signal(SNAME("changed")); } Ref<PackedScene> TileSetScenesCollectionSource::get_scene_tile_scene(int p_id) const { @@ -2135,7 +3010,7 @@ void TileSetScenesCollectionSource::set_scene_tile_display_placeholder(int p_id, scenes[p_id].display_placeholder = p_display_placeholder; - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileSetScenesCollectionSource::get_scene_tile_display_placeholder(int p_id) const { @@ -2148,7 +3023,7 @@ void TileSetScenesCollectionSource::remove_scene_tile(int p_id) { scenes.erase(p_id); scenes_ids.erase(p_id); - emit_signal("changed"); + emit_signal(SNAME("changed")); } int TileSetScenesCollectionSource::get_next_scene_tile_id() const { @@ -2158,7 +3033,7 @@ int TileSetScenesCollectionSource::get_next_scene_tile_id() const { bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_integer()) { + if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int()) { int scene_id = components[1].to_int(); if (components.size() >= 3 && components[2] == "scene") { if (has_scene_tile_id(scene_id)) { @@ -2182,7 +3057,7 @@ bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant bool TileSetScenesCollectionSource::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_integer() && scenes.has(components[1].to_int())) { + if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int() && scenes.has(components[1].to_int())) { if (components.size() >= 3 && components[2] == "scene") { r_ret = scenes[components[1].to_int()].scene; return true; @@ -2235,16 +3110,14 @@ void TileSetScenesCollectionSource::_bind_methods() { void TileData::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; - if (tile_set) { - occluders.resize(tile_set->get_occlusion_layers_count()); - physics.resize(tile_set->get_physics_layers_count()); - navigation.resize(tile_set->get_navigation_layers_count()); - custom_data.resize(tile_set->get_custom_data_layers_count()); - } - notify_property_list_changed(); + notify_tile_data_properties_should_change(); } void TileData::notify_tile_data_properties_should_change() { + if (!tile_set) { + return; + } + occluders.resize(tile_set->get_occlusion_layers_count()); physics.resize(tile_set->get_physics_layers_count()); for (int bit_index = 0; bit_index < 16; bit_index++) { @@ -2271,7 +3144,7 @@ void TileData::notify_tile_data_properties_should_change() { } notify_property_list_changed(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } void TileData::reset_state() { @@ -2293,7 +3166,7 @@ bool TileData::is_allowing_transform() const { void TileData::set_flip_h(bool p_flip_h) { ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); flip_h = p_flip_h; - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileData::get_flip_h() const { return flip_h; @@ -2302,7 +3175,7 @@ bool TileData::get_flip_h() const { void TileData::set_flip_v(bool p_flip_v) { ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); flip_v = p_flip_v; - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileData::get_flip_v() const { @@ -2312,7 +3185,7 @@ bool TileData::get_flip_v() const { void TileData::set_transpose(bool p_transpose) { ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); transpose = p_transpose; - emit_signal("changed"); + emit_signal(SNAME("changed")); } bool TileData::get_transpose() const { return transpose; @@ -2320,7 +3193,7 @@ bool TileData::get_transpose() const { void TileData::set_texture_offset(Vector2i p_texture_offset) { tex_offset = p_texture_offset; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Vector2i TileData::get_texture_offset() const { @@ -2329,7 +3202,7 @@ Vector2i TileData::get_texture_offset() const { void TileData::tile_set_material(Ref<ShaderMaterial> p_material) { material = p_material; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Ref<ShaderMaterial> TileData::tile_get_material() const { return material; @@ -2337,7 +3210,7 @@ Ref<ShaderMaterial> TileData::tile_get_material() const { void TileData::set_modulate(Color p_modulate) { modulate = p_modulate; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Color TileData::get_modulate() const { return modulate; @@ -2345,7 +3218,7 @@ Color TileData::get_modulate() const { void TileData::set_z_index(int p_z_index) { z_index = p_z_index; - emit_signal("changed"); + emit_signal(SNAME("changed")); } int TileData::get_z_index() const { return z_index; @@ -2353,7 +3226,7 @@ int TileData::get_z_index() const { void TileData::set_y_sort_origin(int p_y_sort_origin) { y_sort_origin = p_y_sort_origin; - emit_signal("changed"); + emit_signal(SNAME("changed")); } int TileData::get_y_sort_origin() const { return y_sort_origin; @@ -2362,7 +3235,7 @@ int TileData::get_y_sort_origin() const { void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) { ERR_FAIL_INDEX(p_layer_id, occluders.size()); occluders.write[p_layer_id] = p_occluder_polygon; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { @@ -2371,80 +3244,116 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_shapes_count(int p_layer_id) const { +int TileData::get_collision_polygons_count(int p_layer_id) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].shapes.size(); + return physics[p_layer_id].polygons.size(); } -void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) { +void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_COND(p_shapes_count < 0); - physics.write[p_layer_id].shapes.resize(p_shapes_count); + ERR_FAIL_COND(p_polygons_count < 0); + physics.write[p_layer_id].polygons.resize(p_polygons_count); notify_property_list_changed(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } -void TileData::add_collision_shape(int p_layer_id) { +void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData()); - emit_signal("changed"); + physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); + emit_signal(SNAME("changed")); } -void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) { +void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.remove(p_shape_index); - emit_signal("changed"); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.remove(p_polygon_index); + emit_signal(SNAME("changed")); } -void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape) { +void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape; - emit_signal("changed"); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points."); + + if (p_polygon.is_empty()) { + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear(); + } else { + // Decompose into convex shapes. + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon); + ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes."); + + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size()); + for (int i = 0; i < decomp.size(); i++) { + Ref<ConvexPolygonShape2D> shape; + shape.instantiate(); + shape->set_points(decomp[i]); + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape; + } + } + physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon; + emit_signal(SNAME("changed")); } -Ref<Shape2D> TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<Shape2D>()); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref<Shape2D>()); - return physics[p_layer_id].shapes[p_shape_index].shape; +Vector<Vector2> TileData::get_collision_polygon_points(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector<Vector2>()); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Vector<Vector2>()); + return physics[p_layer_id].polygons[p_polygon_index].polygon; } -void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) { +void TileData::set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way; - emit_signal("changed"); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way = p_one_way; + emit_signal(SNAME("changed")); } -bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const { +bool TileData::is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false); - return physics[p_layer_id].shapes[p_shape_index].one_way; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), false); + return physics[p_layer_id].polygons[p_polygon_index].one_way; } -void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) { +void TileData::set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin; - emit_signal("changed"); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way_margin = p_one_way_margin; + emit_signal(SNAME("changed")); } -float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const { +float TileData::get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0); - return physics[p_layer_id].shapes[p_shape_index].one_way_margin; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0.0); + return physics[p_layer_id].polygons[p_polygon_index].one_way_margin; +} + +int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0); + return physics[p_layer_id].polygons[p_polygon_index].shapes.size(); +} + +Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>()); + ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>()); + return physics[p_layer_id].polygons[shape_index].shapes[shape_index]; } // Terrain void TileData::set_terrain_set(int p_terrain_set) { ERR_FAIL_COND(p_terrain_set < -1); + if (p_terrain_set == terrain_set) { + return; + } if (tile_set) { ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; + } } terrain_set = p_terrain_set; notify_property_list_changed(); - emit_signal("changed"); + emit_signal(SNAME("changed")); } int TileData::get_terrain_set() const { @@ -2452,16 +3361,18 @@ int TileData::get_terrain_set() const { } void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { + ERR_FAIL_COND(terrain_set < 0); ERR_FAIL_COND(p_terrain_index < -1); if (tile_set) { ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit)); } terrain_peering_bits[p_peering_bit] = p_terrain_index; - emit_signal("changed"); + emit_signal(SNAME("changed")); } int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1); return terrain_peering_bits[p_peering_bit]; } @@ -2475,7 +3386,7 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) { ERR_FAIL_INDEX(p_layer_id, navigation.size()); navigation.write[p_layer_id] = p_navigation_polygon; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const { @@ -2485,9 +3396,9 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const { // Misc void TileData::set_probability(float p_probability) { - ERR_FAIL_COND(p_probability <= 0.0); + ERR_FAIL_COND(p_probability < 0.0); probability = p_probability; - emit_signal("changed"); + emit_signal(SNAME("changed")); } float TileData::get_probability() const { return probability; @@ -2511,7 +3422,7 @@ Variant TileData::get_custom_data(String p_layer_name) const { void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data.size()); custom_data.write[p_layer_id] = p_value; - emit_signal("changed"); + emit_signal(SNAME("changed")); } Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { @@ -2522,7 +3433,7 @@ Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { bool TileData::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2542,11 +3453,11 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { set_occluder(layer_index, polygon); return true; } - } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "shapes_count") { + if (components.size() == 2 && components[1] == "polygons_count") { if (p_value.get_type() != Variant::INT) { return false; } @@ -2558,13 +3469,13 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_shapes_count(layer_index, p_value); + set_collision_polygons_count(layer_index, p_value); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); - if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") { + if (components[2] == "points" || components[2] == "one_way" || components[2] == "one_way_margin") { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -2573,23 +3484,23 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } - if (shape_index >= physics[layer_index].shapes.size()) { - physics.write[layer_index].shapes.resize(shape_index + 1); + if (polygon_index >= physics[layer_index].polygons.size()) { + physics.write[layer_index].polygons.resize(polygon_index + 1); } } - if (components[2] == "shape") { - Ref<Shape2D> shape = p_value; - set_collision_shape_shape(layer_index, shape_index, shape); + if (components[2] == "points") { + Vector<Vector2> polygon = p_value; + set_collision_polygon_points(layer_index, polygon_index, polygon); return true; } else if (components[2] == "one_way") { - set_collision_shape_one_way(layer_index, shape_index, p_value); + set_collision_polygon_one_way(layer_index, polygon_index, p_value); return true; } else if (components[2] == "one_way_margin") { - set_collision_shape_one_way_margin(layer_index, shape_index, p_value); + set_collision_polygon_one_way_margin(layer_index, polygon_index, p_value); return true; } } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // Navigation layers. int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2611,43 +3522,15 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value); - } else if (components[1] == "right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value); - } else if (components[1] == "bottom_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value); - } else if (components[1] == "bottom_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value); - } else if (components[1] == "bottom_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value); - } else if (components[1] == "bottom_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value); - } else if (components[1] == "left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value); - } else if (components[1] == "left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value); - } else if (components[1] == "top_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value); - } else if (components[1] == "top_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value); - } else if (components[1] == "top_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value); - } else if (components[1] == "top_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value); - } else if (components[1] == "top_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value); - } else if (components[1] == "top_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value); - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + set_peering_bit_terrain(bit, p_value); + return true; + } } - return true; - } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) { + return false; + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { // Custom data layers. int layer_index = components[0].trim_prefix("custom_data_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2671,7 +3554,7 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); if (tile_set) { - if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2682,72 +3565,43 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_occluder(layer_index); return true; } - } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) { + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "shapes_count") { - r_ret = get_collision_shapes_count(layer_index); + if (components.size() == 2 && components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); - if (shape_index >= physics[layer_index].shapes.size()) { + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (polygon_index >= physics[layer_index].polygons.size()) { return false; } - if (components[2] == "shape") { - r_ret = get_collision_shape_shape(layer_index, shape_index); + if (components[2] == "points") { + r_ret = get_collision_polygon_points(layer_index, polygon_index); return true; } else if (components[2] == "one_way") { - r_ret = is_collision_shape_one_way(layer_index, shape_index); + r_ret = is_collision_polygon_one_way(layer_index, polygon_index); return true; } else if (components[2] == "one_way_margin") { - r_ret = get_collision_shape_one_way_margin(layer_index, shape_index); + r_ret = get_collision_polygon_one_way_margin(layer_index, polygon_index); return true; } } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE]; - } else if (components[1] == "right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER]; - } else if (components[1] == "bottom_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE]; - } else if (components[1] == "bottom_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER]; - } else if (components[1] == "bottom_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE]; - } else if (components[1] == "bottom_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER]; - } else if (components[1] == "bottom_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE]; - } else if (components[1] == "bottom_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER]; - } else if (components[1] == "left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE]; - } else if (components[1] == "left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER]; - } else if (components[1] == "top_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE]; - } else if (components[1] == "top_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER]; - } else if (components[1] == "top_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE]; - } else if (components[1] == "top_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER]; - } else if (components[1] == "top_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE]; - } else if (components[1] == "top_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER]; - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + r_ret = terrain_peering_bits[i]; + return true; + } } - return true; - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) { + return false; + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2758,7 +3612,7 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_navigation_polygon(layer_index); return true; } - } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) { + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { // Custom data layers. int layer_index = components[0].trim_prefix("custom_data_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); @@ -2791,26 +3645,26 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - for (int j = 0; j < physics[i].shapes.size(); j++) { - // physics_layer_%d/shapes_count - property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT); - if (!physics[i].shapes[j].shape.is_valid()) { + for (int j = 0; j < physics[i].polygons.size(); j++) { + // physics_layer_%d/points + property_info = PropertyInfo(Variant::ARRAY, vformat("physics_layer_%d/polygon_%d/points", i, j), PROPERTY_HINT_ARRAY_TYPE, "Vector2", PROPERTY_USAGE_DEFAULT); + if (physics[i].polygons[j].polygon.is_empty()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way - property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j)); - if (physics[i].shapes[j].one_way == false) { + // physics_layer_%d/polygon_%d/one_way + property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/polygon_%d/one_way", i, j)); + if (physics[i].polygons[j].one_way == false) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way_margin - property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j)); - if (physics[i].shapes[j].one_way_margin == 1.0) { + // physics_layer_%d/polygon_%d/one_way_margin + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/polygon_%d/one_way_margin", i, j)); + if (physics[i].polygons[j].one_way_margin == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -2820,117 +3674,15 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Terrain data if (terrain_set >= 0) { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (is_valid_peering_bit_terrain(bit)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); + if (get_peering_bit_terrain(bit) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); } - p_list->push_back(property_info); } } @@ -2982,16 +3734,16 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count); - ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count); - ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape); - ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape); - ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); + ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); + ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); + ClassDB::bind_method(D_METHOD("get_collision_polygon_points", "layer_id", "polygon_index"), &TileData::get_collision_polygon_points); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way", "layer_id", "polygon_index", "one_way"), &TileData::set_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("is_collision_polygon_one_way", "layer_id", "polygon_index"), &TileData::is_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way_margin", "layer_id", "polygon_index", "one_way_margin"), &TileData::set_collision_polygon_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygon_one_way_margin", "layer_id", "polygon_index"), &TileData::get_collision_polygon_one_way_margin); // Terrain ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); @@ -3030,805 +3782,6 @@ void TileData::_bind_methods() { ADD_SIGNAL(MethodInfo("changed")); } - -/////////////////////////////// TileSetPluginAtlasTerrain ////////////////////////////////////// - -// --- PLUGINS --- -void TileSetPluginAtlasTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - Rect2 bit_rect; - bit_rect.size = Vector2(p_size) / 3; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit_rect.position = Vector2(1, -1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit_rect.position = Vector2(1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit_rect.position = Vector2(-1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit_rect.position = Vector2(-3, 1); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit_rect.position = Vector2(-3, -1); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit_rect.position = Vector2(-3, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit_rect.position = Vector2(-1, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit_rect.position = Vector2(1, -3); - break; - default: - break; - } - bit_rect.position *= Vector2(p_size) / 6.0; - p_canvas_item->draw_rect(bit_rect, p_color); -} - -void TileSetPluginAtlasTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(1, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(1, 2) * unit); - polygon.push_back(Vector2(2, 1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1, 2) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1, -2) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(1, -2) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, 0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -#define TERRAIN_ALPHA 0.8 - -#define DRAW_TERRAIN_BIT(f, bit) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit)); \ - } \ - } - -#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \ - } \ - } - -void TileSetPluginAtlasTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) { - ERR_FAIL_COND(!p_tile_set); - ERR_FAIL_COND(!p_tile_data); - - int terrain_set = p_tile_data->get_terrain_set(); - if (terrain_set < 0) { - return; - } - TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set); - - TileSet::TileShape shape = p_tile_set->get_tile_shape(); - Vector2i size = p_tile_set->get_tile_size(); - - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } - } else { - TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis(); - float overlap = 0.0; - switch (p_tile_set->get_tile_shape()) { - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } - } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); -} - /////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { @@ -3868,8 +3821,8 @@ void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int } break; case CanvasItem::NOTIFICATION_DRAW: { Ref<TileSet> tile_set = p_tile_map->get_tileset(); - if (tile_set.is_valid()) { - RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(p_tile_map->get_canvas_item(), tile_set->is_y_sorting()); + if (tile_set.is_valid() || p_tile_map->is_y_sort_enabled()) { + RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(p_tile_map->get_canvas_item(), tile_set->is_y_sorting() || p_tile_map->is_y_sort_enabled()); } } break; } @@ -4214,15 +4167,17 @@ void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self for (int body_index = 0; body_index < q.bodies.size(); body_index++) { // Add the shapes again. - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index); - float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index); - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { Transform2D xform = Transform2D(); xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); @@ -4295,7 +4250,23 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap Ref<TileSet> tile_set = p_tile_map->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_collisions_hint())) { + if (!p_tile_map->get_tree()) { + return; + } + + bool show_collision = false; + switch (p_tile_map->get_collision_visibility_mode()) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_collision = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_collision = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_collision = true; + break; + } + if (!show_collision) { return; } @@ -4323,11 +4294,13 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - // Draw the debug shape. - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { - shape->draw(p_quadrant->debug_canvas_item, debug_collision_color); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector<Color> color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); } } } @@ -4454,7 +4427,23 @@ void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, Tile Ref<TileSet> tile_set = p_tile_map->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_navigation_hint())) { + if (!p_tile_map->get_tree()) { + return; + } + + bool show_navigation = false; + switch (p_tile_map->get_navigation_visibility_mode()) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_navigation = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_navigation = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_navigation = true; + break; + } + if (!show_navigation) { return; } @@ -4551,7 +4540,7 @@ void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, if (scenes_collection_source) { Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); if (packed_scene.is_valid()) { - Node *scene = packed_scene->instance(); + Node *scene = packed_scene->instantiate(); p_tile_map->add_child(scene); Control *scene_as_control = Object::cast_to<Control>(scene); Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 6cf4198f30..dbf6dbabe6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -33,19 +33,18 @@ #include "core/io/resource.h" #include "core/object/object.h" +#include "core/templates/local_vector.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" #ifndef DISABLE_DEPRECATED -#include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/resources/shader.h" -#include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" #endif @@ -60,7 +59,6 @@ class TileSetPlugin; class TileSetPluginAtlasRendering; class TileSetPluginAtlasPhysics; class TileSetPluginAtlasNavigation; -class TileSetPluginAtlasTerrain; class TileSet : public Resource { GDCLASS(TileSet, Resource); @@ -138,6 +136,8 @@ public: CELL_NEIGHBOR_MAX, }; + static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[]; + enum TerrainMode { TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0, TERRAIN_MODE_MATCH_CORNERS, @@ -194,6 +194,10 @@ private: }; Vector<OcclusionLayer> occlusion_layers; + Ref<ArrayMesh> tile_lines_mesh; + Ref<ArrayMesh> tile_filled_mesh; + bool tile_meshes_dirty = true; + // Physics struct PhysicsLayer { uint32_t collision_layer = 1; @@ -213,6 +217,9 @@ private: }; Vector<TerrainSet> terrain_sets; + Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; + bool terrain_bits_meshes_dirty = true; + // Navigation struct Navigationlayer { uint32_t layers = 1; @@ -239,6 +246,19 @@ private: void _compute_next_source_id(); void _source_changed(); + // Helpers + Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + protected: static void _bind_methods(); @@ -257,8 +277,6 @@ public: TileOffsetAxis get_tile_offset_axis() const; void set_tile_size(Size2i p_size); Size2i get_tile_size() const; - void set_tile_skew(Vector2 p_skew); - Vector2 get_tile_skew() const; // -- Sources management -- int get_next_source_id() const; @@ -305,6 +323,7 @@ public: String get_terrain_name(int p_terrain_set, int p_terrain_index) const; void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); Color get_terrain_color(int p_terrain_set, int p_terrain_index) const; + bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const; bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation @@ -323,8 +342,14 @@ public: Variant::Type get_custom_data_type(int p_layer_id) const; // Helpers + Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); + Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit); + void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data); + Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size); + + // Resource management virtual void reset_state() override; TileSet(); @@ -509,13 +534,14 @@ private: // Physics struct PhysicsLayerTileData { - struct ShapeTileData { - Ref<Shape2D> shape = Ref<Shape2D>(); + struct PolygonShapeTileData { + LocalVector<Vector2> polygon; + LocalVector<Ref<ConvexPolygonShape2D>> shapes; bool one_way = false; float one_way_margin = 1.0; }; - Vector<ShapeTileData> shapes; + Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; // TODO add support for areas. @@ -570,16 +596,18 @@ public: Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; // Physics - int get_collision_shapes_count(int p_layer_id) const; - void set_collision_shapes_count(int p_layer_id, int p_shapes_count); - void add_collision_shape(int p_layer_id); - void remove_collision_shape(int p_layer_id, int p_shape_index); - void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape); - Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way); - bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin); - float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const; + int get_collision_polygons_count(int p_layer_id) const; + void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + void add_collision_polygon(int p_layer_id); + void remove_collision_polygon(int p_layer_id, int p_polygon_index); + void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon); + Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way); + bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin); + float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const; + int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const; + Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const; // Terrain void set_terrain_set(int p_terrain_id); @@ -637,26 +665,6 @@ public: static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); }; -class TileSetPluginAtlasTerrain : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasTerrain, TileSetPlugin); - -private: - static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - -public: - static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data); -}; - class TileSetPluginAtlasPhysics : public TileSetPlugin { GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index da29f2ebd8..0f6412e6e9 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -33,6 +33,7 @@ #include "core/templates/vmap.h" #include "servers/rendering/shader_types.h" #include "visual_shader_nodes.h" +#include "visual_shader_particle_nodes.h" #include "visual_shader_sdf_nodes.h" bool VisualShaderNode::is_simple_decl() const { @@ -60,6 +61,20 @@ Variant VisualShaderNode::get_input_port_default_value(int p_port) const { return Variant(); } +void VisualShaderNode::remove_input_port_default_value(int p_port) { + if (default_input_values.has(p_port)) { + default_input_values.erase(p_port); + emit_changed(); + } +} + +void VisualShaderNode::clear_default_input_values() { + if (!default_input_values.is_empty()) { + default_input_values.clear(); + emit_changed(); + } +} + bool VisualShaderNode::is_port_separator(int p_index) const { return false; } @@ -94,6 +109,52 @@ bool VisualShaderNode::is_generate_input_var(int p_port) const { return true; } +bool VisualShaderNode::is_output_port_expandable(int p_port) const { + return false; +} + +void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) { + for (int i = 0; i < p_values.size(); i++) { + expanded_output_ports[p_values[i]] = true; + } + emit_changed(); +} + +Array VisualShaderNode::_get_output_ports_expanded() const { + Array arr; + for (int i = 0; i < get_output_port_count(); i++) { + if (_is_output_port_expanded(i)) { + arr.push_back(i); + } + } + return arr; +} + +void VisualShaderNode::_set_output_port_expanded(int p_port, bool p_expanded) { + expanded_output_ports[p_port] = p_expanded; + emit_changed(); +} + +bool VisualShaderNode::_is_output_port_expanded(int p_port) const { + if (expanded_output_ports.has(p_port)) { + return expanded_output_ports[p_port]; + } + return false; +} + +int VisualShaderNode::get_expanded_output_port_count() const { + int count = get_output_port_count(); + int count2 = count; + for (int i = 0; i < count; i++) { + if (is_output_port_expandable(i) && _is_output_port_expanded(i)) { + if (get_output_port_type(i) == PORT_TYPE_VECTOR) { + count2 += 3; + } + } + } + return count2; +} + bool VisualShaderNode::is_code_generated() const { return true; } @@ -106,6 +167,14 @@ bool VisualShaderNode::is_use_prop_slots() const { return false; } +bool VisualShaderNode::is_disabled() const { + return disabled; +} + +void VisualShaderNode::set_disabled(bool p_disabled) { + disabled = p_disabled; +} + Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { return Vector<VisualShader::DefaultTextureParam>(); } @@ -157,14 +226,24 @@ void VisualShaderNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview); ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview); + ClassDB::bind_method(D_METHOD("_set_output_port_expanded", "port"), &VisualShaderNode::_set_output_port_expanded); + ClassDB::bind_method(D_METHOD("_is_output_port_expanded"), &VisualShaderNode::_is_output_port_expanded); + + ClassDB::bind_method(D_METHOD("_set_output_ports_expanded", "values"), &VisualShaderNode::_set_output_ports_expanded); + ClassDB::bind_method(D_METHOD("_get_output_ports_expanded"), &VisualShaderNode::_get_output_ports_expanded); + ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value); ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value); + ClassDB::bind_method(D_METHOD("remove_input_port_default_value", "port"), &VisualShaderNode::remove_input_port_default_value); + ClassDB::bind_method(D_METHOD("clear_default_input_values"), &VisualShaderNode::clear_default_input_values); + ClassDB::bind_method(D_METHOD("set_default_input_values", "values"), &VisualShaderNode::set_default_input_values); ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values); ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded"); ADD_SIGNAL(MethodInfo("editor_refresh_request")); BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR); @@ -273,14 +352,14 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader:: for (int i = 0; i < get_output_port_count(); i++) { output_vars.push_back(p_output_vars[i]); } - String code = "\t{\n"; + String code = " {\n"; String _code = (String)get_script_instance()->call("_get_code", input_vars, output_vars, (int)p_mode, (int)p_type); bool nend = _code.ends_with("\n"); - _code = _code.insert(0, "\t\t"); - _code = _code.replace("\n", "\n\t\t"); + _code = _code.insert(0, " "); + _code = _code.replace("\n", "\n "); code += _code; if (!nend) { - code += "\n\t}"; + code += "\n }"; } else { code.remove(code.size() - 1); code += "}"; @@ -312,6 +391,18 @@ void VisualShaderNodeCustom::set_default_input_values(const Array &p_values) { } } +void VisualShaderNodeCustom::remove_input_port_default_value(int p_port) { + if (!is_initialized) { + VisualShaderNode::remove_input_port_default_value(p_port); + } +} + +void VisualShaderNodeCustom::clear_default_input_values() { + if (!is_initialized) { + VisualShaderNode::clear_default_input_values(); + } +} + void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Variant &p_value) { VisualShaderNode::set_input_port_default_value(p_port, p_value); } @@ -526,7 +617,7 @@ void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_c if (g->nodes[p_id].node->get_class_name() == p_new_class) { return; } - VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(p_new_class)); + VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(p_new_class)); vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update)); g->nodes[p_id].node = Ref<VisualShaderNode>(vsn); @@ -576,7 +667,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po return false; } - if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) { + if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_expanded_output_port_count()) { return false; } @@ -617,7 +708,7 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from Graph *g = &graph[p_type]; ERR_FAIL_COND(!g->nodes.has(p_from_node)); - ERR_FAIL_INDEX(p_from_port, g->nodes[p_from_node].node->get_output_port_count()); + ERR_FAIL_INDEX(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count()); ERR_FAIL_COND(!g->nodes.has(p_to_node)); ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count()); @@ -639,7 +730,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, Graph *g = &graph[p_type]; ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER); @@ -790,7 +881,7 @@ bool VisualShader::is_text_shader() const { String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const { Ref<VisualShaderNode> node = get_node(p_type, p_node); ERR_FAIL_COND_V(!node.is_valid(), String()); - ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String()); + ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_expanded_output_port_count(), String()); ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String()); StringBuilder global_code; @@ -809,7 +900,7 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; expr += global_expression->generate_global(get_mode(), Type(i), -1); - expr = expr.replace("\n", "\n\t"); + expr = expr.replace("\n", "\n "); expr += "\n"; global_expressions += expr; } @@ -844,13 +935,13 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port ERR_FAIL_COND_V(err != OK, String()); if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) { - code += "\tCOLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n"; + code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n"; } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR_INT) { - code += "\tCOLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n"; + code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n"; } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) { - code += "\tCOLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n"; + code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n"; } else { - code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; + code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } code += "}\n"; @@ -989,9 +1080,11 @@ static const char *type_string[VisualShader::TYPE_MAX] = { "vertex", "fragment", "light", - "emit", + "start", "process", - "end", + "collide", + "start_custom", + "process_custom", "sky", }; @@ -1207,6 +1300,12 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const { const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node; + if (vsnode->is_disabled()) { + code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n"; + code += " // Node is disabled and code is not generated.\n"; + return OK; + } + //check inputs recursively first int input_count = vsnode->get_input_port_count(); for (int i = 0; i < input_count; i++) { @@ -1261,7 +1360,8 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui return OK; } - code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n"; + String node_name = "// " + vsnode->get_caption() + ":" + itos(node) + "\n"; + String node_code; Vector<String> input_vars; input_vars.resize(vsnode->get_input_port_count()); @@ -1275,6 +1375,11 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui if (input_connections.has(ck)) { //connected to something, use that output int from_node = input_connections[ck]->get().from_node; + + if (graph[type].nodes[from_node].node->is_disabled()) { + continue; + } + int from_port = input_connections[ck]->get().from_port; VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i); @@ -1327,21 +1432,21 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui if (defval.get_type() == Variant::FLOAT) { float val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); - code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n"; + node_code += " float " + inputs[i] + " = " + vformat("%.5f", val) + ";\n"; } else if (defval.get_type() == Variant::INT) { int val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); - code += "\tint " + inputs[i] + " = " + itos(val) + ";\n"; + node_code += " int " + inputs[i] + " = " + itos(val) + ";\n"; } else if (defval.get_type() == Variant::BOOL) { bool val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); - code += "\tbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n"; + node_code += " bool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n"; } else if (defval.get_type() == Variant::VECTOR3) { Vector3 val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); - code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z); - } else if (defval.get_type() == Variant::TRANSFORM) { - Transform val = defval; + node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::TRANSFORM3D) { + Transform3D val = defval; val.basis.transpose(); inputs[i] = "n_in" + itos(node) + "p" + itos(i); Array values; @@ -1354,7 +1459,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui values.push_back(val.origin.y); values.push_back(val.origin.z); bool err = false; - code += "\tmat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err); + node_code += " mat4 " + inputs[i] + " = " + String("mat4(vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 0.0), vec4(%.5f, %.5f, %.5f, 1.0));\n").sprintf(values, &err); } else { //will go empty, node is expected to know what it is doing at this point and handle it } @@ -1362,13 +1467,30 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui } int output_count = vsnode->get_output_port_count(); + int initial_output_count = output_count; + + Map<int, bool> expanded_output_ports; + + for (int i = 0; i < initial_output_count; i++) { + bool expanded = false; + + if (vsnode->is_output_port_expandable(i) && vsnode->_is_output_port_expanded(i)) { + expanded = true; + + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + output_count += 3; + } + } + expanded_output_ports.insert(i, expanded); + } + Vector<String> output_vars; - output_vars.resize(vsnode->get_output_port_count()); + output_vars.resize(output_count); String *outputs = output_vars.ptrw(); if (vsnode->is_simple_decl()) { // less code to generate for some simple_decl nodes - for (int i = 0; i < output_count; i++) { - String var_name = "n_out" + itos(node) + "p" + itos(i); + for (int i = 0, j = 0; i < initial_output_count; i++, j++) { + String var_name = "n_out" + itos(node) + "p" + itos(j); switch (vsnode->get_output_port_type(i)) { case VisualShaderNode::PORT_TYPE_SCALAR: outputs[i] = "float " + var_name; @@ -1388,34 +1510,88 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui default: { } } + if (expanded_output_ports[i]) { + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + j += 3; + } + } } } else { - for (int i = 0; i < output_count; i++) { - outputs[i] = "n_out" + itos(node) + "p" + itos(i); + for (int i = 0, j = 0; i < initial_output_count; i++, j++) { + outputs[i] = "n_out" + itos(node) + "p" + itos(j); switch (vsnode->get_output_port_type(i)) { case VisualShaderNode::PORT_TYPE_SCALAR: - code += String() + "\tfloat " + outputs[i] + ";\n"; + code += " float " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_SCALAR_INT: - code += String() + "\tint " + outputs[i] + ";\n"; + code += " int " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_VECTOR: - code += String() + "\tvec3 " + outputs[i] + ";\n"; + code += " vec3 " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_BOOLEAN: - code += String() + "\tbool " + outputs[i] + ";\n"; + code += " bool " + outputs[i] + ";\n"; break; case VisualShaderNode::PORT_TYPE_TRANSFORM: - code += String() + "\tmat4 " + outputs[i] + ";\n"; + code += " mat4 " + outputs[i] + ";\n"; break; default: { } } + if (expanded_output_ports[i]) { + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + j += 3; + } + } } } - code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview); + node_code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview); + if (node_code != String()) { + code += node_name; + code += node_code; + code += "\n"; + } + + for (int i = 0; i < output_count; i++) { + bool new_line_inserted = false; + if (expanded_output_ports[i]) { + if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) { + if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component + if (!new_line_inserted) { + code += "\n"; + new_line_inserted = true; + } + String r = "n_out" + itos(node) + "p" + itos(i + 1); + code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n"; + outputs[i + 1] = r; + } + + if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component + if (!new_line_inserted) { + code += "\n"; + new_line_inserted = true; + } + String g = "n_out" + itos(node) + "p" + itos(i + 2); + code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n"; + outputs[i + 2] = g; + } + + if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component + if (!new_line_inserted) { + code += "\n"; + new_line_inserted = true; + } + String b = "n_out" + itos(node) + "p" + itos(i + 3); + code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n"; + outputs[i + 3] = b; + } + + i += 3; + } + } + } code += "\n"; // processed.insert(node); @@ -1426,7 +1602,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui bool VisualShader::has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const { if (!ShaderTypes::get_singleton()->get_functions(p_mode).has(p_func_name)) { if (p_mode == RenderingServer::ShaderMode::SHADER_PARTICLES) { - if (p_func_name == "emit" || p_func_name == "process" || p_func_name == "end") { + if (p_func_name == "start_custom" || p_func_name == "process_custom" || p_func_name == "collide") { return true; } } @@ -1507,11 +1683,12 @@ void VisualShader::_update_shader() const { global_code += "render_mode " + render_mode + ";\n\n"; } - static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "emit", "process", "end", "sky" }; + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky" }; String global_expressions; Set<String> used_uniform_names; List<VisualShaderNodeUniform *> uniforms; + Map<int, List<int>> emitters; for (int i = 0, index = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { @@ -1524,7 +1701,7 @@ void VisualShader::_update_shader() const { String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; expr += global_expression->generate_global(get_mode(), Type(i), -1); - expr = expr.replace("\n", "\n\t"); + expr = expr.replace("\n", "\n "); expr += "\n"; global_expressions += expr; } @@ -1536,6 +1713,19 @@ void VisualShader::_update_shader() const { if (uniform.is_valid()) { uniforms.push_back(uniform.ptr()); } + Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr()); + if (emit_particle.is_valid()) { + if (!emitters.has(i)) { + emitters.insert(i, List<int>()); + } + + for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) { + if (M->get().node == emit_particle.ptr()) { + emitters[i].push_back(M->key()); + break; + } + } + } } } @@ -1562,6 +1752,11 @@ void VisualShader::_update_shader() const { StringBuilder func_code; + bool is_empty_func = false; + if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY) { + is_empty_func = true; + } + for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) { ConnectionKey from_key; from_key.node = E->get().from_node; @@ -1574,7 +1769,16 @@ void VisualShader::_update_shader() const { to_key.port = E->get().to_port; input_connections.insert(to_key, E); + + if (is_empty_func && to_key.node == NODE_ID_OUTPUT) { + is_empty_func = false; + } + } + + if (is_empty_func) { + continue; } + if (shader_mode != Shader::MODE_PARTICLES) { func_code += "\nvoid " + String(func_name[i]) + "() {\n"; } @@ -1584,6 +1788,13 @@ void VisualShader::_update_shader() const { Error err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false, classes); ERR_FAIL_COND(err != OK); + if (emitters.has(i)) { + for (List<int>::Element *E = emitters[i].front(); E; E = E->next()) { + err = _write_node(Type(i), global_code, global_code_per_node, global_code_per_func, func_code, default_tex_params, input_connections, output_connections, E->get(), processed, false, classes); + ERR_FAIL_COND(err != OK); + } + } + if (shader_mode == Shader::MODE_PARTICLES) { code_map.insert(i, func_code); } else { @@ -1592,19 +1803,130 @@ void VisualShader::_update_shader() const { } } + String global_compute_code; + if (shader_mode == Shader::MODE_PARTICLES) { - code += "\nvoid compute() {\n"; - code += "\tif (RESTART) {\n"; - code += code_map[TYPE_EMIT]; - code += "\t} else {\n"; - code += code_map[TYPE_PROCESS]; - code += "\t}\n"; - code += "}\n"; + bool has_start = !code_map[TYPE_START].is_empty(); + bool has_start_custom = !code_map[TYPE_START_CUSTOM].is_empty(); + bool has_process = !code_map[TYPE_PROCESS].is_empty(); + bool has_process_custom = !code_map[TYPE_PROCESS_CUSTOM].is_empty(); + bool has_collide = !code_map[TYPE_COLLIDE].is_empty(); + + code += "void start() {\n"; + if (has_start || has_start_custom) { + code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n"; + code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; + code += " float __radians;\n"; + code += " vec3 __vec3_buff1;\n"; + code += " vec3 __vec3_buff2;\n"; + code += " float __scalar_buff1;\n"; + code += " float __scalar_buff2;\n"; + code += " vec3 __ndiff = normalize(__diff);\n\n"; + } + if (has_start) { + code += " {\n"; + code += code_map[TYPE_START].replace("\n ", "\n "); + code += " }\n"; + if (has_start_custom) { + code += " \n"; + } + } + if (has_start_custom) { + code += " {\n"; + code += code_map[TYPE_START_CUSTOM].replace("\n ", "\n "); + code += " }\n"; + } + code += "}\n\n"; + code += "void process() {\n"; + if (has_process || has_process_custom || has_collide) { + code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n"; + code += " vec3 __vec3_buff1;\n"; + code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 __ndiff = normalize(__diff);\n\n"; + } + code += " {\n"; + String tab = " "; + if (has_collide) { + code += " if (COLLIDED) {\n\n"; + code += code_map[TYPE_COLLIDE].replace("\n ", "\n "); + if (has_process) { + code += " } else {\n\n"; + tab += " "; + } + } + if (has_process) { + code += code_map[TYPE_PROCESS].replace("\n ", "\n " + tab); + } + if (has_collide) { + code += " }\n"; + } + code += " }\n"; + + if (has_process_custom) { + code += " {\n\n"; + code += code_map[TYPE_PROCESS_CUSTOM].replace("\n ", "\n "); + code += " }\n"; + } + + code += "}\n\n"; + + global_compute_code += "float __rand_from_seed(inout uint seed) {\n"; + global_compute_code += " int k;\n"; + global_compute_code += " int s = int(seed);\n"; + global_compute_code += " if (s == 0)\n"; + global_compute_code += " s = 305420679;\n"; + global_compute_code += " k = s / 127773;\n"; + global_compute_code += " s = 16807 * (s - k * 127773) - 2836 * k;\n"; + global_compute_code += " if (s < 0)\n"; + global_compute_code += " s += 2147483647;\n"; + global_compute_code += " seed = uint(s);\n"; + global_compute_code += " return float(seed % uint(65536)) / 65535.0;\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "float __rand_from_seed_m1_p1(inout uint seed) {\n"; + global_compute_code += " return __rand_from_seed(seed) * 2.0 - 1.0;\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "float __randf_range(inout uint seed, float from, float to) {\n"; + global_compute_code += " return __rand_from_seed(seed) * (to - from) + from;\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "vec3 __randv_range(inout uint seed, vec3 from, vec3 to) {\n"; + global_compute_code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "uint __hash(uint x) {\n"; + global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; + global_compute_code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; + global_compute_code += " x = (x >> uint(16)) ^ x;\n"; + global_compute_code += " return x;\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "mat3 __build_rotation_mat3(vec3 axis, float angle) {\n"; + global_compute_code += " axis = normalize(axis);\n"; + global_compute_code += " float s = sin(angle);\n"; + global_compute_code += " float c = cos(angle);\n"; + global_compute_code += " float oc = 1.0 - c;\n"; + global_compute_code += " return mat3(vec3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s), vec3(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s), vec3(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c));\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "mat4 __build_rotation_mat4(vec3 axis, float angle) {\n"; + global_compute_code += " axis = normalize(axis);\n"; + global_compute_code += " float s = sin(angle);\n"; + global_compute_code += " float c = cos(angle);\n"; + global_compute_code += " float oc = 1.0 - c;\n"; + global_compute_code += " return mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n"; + global_compute_code += "}\n\n"; + + global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n"; + global_compute_code += " return normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n"; + global_compute_code += "}\n\n"; } //set code secretly global_code += "\n\n"; String final_code = global_code; + final_code += global_compute_code; final_code += global_code_per_node; final_code += global_expressions; String tcode = code; @@ -1621,7 +1943,7 @@ void VisualShader::_update_shader() const { const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param); } if (previous_code != final_code) { - const_cast<VisualShader *>(this)->emit_signal("changed"); + const_cast<VisualShader *>(this)->emit_signal(SNAME("changed")); } previous_code = final_code; } @@ -1632,7 +1954,7 @@ void VisualShader::_queue_update() { } dirty.set(); - call_deferred("_update_shader"); + call_deferred(SNAME("_update_shader")); } void VisualShader::_input_type_changed(Type p_type, int p_id) { @@ -1695,9 +2017,11 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_VERTEX); BIND_ENUM_CONSTANT(TYPE_FRAGMENT); BIND_ENUM_CONSTANT(TYPE_LIGHT); - BIND_ENUM_CONSTANT(TYPE_EMIT); + BIND_ENUM_CONSTANT(TYPE_START); BIND_ENUM_CONSTANT(TYPE_PROCESS); - BIND_ENUM_CONSTANT(TYPE_END); + BIND_ENUM_CONSTANT(TYPE_COLLIDE); + BIND_ENUM_CONSTANT(TYPE_START_CUSTOM); + BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM); BIND_ENUM_CONSTANT(TYPE_SKY); BIND_ENUM_CONSTANT(TYPE_MAX); @@ -1708,11 +2032,20 @@ void VisualShader::_bind_methods() { VisualShader::VisualShader() { dirty.set(); for (int i = 0; i < TYPE_MAX; i++) { - Ref<VisualShaderNodeOutput> output; - output.instance(); - output->shader_type = Type(i); - output->shader_mode = shader_mode; - graph[i].nodes[NODE_ID_OUTPUT].node = output; + if (i > (int)TYPE_LIGHT && i < (int)TYPE_SKY) { + Ref<VisualShaderNodeParticleOutput> output; + output.instantiate(); + output->shader_type = Type(i); + output->shader_mode = shader_mode; + graph[i].nodes[NODE_ID_OUTPUT].node = output; + } else { + Ref<VisualShaderNodeOutput> output; + output.instantiate(); + output->shader_type = Type(i); + output->shader_mode = shader_mode; + graph[i].nodes[NODE_ID_OUTPUT].node = output; + } + graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150); } } @@ -1841,27 +2174,45 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, - // Particles, Emit - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Start + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + // Particles, Start (Custom) + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Particles, Process + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, @@ -1871,20 +2222,39 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Particles, End - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Process (Custom) + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + // Particles, Collide + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "collision_normal", "COLLISION_NORMAL" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR_INT, "index", "INDEX" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Sky, Sky { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" }, @@ -1960,11 +2330,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, - // Particles, Vertex - { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0, 0.0, 1.0)" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + // Particles + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2008,7 +2380,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T while (preview_ports[idx].mode != Shader::MODE_MAX) { if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) { - code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n"; + code = " " + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n"; break; } idx++; @@ -2017,19 +2389,19 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T if (code == String()) { switch (get_output_port_type(0)) { case PORT_TYPE_SCALAR: { - code = "\t" + p_output_vars[0] + " = 0.0;\n"; + code = " " + p_output_vars[0] + " = 0.0;\n"; } break; case PORT_TYPE_SCALAR_INT: { - code = "\t" + p_output_vars[0] + " = 0;\n"; + code = " " + p_output_vars[0] + " = 0;\n"; } break; case PORT_TYPE_VECTOR: { - code = "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + code = " " + p_output_vars[0] + " = vec3(0.0);\n"; } break; case PORT_TYPE_TRANSFORM: { - code = "\t" + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } break; case PORT_TYPE_BOOLEAN: { - code = "\t" + p_output_vars[0] + " = false;\n"; + code = " " + p_output_vars[0] + " = false;\n"; } break; default: //default (none found) is scalar break; @@ -2045,14 +2417,14 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T while (ports[idx].mode != Shader::MODE_MAX) { if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) { - code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n"; + code = " " + p_output_vars[0] + " = " + ports[idx].string + ";\n"; break; } idx++; } if (code == String()) { - code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar + code = " " + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar } return code; @@ -2064,7 +2436,7 @@ void VisualShaderNodeInput::set_input_name(String p_name) { input_name = p_name; emit_changed(); if (get_input_type_by_name(input_name) != prev_type) { - emit_signal("input_type_changed"); + emit_signal(SNAME("input_type_changed")); } } @@ -2175,6 +2547,14 @@ Vector<StringName> VisualShaderNodeInput::get_editable_properties() const { return props; } +void VisualShaderNodeInput::set_shader_type(VisualShader::Type p_shader_type) { + shader_type = p_shader_type; +} + +void VisualShaderNodeInput::set_shader_mode(Shader::Mode p_shader_mode) { + shader_mode = p_shader_mode; +} + void VisualShaderNodeInput::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name); ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name); @@ -2342,24 +2722,46 @@ VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_ return UniformType::UNIFORM_TYPE_FLOAT; } +VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < uniforms.size()) { + switch (uniforms[p_idx].type) { + case UniformType::UNIFORM_TYPE_FLOAT: + return PORT_TYPE_SCALAR; + case UniformType::UNIFORM_TYPE_INT: + return PORT_TYPE_SCALAR_INT; + case UniformType::UNIFORM_TYPE_SAMPLER: + return PORT_TYPE_SAMPLER; + case UniformType::UNIFORM_TYPE_VECTOR: + return PORT_TYPE_VECTOR; + case UniformType::UNIFORM_TYPE_TRANSFORM: + return PORT_TYPE_TRANSFORM; + case UniformType::UNIFORM_TYPE_COLOR: + return PORT_TYPE_VECTOR; + default: + break; + } + } + return PORT_TYPE_SCALAR; +} + String VisualShaderNodeUniformRef::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 (uniform_type) { case UniformType::UNIFORM_TYPE_FLOAT: if (uniform_name == "[None]") { - return "\t" + p_output_vars[0] + " = 0.0;\n"; + return " " + p_output_vars[0] + " = 0.0;\n"; } - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; case UniformType::UNIFORM_TYPE_INT: - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; case UniformType::UNIFORM_TYPE_BOOLEAN: - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; case UniformType::UNIFORM_TYPE_VECTOR: - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; case UniformType::UNIFORM_TYPE_TRANSFORM: - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; case UniformType::UNIFORM_TYPE_COLOR: { - String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; + String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; + code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; return code; } break; case UniformType::UNIFORM_TYPE_SAMPLER: @@ -2412,6 +2814,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" }, + // Spatial, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, @@ -2455,30 +2859,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, - // Particles, Emit - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_EMIT, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - // Particles, Process - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - // Particles, End - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_END, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, + // Sky, Sky { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, @@ -2551,9 +2932,13 @@ String VisualShaderNodeOutput::get_output_port_name(int p_port) const { } bool VisualShaderNodeOutput::is_port_separator(int p_index) const { + if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_VERTEX) { + String name = get_input_port_name(p_index); + return bool(name == "Model View Matrix"); + } if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { String name = get_input_port_name(p_index); - return (name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold"); + return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold"); } return false; } @@ -2572,9 +2957,9 @@ String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader:: if (p_input_vars[count] != String()) { String s = ports[idx].string; if (s.find(":") != -1) { - code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n"; + code += " " + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n"; } else { - code += "\t" + s + " = " + p_input_vars[count] + ";\n"; + code += " " + s + " = " + p_input_vars[count] + ";\n"; } } count++; @@ -2592,7 +2977,7 @@ VisualShaderNodeOutput::VisualShaderNodeOutput() { void VisualShaderNodeUniform::set_uniform_name(const String &p_name) { uniform_name = p_name; - emit_signal("name_changed"); + emit_signal(SNAME("name_changed")); emit_changed(); } @@ -3249,11 +3634,11 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad String _expression = expression; _expression = _expression.insert(0, "\n"); - _expression = _expression.replace("\n", "\n\t\t"); + _expression = _expression.replace("\n", "\n "); static Vector<String> pre_symbols; if (pre_symbols.is_empty()) { - pre_symbols.push_back("\t"); + pre_symbols.push_back(" "); pre_symbols.push_back(","); pre_symbols.push_back(";"); pre_symbols.push_back("{"); @@ -3273,7 +3658,7 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad static Vector<String> post_symbols; if (post_symbols.is_empty()) { - post_symbols.push_back("\t"); + post_symbols.push_back(" "); post_symbols.push_back("\n"); post_symbols.push_back(","); post_symbols.push_back(";"); @@ -3332,14 +3717,14 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad default: continue; } - output_initializer += "\t" + p_output_vars[i] + " = " + tk + ";\n"; + output_initializer += " " + p_output_vars[i] + " = " + tk + ";\n"; } String code; code += output_initializer; - code += "\t{"; + code += " {"; code += _expression; - code += "\n\t}\n"; + code += "\n }\n"; return code; } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 841672294e..880c401b29 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -51,9 +51,11 @@ public: TYPE_VERTEX, TYPE_FRAGMENT, TYPE_LIGHT, - TYPE_EMIT, + TYPE_START, TYPE_PROCESS, - TYPE_END, + TYPE_COLLIDE, + TYPE_START_CUSTOM, + TYPE_PROCESS_CUSTOM, TYPE_SKY, TYPE_MAX }; @@ -199,9 +201,12 @@ class VisualShaderNode : public Resource { Map<int, Variant> default_input_values; Map<int, bool> connected_input_ports; Map<int, int> connected_output_ports; + Map<int, bool> expanded_output_ports; protected: bool simple_decl = true; + bool disabled = false; + static void _bind_methods(); public: @@ -227,6 +232,8 @@ public: Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied) Array get_default_input_values() const; virtual void set_default_input_values(const Array &p_values); + virtual void remove_input_port_default_value(int p_port); + virtual void clear_default_input_values(); virtual int get_output_port_count() const = 0; virtual PortType get_output_port_type(int p_port) const = 0; @@ -245,10 +252,20 @@ public: void set_input_port_connected(int p_port, bool p_connected); virtual bool is_generate_input_var(int p_port) const; + virtual bool is_output_port_expandable(int p_port) const; + void _set_output_ports_expanded(const Array &p_data); + Array _get_output_ports_expanded() const; + void _set_output_port_expanded(int p_port, bool p_expanded); + bool _is_output_port_expanded(int p_port) const; + int get_expanded_output_port_count() const; + virtual bool is_code_generated() const; virtual bool is_show_prop_names() const; virtual bool is_use_prop_slots() const; + bool is_disabled() const; + void set_disabled(bool p_disabled = true); + virtual Vector<StringName> get_editable_properties() const; virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; @@ -292,6 +309,8 @@ protected: virtual void set_input_port_default_value(int p_port, const Variant &p_value) override; virtual void set_default_input_values(const Array &p_values) override; + virtual void remove_input_port_default_value(int p_port) override; + virtual void clear_default_input_values() override; protected: void _set_input_port_default_value(int p_port, const Variant &p_value); @@ -331,6 +350,10 @@ class VisualShaderNodeInput : public VisualShaderNode { String input_name = "[None]"; +public: + void set_shader_type(VisualShader::Type p_shader_type); + void set_shader_mode(Shader::Mode p_shader_mode); + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const override; @@ -494,6 +517,7 @@ public: String get_uniform_name_by_index(int p_idx) const; UniformType get_uniform_type_by_name(const String &p_name) const; UniformType get_uniform_type_by_index(int p_idx) const; + PortType get_port_type_by_index(int p_idx) const; virtual Vector<StringName> get_editable_properties() const override; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 7943b95177..6fd6fd8f3b 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -66,7 +66,7 @@ String VisualShaderNodeFloatConstant::get_output_port_name(int p_port) const { } String VisualShaderNodeFloatConstant::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 { - return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n"; + return " " + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n"; } void VisualShaderNodeFloatConstant::set_constant(float p_value) { @@ -125,7 +125,7 @@ String VisualShaderNodeIntConstant::get_output_port_name(int p_port) const { } String VisualShaderNodeIntConstant::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 { - return "\t" + p_output_vars[0] + " = " + itos(constant) + ";\n"; + return " " + p_output_vars[0] + " = " + itos(constant) + ";\n"; } void VisualShaderNodeIntConstant::set_constant(int p_value) { @@ -184,7 +184,7 @@ String VisualShaderNodeBooleanConstant::get_output_port_name(int p_port) const { } String VisualShaderNodeBooleanConstant::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 { - return "\t" + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n"; + return " " + p_output_vars[0] + " = " + (constant ? "true" : "false") + ";\n"; } void VisualShaderNodeBooleanConstant::set_constant(bool p_value) { @@ -242,10 +242,17 @@ String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const { return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port } +bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; +} + String VisualShaderNodeColorConstant::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 { String code; - code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n"; - code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n"; + code += " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n"; + code += " " + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n"; return code; } @@ -306,7 +313,7 @@ String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const { } String VisualShaderNodeVec3Constant::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 { - return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n"; + return " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.x, constant.y, constant.z) + ";\n"; } void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) { @@ -334,10 +341,10 @@ void VisualShaderNodeVec3Constant::_bind_methods() { VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() { } -////////////// Transform +////////////// Transform3D String VisualShaderNodeTransformConstant::get_caption() const { - return "Transform"; + return "Transform3D"; } int VisualShaderNodeTransformConstant::get_input_port_count() const { @@ -365,10 +372,10 @@ String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const } String VisualShaderNodeTransformConstant::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 { - Transform t = constant; + Transform3D t = constant; t.basis.transpose(); - String code = "\t" + p_output_vars[0] + " = mat4("; + String code = " " + p_output_vars[0] + " = mat4("; code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[0].x, t.basis[0].y, t.basis[0].z); code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[1].x, t.basis[1].y, t.basis[1].z); code += vformat("vec4(%.6f, %.6f, %.6f, 0.0), ", t.basis[2].x, t.basis[2].y, t.basis[2].z); @@ -376,12 +383,12 @@ String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, Vis return code; } -void VisualShaderNodeTransformConstant::set_constant(Transform p_value) { +void VisualShaderNodeTransformConstant::set_constant(Transform3D p_value) { constant = p_value; emit_changed(); } -Transform VisualShaderNodeTransformConstant::get_constant() const { +Transform3D VisualShaderNodeTransformConstant::get_constant() const { return constant; } @@ -395,7 +402,7 @@ void VisualShaderNodeTransformConstant::_bind_methods() { ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant); ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "constant"), "set_constant", "get_constant"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "constant"), "set_constant", "get_constant"); } VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() { @@ -455,6 +462,13 @@ String VisualShaderNodeTexture::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } +bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; +} + String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const { if (p_port == 0) { return "default"; @@ -509,20 +523,20 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\tvec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; } else { - code += "\tvec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\tvec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; + code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; } else { - code += "\tvec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; return code; } @@ -530,98 +544,98 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String id = p_input_vars[2]; String code; - code += "\t{\n"; + code += " {\n"; if (id == String()) { - code += "\t\tvec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; } else { if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; } else { - code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; + code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; } else { - code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; + code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; } - code += "\t}\n"; + code += " }\n"; return code; } if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n"; + code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n"; + code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = _tex_read.a;\n"; + code += " }\n"; return code; } if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n"; + code += " vec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n"; + code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = _tex_read.a;\n"; + code += " }\n"; return code; } if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; + code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; + code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; } else { - code += "\t\tvec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = _tex_read.a;\n"; + code += " }\n"; return code; } @@ -629,44 +643,44 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: { if (source == SOURCE_DEPTH) { String code; - code += "\t" + p_output_vars[0] + " = 0.0;\n"; - code += "\t" + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = 0.0;\n"; + code += " " + p_output_vars[1] + " = 1.0;\n"; return code; } } if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; + code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; } else { - code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tfloat _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; + code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; } else { - code += "\t\tfloat _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; + code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; } - code += "\t\t" + p_output_vars[0] + " = _depth;\n"; - code += "\t\t" + p_output_vars[1] + " = 1.0;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = _depth;\n"; + code += " " + p_output_vars[1] + " = 1.0;\n"; + code += " }\n"; return code; } else if (source == SOURCE_DEPTH) { String code; - code += "\t" + p_output_vars[0] + " = 0.0;\n"; - code += "\t" + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = 0.0;\n"; + code += " " + p_output_vars[1] + " = 1.0;\n"; return code; } //none String code; - code += "\t" + p_output_vars[0] + " = vec3(0.0);\n"; - code += "\t" + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec3(0.0);\n"; + code += " " + p_output_vars[1] + " = 1.0;\n"; return code; } @@ -693,7 +707,7 @@ void VisualShaderNodeTexture::set_source(Source p_source) { break; } emit_changed(); - emit_signal("editor_refresh_request"); + emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const { @@ -775,7 +789,7 @@ void VisualShaderNodeTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort"), "set_source", "get_source"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type"); BIND_ENUM_CONSTANT(SOURCE_TEXTURE); BIND_ENUM_CONSTANT(SOURCE_SCREEN); @@ -791,7 +805,7 @@ void VisualShaderNodeTexture::_bind_methods() { VisualShaderNodeTexture::VisualShaderNodeTexture() { } -////////////// Curve +////////////// CurveTexture String VisualShaderNodeCurveTexture::get_caption() const { return "CurveTexture"; @@ -842,11 +856,11 @@ String VisualShaderNodeCurveTexture::generate_global(Shader::Mode p_mode, Visual String VisualShaderNodeCurveTexture::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 (p_input_vars[0] == String()) { - return "\t" + p_output_vars[0] + " = 0.0;\n"; + return " " + p_output_vars[0] + " = 0.0;\n"; } String id = make_unique_id(p_type, p_id, "curve"); String code; - code += "\t" + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ", 0.0)).r;\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).r;\n"; return code; } @@ -875,6 +889,90 @@ VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() { allow_v_resize = false; } +////////////// CurveXYZTexture + +String VisualShaderNodeCurveXYZTexture::get_caption() const { + return "CurveXYZTexture"; +} + +int VisualShaderNodeCurveXYZTexture::get_input_port_count() const { + return 1; +} + +VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeCurveXYZTexture::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeCurveXYZTexture::get_output_port_count() const { + return 1; +} + +VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeCurveXYZTexture::get_output_port_name(int p_port) const { + return String(); +} + +void VisualShaderNodeCurveXYZTexture::set_texture(Ref<CurveXYZTexture> p_texture) { + texture = p_texture; + emit_changed(); +} + +Ref<CurveXYZTexture> VisualShaderNodeCurveXYZTexture::get_texture() const { + return texture; +} + +Vector<StringName> VisualShaderNodeCurveXYZTexture::get_editable_properties() const { + Vector<StringName> props; + props.push_back("texture"); + return props; +} + +String VisualShaderNodeCurveXYZTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform sampler2D " + make_unique_id(p_type, p_id, "curve3d") + ";\n"; +} + +String VisualShaderNodeCurveXYZTexture::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 (p_input_vars[0] == String()) { + return " " + p_output_vars[0] + " = vec3(0.0);\n"; + } + String id = make_unique_id(p_type, p_id, "curve3d"); + String code; + code += " " + p_output_vars[0] + " = texture(" + id + ", vec2(" + p_input_vars[0] + ")).rgb;\n"; + return code; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveXYZTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "curve3d"); + dtp.param = texture; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +void VisualShaderNodeCurveXYZTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &VisualShaderNodeCurveXYZTexture::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeCurveXYZTexture::get_texture); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "CurveXYZTexture"), "set_texture", "get_texture"); +} + +bool VisualShaderNodeCurveXYZTexture::is_use_prop_slots() const { + return true; +} + +VisualShaderNodeCurveXYZTexture::VisualShaderNodeCurveXYZTexture() { + simple_decl = true; + allow_v_resize = false; +} + ////////////// Sample3D int VisualShaderNodeSample3D::get_input_port_count() const { @@ -917,6 +1015,13 @@ String VisualShaderNodeSample3D::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } +bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; +} + String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const { if (p_port == 0) { return "default"; @@ -935,7 +1040,7 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader String code; if (source == SOURCE_TEXTURE || source == SOURCE_PORT) { String id; - code += "\t{\n"; + code += " {\n"; if (source == SOURCE_TEXTURE) { id = make_unique_id(p_type, p_id, "tex3d"); } else { @@ -944,34 +1049,34 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader if (id != String()) { if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; } else { - code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += "\t\tvec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } } else { - code += "\t\tvec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; } - code += "\t\t" + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; + code += " }\n"; return code; } - code += "\t" + p_output_vars[0] + " = vec3(0.0);\n"; - code += "\t" + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec3(0.0);\n"; + code += " " + p_output_vars[1] + " = 1.0;\n"; return code; } void VisualShaderNodeSample3D::set_source(Source p_source) { source = p_source; emit_changed(); - emit_signal("editor_refresh_request"); + emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeSample3D::Source VisualShaderNodeSample3D::get_source() const { @@ -1168,6 +1273,13 @@ String VisualShaderNodeCubemap::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } +bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; +} + Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubemap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { VisualShader::DefaultTextureParam dtp; dtp.name = make_unique_id(p_type, p_id, "cube"); @@ -1213,33 +1325,33 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader: return String(); } - code += "\t{\n"; + code += " {\n"; if (id == String()) { - code += "\t\tvec4 " + id + "_read = vec4(0.0);\n"; - code += "\t\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; - code += "\t}\n"; + code += " vec4 " + id + "_read = vec4(0.0);\n"; + code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; + code += " }\n"; return code; } if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; } else { - code += "\t\tvec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n"; + code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += "\t\tvec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; + code += " }\n"; return code; } @@ -1254,7 +1366,7 @@ String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const { void VisualShaderNodeCubemap::set_source(Source p_source) { source = p_source; emit_changed(); - emit_signal("editor_refresh_request"); + emit_signal(SNAME("editor_refresh_request")); } VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const { @@ -1308,7 +1420,7 @@ void VisualShaderNodeCubemap::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,SamplerPort"), "set_source", "get_source"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "Cubemap"), "set_cube_map", "get_cube_map"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type"); BIND_ENUM_CONSTANT(SOURCE_TEXTURE); BIND_ENUM_CONSTANT(SOURCE_PORT); @@ -1353,7 +1465,7 @@ String VisualShaderNodeFloatOp::get_output_port_name(int p_port) const { } String VisualShaderNodeFloatOp::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 { - String code = "\t" + p_output_vars[0] + " = "; + String code = " " + p_output_vars[0] + " = "; switch (op) { case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; @@ -1409,7 +1521,7 @@ void VisualShaderNodeFloatOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeFloatOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeFloatOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2,Step"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,ATan2,Step"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_ADD); BIND_ENUM_CONSTANT(OP_SUB); @@ -1459,7 +1571,7 @@ String VisualShaderNodeIntOp::get_output_port_name(int p_port) const { } String VisualShaderNodeIntOp::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 { - String code = "\t" + p_output_vars[0] + " = "; + String code = " " + p_output_vars[0] + " = "; switch (op) { case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; @@ -1506,7 +1618,7 @@ void VisualShaderNodeIntOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeIntOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeIntOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_ADD); BIND_ENUM_CONSTANT(OP_SUB); @@ -1553,7 +1665,7 @@ String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const { } String VisualShaderNodeVectorOp::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 { - String code = "\t" + p_output_vars[0] + " = "; + String code = " " + p_output_vars[0] + " = "; switch (op) { case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; @@ -1615,7 +1727,7 @@ void VisualShaderNodeVectorOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross,Atan2,Reflect,Step"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,Cross,ATan2,Reflect,Step"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_ADD); BIND_ENUM_CONSTANT(OP_SUB); @@ -1671,64 +1783,64 @@ String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader: static const char *axisn[3] = { "x", "y", "z" }; switch (op) { case OP_SCREEN: { - code += "\t" + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") * (vec3(1.0) - " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") * (vec3(1.0) - " + p_input_vars[1] + ");\n"; } break; case OP_DIFFERENCE: { - code += "\t" + p_output_vars[0] + " = abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ");\n"; } break; case OP_DARKEN: { - code += "\t" + p_output_vars[0] + " = min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } break; case OP_LIGHTEN: { - code += "\t" + p_output_vars[0] + " = max(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = max(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } break; case OP_OVERLAY: { for (int i = 0; i < 3; i++) { - code += "\t{\n"; - code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; - code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; - code += "\t\tif (base < 0.5) {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n"; - code += "\t\t} else {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n"; - code += "\t\t}\n"; - code += "\t}\n"; + code += " {\n"; + code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += " if (base < 0.5) {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n"; + code += " } else {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n"; + code += " }\n"; + code += " }\n"; } } break; case OP_DODGE: { - code += "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + ") / (vec3(1.0) - " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = (" + p_input_vars[0] + ") / (vec3(1.0) - " + p_input_vars[1] + ");\n"; } break; case OP_BURN: { - code += "\t" + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") / (" + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = vec3(1.0) - (vec3(1.0) - " + p_input_vars[0] + ") / (" + p_input_vars[1] + ");\n"; } break; case OP_SOFT_LIGHT: { for (int i = 0; i < 3; i++) { - code += "\t{\n"; - code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; - code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; - code += "\t\tif (base < 0.5) {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend + 0.5));\n"; - code += "\t\t} else {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - (blend - 0.5)));\n"; - code += "\t\t}\n"; - code += "\t}\n"; + code += " {\n"; + code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += " if (base < 0.5) {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = (base * (blend + 0.5));\n"; + code += " } else {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - (blend - 0.5)));\n"; + code += " }\n"; + code += " }\n"; } } break; case OP_HARD_LIGHT: { for (int i = 0; i < 3; i++) { - code += "\t{\n"; - code += "\t\tfloat base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; - code += "\t\tfloat blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; - code += "\t\tif (base < 0.5) {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0 * blend));\n"; - code += "\t\t} else {\n"; - code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - 2.0 * (blend - 0.5)));\n"; - code += "\t\t}\n"; - code += "\t}\n"; + code += " {\n"; + code += " float base = " + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += " float blend = " + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += " if (base < 0.5) {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0 * blend));\n"; + code += " } else {\n"; + code += " " + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0 - base) * (1.0 - 2.0 * (blend - 0.5)));\n"; + code += " }\n"; + code += " }\n"; } } break; @@ -1785,7 +1897,7 @@ void VisualShaderNodeColorOp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator); ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator); - ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,Soft Light,Hard Light"), "set_operator", "get_operator"); BIND_ENUM_CONSTANT(OP_SCREEN); BIND_ENUM_CONSTANT(OP_DIFFERENCE); @@ -1835,13 +1947,13 @@ String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const { 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 "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; + return " " + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; } else if (op == OP_BxA) { - return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n"; + return " " + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n"; } else if (op == OP_AxB_COMP) { - return "\t" + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } else { - return "\t" + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n"; + return " " + p_output_vars[0] + " = matrixCompMult(" + p_input_vars[1] + ", " + p_input_vars[0] + ");\n"; } } @@ -1873,8 +1985,8 @@ void VisualShaderNodeTransformMult::_bind_methods() { } VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() { - set_input_port_default_value(0, Transform()); - set_input_port_default_value(1, Transform()); + set_input_port_default_value(0, Transform3D()); + set_input_port_default_value(1, Transform3D()); } ////////////// TransformVec Mult @@ -1909,13 +2021,13 @@ String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const String VisualShaderNodeTransformVecMult::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 "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0)).xyz;\n"; + return " " + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0)).xyz;\n"; } else if (op == OP_BxA) { - return "\t" + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + ").xyz;\n"; + return " " + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + ").xyz;\n"; } else if (op == OP_3x3_AxB) { - return "\t" + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0)).xyz;\n"; + return " " + p_output_vars[0] + " = (" + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0)).xyz;\n"; } else { - return "\t" + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + ").xyz;\n"; + return " " + p_output_vars[0] + " = (vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + ").xyz;\n"; } } @@ -1947,7 +2059,7 @@ void VisualShaderNodeTransformVecMult::_bind_methods() { } VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() { - set_input_port_default_value(0, Transform()); + set_input_port_default_value(0, Transform3D()); set_input_port_default_value(1, Vector3()); } @@ -2017,7 +2129,7 @@ String VisualShaderNodeFloatFunc::generate_code(Shader::Mode p_mode, VisualShade "1.0 - $" }; - return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; + return " " + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; } void VisualShaderNodeFloatFunc::set_function(Function p_func) { @@ -2116,7 +2228,7 @@ String VisualShaderNodeIntFunc::generate_code(Shader::Mode p_mode, VisualShader: "sign($)" }; - return "\t" + p_output_vars[0] + " = " + String(int_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; + return " " + p_output_vars[0] + " = " + String(int_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; } void VisualShaderNodeIntFunc::set_function(Function p_func) { @@ -2221,25 +2333,25 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad String code; if (func == FUNC_RGB2HSV) { - code += "\t{\n"; - code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; - code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; - code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; - code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; - code += "\t\tfloat d = q.x - min(q.w, q.y);\n"; - code += "\t\tfloat e = 1.0e-10;\n"; - code += "\t\t" + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; - code += "\t}\n"; + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; + code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; + code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; + code += " float d = q.x - min(q.w, q.y);\n"; + code += " float e = 1.0e-10;\n"; + code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; + code += " }\n"; } else if (func == FUNC_HSV2RGB) { - code += "\t{\n"; - code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; - code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; - code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; - code += "\t\t" + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; - code += "\t}\n"; + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; + code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; + code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; + code += " }\n"; } else { - code += "\t" + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; } return code; @@ -2349,22 +2461,22 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade switch (func) { case FUNC_GRAYSCALE: - code += "\t{\n"; - code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; - code += "\t\tfloat max1 = max(c.r, c.g);\n"; - code += "\t\tfloat max2 = max(max1, c.b);\n"; - code += "\t\tfloat max3 = max(max1, max2);\n"; - code += "\t\t" + p_output_vars[0] + " = vec3(max3, max3, max3);\n"; - code += "\t}\n"; + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " float max1 = max(c.r, c.g);\n"; + code += " float max2 = max(max1, c.b);\n"; + code += " float max3 = max(max1, max2);\n"; + code += " " + p_output_vars[0] + " = vec3(max3, max3, max3);\n"; + code += " }\n"; break; case FUNC_SEPIA: - code += "\t{\n"; - code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; - code += "\t\tfloat r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n"; - code += "\t\tfloat g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n"; - code += "\t\tfloat b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n"; - code += "\t\t" + p_output_vars[0] + " = vec3(r, g, b);\n"; - code += "\t}\n"; + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " float r = (c.r * .393) + (c.g *.769) + (c.b * .189);\n"; + code += " float g = (c.r * .349) + (c.g *.686) + (c.b * .168);\n"; + code += " float b = (c.r * .272) + (c.g *.534) + (c.b * .131);\n"; + code += " " + p_output_vars[0] + " = vec3(r, g, b);\n"; + code += " }\n"; break; } @@ -2438,7 +2550,7 @@ String VisualShaderNodeTransformFunc::generate_code(Shader::Mode p_mode, VisualS }; String code; - code += "\t" + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; return code; } @@ -2468,7 +2580,144 @@ void VisualShaderNodeTransformFunc::_bind_methods() { } VisualShaderNodeTransformFunc::VisualShaderNodeTransformFunc() { - set_input_port_default_value(0, Transform()); + set_input_port_default_value(0, Transform3D()); +} + +////////////// UV Func + +String VisualShaderNodeUVFunc::get_caption() const { + return "UVFunc"; +} + +int VisualShaderNodeUVFunc::get_input_port_count() const { + return 3; +} + +VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + [[fallthrough]]; // uv + case 1: + return PORT_TYPE_VECTOR; // scale + case 2: + return PORT_TYPE_VECTOR; // offset & pivot + default: + break; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "uv"; + case 1: + return "scale"; + case 2: + switch (func) { + case FUNC_PANNING: + return "offset"; + case FUNC_SCALING: + return "pivot"; + case FUNC_MAX: + break; + default: + break; + } + break; + default: + break; + } + return ""; +} + +String VisualShaderNodeUVFunc::get_input_port_default_hint(int p_port) const { + if (p_port == 0) { + return "UV"; + } + return ""; +} + +int VisualShaderNodeUVFunc::get_output_port_count() const { + return 1; +} + +VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeUVFunc::get_output_port_name(int p_port) const { + return "uv"; +} + +bool VisualShaderNodeUVFunc::is_show_prop_names() const { + return true; +} + +String VisualShaderNodeUVFunc::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 { + String code; + + String uv; + if (p_input_vars[0].is_empty()) { + uv = "vec3(UV.xy, 0.0)"; + } else { + uv = vformat("%s", p_input_vars[0]); + } + String scale = vformat("%s", p_input_vars[1]); + String offset_pivot = vformat("%s", p_input_vars[2]); + + switch (func) { + case FUNC_PANNING: { + code += vformat(" %s = fma(%s, %s, %s);\n", p_output_vars[0], offset_pivot, scale, uv); + } break; + 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: + break; + } + return code; +} + +void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_func) { + ERR_FAIL_INDEX(int(p_func), 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 + } + emit_changed(); +} + +VisualShaderNodeUVFunc::Function VisualShaderNodeUVFunc::get_function() const { + return func; +} + +Vector<StringName> VisualShaderNodeUVFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeUVFunc::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeUVFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeUVFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Panning,Scaling"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_PANNING); + BIND_ENUM_CONSTANT(FUNC_SCALING); + BIND_ENUM_CONSTANT(FUNC_MAX); +} + +VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() { + set_input_port_default_value(1, Vector3(1.0, 1.0, 0.0)); // scale + set_input_port_default_value(2, Vector3()); // offset } ////////////// Dot Product @@ -2502,7 +2751,7 @@ String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const { } String VisualShaderNodeDotProduct::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 { - return "\t" + p_output_vars[0] + " = dot(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + return " " + p_output_vars[0] + " = dot(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() { @@ -2541,7 +2790,7 @@ String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const { } String VisualShaderNodeVectorLen::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 { - return "\t" + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n"; + return " " + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n"; } VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() { @@ -2579,11 +2828,11 @@ String VisualShaderNodeDeterminant::get_output_port_name(int p_port) const { } String VisualShaderNodeDeterminant::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 { - return "\t" + p_output_vars[0] + " = determinant(" + p_input_vars[0] + ");\n"; + return " " + p_output_vars[0] + " = determinant(" + p_input_vars[0] + ");\n"; } VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() { - set_input_port_default_value(0, Transform()); + set_input_port_default_value(0, Transform3D()); } ////////////// Scalar Derivative Function @@ -2624,7 +2873,7 @@ String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, }; String code; - code += "\t" + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; return code; } @@ -2696,7 +2945,7 @@ String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, }; String code; - code += "\t" + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; return code; } @@ -2784,7 +3033,7 @@ String VisualShaderNodeClamp::get_output_port_name(int p_port) const { } String VisualShaderNodeClamp::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 { - return "\t" + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = clamp(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } void VisualShaderNodeClamp::set_op_type(OpType p_op_type) { @@ -2883,7 +3132,7 @@ String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const { } String VisualShaderNodeFaceForward::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 { - return "\t" + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } VisualShaderNodeFaceForward::VisualShaderNodeFaceForward() { @@ -2930,7 +3179,7 @@ String VisualShaderNodeOuterProduct::get_output_port_name(int p_port) const { } String VisualShaderNodeOuterProduct::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 { - return "\t" + p_output_vars[0] + " = outerProduct(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0));\n"; + return " " + p_output_vars[0] + " = outerProduct(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0));\n"; } VisualShaderNodeOuterProduct::VisualShaderNodeOuterProduct() { @@ -3034,7 +3283,7 @@ VisualShaderNodeStep::OpType VisualShaderNodeStep::get_op_type() const { } String VisualShaderNodeStep::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 { - return "\t" + p_output_vars[0] + " = step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + return " " + p_output_vars[0] + " = step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } Vector<StringName> VisualShaderNodeStep::get_editable_properties() const { @@ -3161,7 +3410,7 @@ VisualShaderNodeSmoothStep::OpType VisualShaderNodeSmoothStep::get_op_type() con } String VisualShaderNodeSmoothStep::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 { - return "\t" + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = smoothstep(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } Vector<StringName> VisualShaderNodeSmoothStep::get_editable_properties() const { @@ -3224,7 +3473,7 @@ String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const { } String VisualShaderNodeVectorDistance::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 { - return "\t" + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + return " " + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() { @@ -3274,7 +3523,7 @@ String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const { } String VisualShaderNodeVectorRefract::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 { - return "\t" + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = refract(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } VisualShaderNodeVectorRefract::VisualShaderNodeVectorRefract() { @@ -3377,7 +3626,7 @@ VisualShaderNodeMix::OpType VisualShaderNodeMix::get_op_type() const { } String VisualShaderNodeMix::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 { - return "\t" + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = mix(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } Vector<StringName> VisualShaderNodeMix::get_editable_properties() const { @@ -3441,7 +3690,7 @@ String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const { } String VisualShaderNodeVectorCompose::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 { - return "\t" + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() { @@ -3489,7 +3738,7 @@ String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const } String VisualShaderNodeTransformCompose::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 { - return "\t" + p_output_vars[0] + " = mat4(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0), vec4(" + p_input_vars[2] + ", 0.0), vec4(" + p_input_vars[3] + ", 1.0));\n"; + return " " + p_output_vars[0] + " = mat4(vec4(" + p_input_vars[0] + ", 0.0), vec4(" + p_input_vars[1] + ", 0.0), vec4(" + p_input_vars[2] + ", 0.0), vec4(" + p_input_vars[3] + ", 1.0));\n"; } VisualShaderNodeTransformCompose::VisualShaderNodeTransformCompose() { @@ -3536,9 +3785,9 @@ String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const { String VisualShaderNodeVectorDecompose::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 { String code; - code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n"; - code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; - code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n"; + code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; + code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; return code; } @@ -3586,15 +3835,15 @@ String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) cons String VisualShaderNodeTransformDecompose::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 { String code; - code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n"; - code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n"; - code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n"; - code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n"; + code += " " + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n"; + code += " " + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n"; + code += " " + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n"; return code; } VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() { - set_input_port_default_value(0, Transform()); + set_input_port_default_value(0, Transform3D()); } ////////////// Float Uniform @@ -3644,7 +3893,7 @@ String VisualShaderNodeFloatUniform::generate_global(Shader::Mode p_mode, Visual } String VisualShaderNodeFloatUniform::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 { - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeFloatUniform::is_show_prop_names() const { @@ -3815,7 +4064,7 @@ String VisualShaderNodeIntUniform::generate_global(Shader::Mode p_mode, VisualSh } String VisualShaderNodeIntUniform::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 { - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeIntUniform::is_show_prop_names() const { @@ -3899,7 +4148,7 @@ void VisualShaderNodeIntUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntUniform::set_default_value); ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntUniform::get_default_value); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range+Step"), "set_hint", "get_hint"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step"), "set_hint", "get_hint"); ADD_PROPERTY(PropertyInfo(Variant::INT, "min"), "set_min", "get_min"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max"), "set_max", "get_max"); ADD_PROPERTY(PropertyInfo(Variant::INT, "step"), "set_step", "get_step"); @@ -4001,7 +4250,7 @@ String VisualShaderNodeBooleanUniform::generate_global(Shader::Mode p_mode, Visu } String VisualShaderNodeBooleanUniform::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 { - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeBooleanUniform::is_show_prop_names() const { @@ -4101,8 +4350,8 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual } String VisualShaderNodeColorUniform::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 { - String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; + String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; + code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; return code; } @@ -4199,7 +4448,7 @@ String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualS } String VisualShaderNodeVec3Uniform::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 { - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } void VisualShaderNodeVec3Uniform::_bind_methods() { @@ -4280,12 +4529,12 @@ bool VisualShaderNodeTransformUniform::is_default_value_enabled() const { return default_value_enabled; } -void VisualShaderNodeTransformUniform::set_default_value(const Transform &p_value) { +void VisualShaderNodeTransformUniform::set_default_value(const Transform3D &p_value) { default_value = p_value; emit_changed(); } -Transform VisualShaderNodeTransformUniform::get_default_value() const { +Transform3D VisualShaderNodeTransformUniform::get_default_value() const { return default_value; } @@ -4303,7 +4552,7 @@ String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, Vi } String VisualShaderNodeTransformUniform::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 { - return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } void VisualShaderNodeTransformUniform::_bind_methods() { @@ -4314,7 +4563,7 @@ void VisualShaderNodeTransformUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeTransformUniform::get_default_value); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "default_value"), "set_default_value", "get_default_value"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "default_value"), "set_default_value", "get_default_value"); } bool VisualShaderNodeTransformUniform::is_show_prop_names() const { @@ -4435,23 +4684,23 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual } String id = get_uniform_name(); - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String()) { // Use UV by default. if (p_input_vars[1] == String()) { - code += "\t\tvec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " vec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n"; } else { - code += "\t\tvec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " vec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; + code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n"; } else { - code += "\t\tvec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = n_tex_read.a;\n"; + code += " }\n"; return code; } @@ -4487,7 +4736,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default); ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default"); BIND_ENUM_CONSTANT(TYPE_DATA); @@ -4556,20 +4805,20 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader: String code; code += "// TRIPLANAR FUNCTION GLOBAL CODE\n"; - code += "\tvec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {\n"; - code += "\t\tvec4 samp = vec4(0.0);\n"; - code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;\n"; - code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;\n"; - code += "\t\tsamp += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;\n"; - code += "\t\treturn samp;\n"; - code += "\t}\n"; + code += " vec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos) {\n"; + code += " vec4 samp = vec4(0.0);\n"; + code += " samp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;\n"; + code += " samp += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;\n"; + code += " samp += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;\n"; + code += " return samp;\n"; + code += " }\n"; code += "\n"; - code += "\tuniform vec3 triplanar_scale = vec3(1.0, 1.0, 1.0);\n"; - code += "\tuniform vec3 triplanar_offset;\n"; - code += "\tuniform float triplanar_sharpness = 0.5;\n"; + code += " uniform vec3 triplanar_scale = vec3(1.0, 1.0, 1.0);\n"; + code += " uniform vec3 triplanar_offset;\n"; + code += " uniform float triplanar_sharpness = 0.5;\n"; code += "\n"; - code += "\tvarying vec3 triplanar_power_normal;\n"; - code += "\tvarying vec3 triplanar_pos;\n"; + code += " varying vec3 triplanar_power_normal;\n"; + code += " varying vec3 triplanar_pos;\n"; return code; } @@ -4578,11 +4827,11 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader: String code; if (p_type == VisualShader::TYPE_VERTEX) { - code += "\t// TRIPLANAR FUNCTION VERTEX CODE\n"; - code += "\t\ttriplanar_power_normal = pow(abs(NORMAL), vec3(triplanar_sharpness));\n"; - code += "\t\ttriplanar_power_normal /= dot(triplanar_power_normal, vec3(1.0));\n"; - code += "\t\ttriplanar_pos = VERTEX * triplanar_scale + triplanar_offset;\n"; - code += "\t\ttriplanar_pos *= vec3(1.0, -1.0, 1.0);\n"; + code += " // TRIPLANAR FUNCTION VERTEX CODE\n"; + code += " triplanar_power_normal = pow(abs(NORMAL), vec3(triplanar_sharpness));\n"; + code += " triplanar_power_normal /= dot(triplanar_power_normal, vec3(1.0));\n"; + code += " triplanar_pos = VERTEX * triplanar_scale + triplanar_offset;\n"; + code += " triplanar_pos *= vec3(1.0, -1.0, 1.0);\n"; } return code; @@ -4590,21 +4839,21 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader: String VisualShaderNodeTextureUniformTriplanar::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 { String id = get_uniform_name(); - String code = "\t{\n"; + String code = " {\n"; if (p_input_vars[0] == String() && p_input_vars[1] == String()) { - code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; + code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; } else if (p_input_vars[0] != String() && p_input_vars[1] == String()) { - code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; + code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; } else if (p_input_vars[0] == String() && p_input_vars[1] != String()) { - code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; + code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; } else { - code += "\t\tvec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n"; - code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n"; - code += "\t}\n"; + code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n"; + code += " " + p_output_vars[1] + " = n_tex_read.a;\n"; + code += " }\n"; return code; } @@ -4881,18 +5130,18 @@ String VisualShaderNodeIf::get_output_port_name(int p_port) const { String VisualShaderNodeIf::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 { String code; - code += "\tif(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b - code += "\t{\n"; - code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[3] + ";\n"; - code += "\t}\n"; - code += "\telse if(" + p_input_vars[0] + " < " + p_input_vars[1] + ")\n"; // a < b - code += "\t{\n"; - code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[5] + ";\n"; - code += "\t}\n"; - code += "\telse\n"; // a > b (or a >= b if abs(a - b) < tolerance is false) - code += "\t{\n"; - code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[4] + ";\n"; - code += "\t}\n"; + code += " if(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ")\n"; // abs(a - b) < tolerance eg. a == b + code += " {\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[3] + ";\n"; + code += " }\n"; + code += " else if(" + p_input_vars[0] + " < " + p_input_vars[1] + ")\n"; // a < b + code += " {\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[5] + ";\n"; + code += " }\n"; + code += " else\n"; // a > b (or a >= b if abs(a - b) < tolerance is false) + code += " {\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[4] + ";\n"; + code += " }\n"; return code; } @@ -4997,8 +5246,8 @@ void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) { set_input_port_default_value(2, false); break; case OP_TYPE_TRANSFORM: - set_input_port_default_value(1, Transform()); - set_input_port_default_value(2, Transform()); + set_input_port_default_value(1, Transform3D()); + set_input_port_default_value(2, Transform3D()); break; default: break; @@ -5033,14 +5282,14 @@ void VisualShaderNodeSwitch::_bind_methods() { // static String VisualShaderNodeSwitch::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 { String code; - code += "\tif(" + p_input_vars[0] + ")\n"; - code += "\t{\n"; - code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[1] + ";\n"; - code += "\t}\n"; - code += "\telse\n"; - code += "\t{\n"; - code += "\t\t" + p_output_vars[0] + " = " + p_input_vars[2] + ";\n"; - code += "\t}\n"; + code += " if(" + p_input_vars[0] + ")\n"; + code += " {\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[1] + ";\n"; + code += " }\n"; + code += " else\n"; + code += " {\n"; + code += " " + p_output_vars[0] + " = " + p_input_vars[2] + ";\n"; + code += " }\n"; return code; } @@ -5125,12 +5374,12 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader: } if (is_input_port_connected(2)) { - return "\t" + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + "));\n"; + return " " + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + "));\n"; } else { if (get_input_port_default_value(2)) { - return "\t" + p_output_vars[0] + " = pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n"; + return " " + p_output_vars[0] + " = pow(clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n"; } else { - return "\t" + p_output_vars[0] + " = pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n"; + return " " + p_output_vars[0] + " = pow(1.0 - clamp(dot(" + normal + ", " + view + "), 0.0, 1.0), " + p_input_vars[3] + ");\n"; } } } @@ -5186,7 +5435,7 @@ String VisualShaderNodeIs::generate_code(Shader::Mode p_mode, VisualShader::Type }; String code; - code += "\t" + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; return code; } @@ -5313,37 +5562,37 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader: switch (ctype) { case CTYPE_SCALAR: if (func == FUNC_EQUAL) { - code += "\t" + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");"; + 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 += "\t" + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");"; + code += " " + p_output_vars[0] + " = !(abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");"; } else { - code += "\t" + 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("$", ops[func]) + ";\n"; } break; case CTYPE_SCALAR_INT: - code += "\t" + 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("$", ops[func]) + ";\n"; break; case CTYPE_VECTOR: - code += "\t{\n"; - code += "\t\tbvec3 _bv = " + String(funcs[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n"; - code += "\t\t" + p_output_vars[0] + " = " + String(conds[condition]).replace("$", "_bv") + ";\n"; - code += "\t}\n"; + 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 += " }\n"; break; case CTYPE_BOOLEAN: if (func > FUNC_NOT_EQUAL) { - return "\t" + p_output_vars[0] + " = false;\n"; + return " " + p_output_vars[0] + " = false;\n"; } - code += "\t" + 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("$", ops[func]) + ";\n"; break; case CTYPE_TRANSFORM: if (func > FUNC_NOT_EQUAL) { - return "\t" + p_output_vars[0] + " = false;\n"; + return " " + p_output_vars[0] + " = false;\n"; } - code += "\t" + 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("$", ops[func]) + ";\n"; break; default: @@ -5377,8 +5626,8 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_type) { simple_decl = true; break; case CTYPE_TRANSFORM: - set_input_port_default_value(0, Transform()); - set_input_port_default_value(1, Transform()); + set_input_port_default_value(0, Transform3D()); + set_input_port_default_value(1, Transform3D()); simple_decl = true; break; } @@ -5499,7 +5748,7 @@ String VisualShaderNodeMultiplyAdd::get_output_port_name(int p_port) const { } String VisualShaderNodeMultiplyAdd::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 { - return "\t" + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; + return " " + p_output_vars[0] + " = fma(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) { @@ -5551,3 +5800,127 @@ VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() { set_input_port_default_value(1, 0.0); set_input_port_default_value(2, 0.0); } + +////////////// Billboard + +String VisualShaderNodeBillboard::get_caption() const { + return "GetBillboardMatrix"; +} + +int VisualShaderNodeBillboard::get_input_port_count() const { + return 0; +} + +VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeBillboard::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeBillboard::get_output_port_count() const { + return 1; +} + +VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} + +String VisualShaderNodeBillboard::get_output_port_name(int p_port) const { + return "model_view_matrix"; +} + +String VisualShaderNodeBillboard::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 { + String code; + + switch (billboard_type) { + case BILLBOARD_TYPE_ENABLED: + code += " {\n"; + code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);\n"; + if (keep_scale) { + code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + } + code += " " + p_output_vars[0] + " = __mvm;\n"; + code += " }\n"; + break; + case BILLBOARD_TYPE_FIXED_Y: + code += " {\n"; + code += " mat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], WORLD_MATRIX[1], vec4(normalize(cross(CAMERA_MATRIX[0].xyz, WORLD_MATRIX[1].xyz)), 0.0), WORLD_MATRIX[3]);\n"; + if (keep_scale) { + code += " __mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + } else { + code += " __mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + } + code += " " + p_output_vars[0] + " = __mvm;\n"; + code += " }\n"; + break; + case BILLBOARD_TYPE_PARTICLES: + code += " {\n"; + code += " mat4 __wm = mat4(normalize(CAMERA_MATRIX[0]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[1]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[2]) * length(WORLD_MATRIX[2]), WORLD_MATRIX[3]);\n"; + code += " __wm = __wm * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + code += " " + p_output_vars[0] + " = INV_CAMERA_MATRIX * __wm;\n"; + code += " }\n"; + break; + default: + code += " " + p_output_vars[0] + " = mat4(1.0);\n"; + break; + } + + return code; +} + +bool VisualShaderNodeBillboard::is_show_prop_names() const { + return true; +} + +void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) { + ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX); + billboard_type = p_billboard_type; + simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED); + set_disabled(simple_decl); + emit_changed(); +} + +VisualShaderNodeBillboard::BillboardType VisualShaderNodeBillboard::get_billboard_type() const { + return billboard_type; +} + +void VisualShaderNodeBillboard::set_keep_scale_enabled(bool p_enabled) { + keep_scale = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeBillboard::is_keep_scale_enabled() const { + return keep_scale; +} + +Vector<StringName> VisualShaderNodeBillboard::get_editable_properties() const { + Vector<StringName> props; + props.push_back("billboard_type"); + if (billboard_type == BILLBOARD_TYPE_ENABLED || billboard_type == BILLBOARD_TYPE_FIXED_Y) { + props.push_back("keep_scale"); + } + return props; +} + +void VisualShaderNodeBillboard::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_billboard_type", "billboard_type"), &VisualShaderNodeBillboard::set_billboard_type); + ClassDB::bind_method(D_METHOD("get_billboard_type"), &VisualShaderNodeBillboard::get_billboard_type); + + ClassDB::bind_method(D_METHOD("set_keep_scale_enabled", "enabled"), &VisualShaderNodeBillboard::set_keep_scale_enabled); + ClassDB::bind_method(D_METHOD("is_keep_scale_enabled"), &VisualShaderNodeBillboard::is_keep_scale_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard_type", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particles"), "set_billboard_type", "get_billboard_type"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_scale"), "set_keep_scale_enabled", "is_keep_scale_enabled"); + + BIND_ENUM_CONSTANT(BILLBOARD_TYPE_DISABLED); + BIND_ENUM_CONSTANT(BILLBOARD_TYPE_ENABLED); + BIND_ENUM_CONSTANT(BILLBOARD_TYPE_FIXED_Y); + BIND_ENUM_CONSTANT(BILLBOARD_TYPE_PARTICLES); + BIND_ENUM_CONSTANT(BILLBOARD_TYPE_MAX); +} + +VisualShaderNodeBillboard::VisualShaderNodeBillboard() { + simple_decl = false; +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index d3397fad6f..33a45a4384 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -163,6 +163,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool is_output_port_expandable(int p_port) 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; @@ -208,7 +209,7 @@ public: class VisualShaderNodeTransformConstant : public VisualShaderNodeConstant { GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNodeConstant); - Transform constant; + Transform3D constant; protected: static void _bind_methods(); @@ -226,8 +227,8 @@ 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(Transform p_value); - Transform get_constant() const; + void set_constant(Transform3D p_value); + Transform3D get_constant() const; virtual Vector<StringName> get_editable_properties() const override; @@ -275,6 +276,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool is_output_port_expandable(int p_port) const override; virtual String get_input_port_default_hint(int p_port) const override; @@ -336,6 +338,39 @@ public: /////////////////////////////////////// +class VisualShaderNodeCurveXYZTexture : public VisualShaderNodeResizableBase { + GDCLASS(VisualShaderNodeCurveXYZTexture, VisualShaderNodeResizableBase); + Ref<CurveXYZTexture> texture; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + 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; + 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); + Ref<CurveXYZTexture> get_texture() const; + + virtual Vector<StringName> get_editable_properties() const override; + virtual bool is_use_prop_slots() const override; + + VisualShaderNodeCurveXYZTexture(); +}; + +/////////////////////////////////////// + class VisualShaderNodeSample3D : public VisualShaderNode { GDCLASS(VisualShaderNodeSample3D, VisualShaderNode); @@ -359,6 +394,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool is_output_port_expandable(int p_port) 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; @@ -452,6 +488,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool is_output_port_expandable(int p_port) const override; 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; @@ -1015,6 +1052,51 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTransformFunc::Function) /////////////////////////////////////// +/// UV FUNC +/////////////////////////////////////// + +class VisualShaderNodeUVFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeUVFunc, VisualShaderNode); + +public: + enum Function { + FUNC_PANNING, + FUNC_SCALING, + FUNC_MAX, + }; + +protected: + Function func = FUNC_PANNING; + + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + virtual String get_input_port_default_hint(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual bool is_show_prop_names() 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_function(Function p_op); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeUVFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeUVFunc::Function) + +/////////////////////////////////////// /// DOT /////////////////////////////////////// @@ -1784,7 +1866,7 @@ class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { private: bool default_value_enabled = false; - Transform default_value = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0); + Transform3D default_value = Transform3D(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0); protected: static void _bind_methods(); @@ -1809,8 +1891,8 @@ public: void set_default_value_enabled(bool p_enabled); bool is_default_value_enabled() const; - void set_default_value(const Transform &p_value); - Transform get_default_value() const; + void set_default_value(const Transform3D &p_value); + Transform3D get_default_value() const; bool is_qualifier_supported(Qualifier p_qual) const override; bool is_convertible_to_constant() const override; @@ -2213,4 +2295,51 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::OpType) +class VisualShaderNodeBillboard : public VisualShaderNode { + GDCLASS(VisualShaderNodeBillboard, VisualShaderNode); + +public: + enum BillboardType { + BILLBOARD_TYPE_DISABLED, + BILLBOARD_TYPE_ENABLED, + BILLBOARD_TYPE_FIXED_Y, + BILLBOARD_TYPE_PARTICLES, + BILLBOARD_TYPE_MAX, + }; + +protected: + BillboardType billboard_type = BILLBOARD_TYPE_ENABLED; + bool keep_scale = false; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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; + + virtual bool is_show_prop_names() const override; + + void set_billboard_type(BillboardType p_billboard_type); + BillboardType get_billboard_type() const; + + void set_keep_scale_enabled(bool p_enabled); + bool is_keep_scale_enabled() const; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeBillboard(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeBillboard::BillboardType) + #endif // VISUAL_SHADER_NODES_H diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp new file mode 100644 index 0000000000..2250cf8d95 --- /dev/null +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -0,0 +1,1025 @@ +/*************************************************************************/ +/* visual_shader_particle_nodes.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 "visual_shader_particle_nodes.h" + +// VisualShaderNodeParticleEmitter + +int VisualShaderNodeParticleEmitter::get_output_port_count() const { + return 1; +} + +VisualShaderNodeParticleEmitter::PortType VisualShaderNodeParticleEmitter::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "position"; + } + return String(); +} + +VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() { +} + +// VisualShaderNodeParticleSphereEmitter + +String VisualShaderNodeParticleSphereEmitter::get_caption() const { + return "SphereEmitter"; +} + +int VisualShaderNodeParticleSphereEmitter::get_input_port_count() const { + return 2; +} + +VisualShaderNodeParticleSphereEmitter::PortType VisualShaderNodeParticleSphereEmitter::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "radius"; + } else if (p_port == 1) { + return "inner_radius"; + } + return String(); +} + +String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code; + code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n"; + code += " return __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n"; + code += "}\n\n"; + return code; +} + +String VisualShaderNodeParticleSphereEmitter::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 { + String code; + code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; + return code; +} + +VisualShaderNodeParticleSphereEmitter::VisualShaderNodeParticleSphereEmitter() { + set_input_port_default_value(0, 10.0); + set_input_port_default_value(1, 0.0); +} + +// VisualShaderNodeParticleBoxEmitter + +String VisualShaderNodeParticleBoxEmitter::get_caption() const { + return "BoxEmitter"; +} + +int VisualShaderNodeParticleBoxEmitter::get_input_port_count() const { + return 1; +} + +VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleBoxEmitter::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_VECTOR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "extents"; + } + return String(); +} + +String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code; + code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n"; + code += " vec3 half_extents = extents / 2.0;\n"; + code += " return vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n"; + code += "}\n\n"; + return code; +} + +String VisualShaderNodeParticleBoxEmitter::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 { + String code; + code += " " + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n"; + return code; +} + +VisualShaderNodeParticleBoxEmitter::VisualShaderNodeParticleBoxEmitter() { + set_input_port_default_value(0, Vector3(1.0, 1.0, 1.0)); +} + +// VisualShaderNodeParticleRingEmitter + +String VisualShaderNodeParticleRingEmitter::get_caption() const { + return "RingEmitter"; +} + +int VisualShaderNodeParticleRingEmitter::get_input_port_count() const { + return 3; +} + +VisualShaderNodeParticleRingEmitter::PortType VisualShaderNodeParticleRingEmitter::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "radius"; + } else if (p_port == 1) { + return "inner_radius"; + } else if (p_port == 2) { + return "height"; + } + return String(); +} + +String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code; + code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n"; + code += " float angle = __rand_from_seed(seed) * PI * 2.0;\n"; + code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n"; + code += " return vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n"; + code += "}\n\n"; + return code; +} + +String VisualShaderNodeParticleRingEmitter::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 { + String code; + code = " " + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n"; + return code; +} + +VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() { + set_input_port_default_value(0, 10.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +// VisualShaderNodeParticleMultiplyByAxisAngle + +void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_degrees_mode", "enabled"), &VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode); + ClassDB::bind_method(D_METHOD("is_degrees_mode"), &VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "degrees_mode"), "set_degrees_mode", "is_degrees_mode"); +} + +String VisualShaderNodeParticleMultiplyByAxisAngle::get_caption() const { + return "MultiplyByAxisAngle"; +} + +int VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_count() const { + return 3; +} + +VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_type(int p_port) const { + if (p_port == 0 || p_port == 1) { // position, rotation_axis + return PORT_TYPE_VECTOR; + } + return PORT_TYPE_SCALAR; // angle (degrees/radians) +} + +String VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "position"; + } + if (p_port == 1) { + return "axis"; + } + if (p_port == 2) { + if (degrees_mode) { + return "angle (degrees)"; + } else { + return "angle (radians)"; + } + } + return String(); +} + +bool VisualShaderNodeParticleMultiplyByAxisAngle::is_show_prop_names() const { + return true; +} + +int VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_count() const { + return 1; +} + +VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_name(int p_port) const { + return "position"; +} + +String VisualShaderNodeParticleMultiplyByAxisAngle::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 { + String code; + if (degrees_mode) { + code += " " + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", radians(" + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ")) * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n"; + } else { + code += " " + p_output_vars[0] + " = __build_rotation_mat3(" + (p_input_vars[1].is_empty() ? ("vec3" + (String)get_input_port_default_value(1)) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ") * " + (p_input_vars[0].is_empty() ? "vec3(0.0)" : p_input_vars[0]) + ";\n"; + } + return code; +} + +void VisualShaderNodeParticleMultiplyByAxisAngle::set_degrees_mode(bool p_enabled) { + degrees_mode = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const { + return degrees_mode; +} + +Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const { + Vector<StringName> props; + props.push_back("degrees_mode"); + props.push_back("axis_amount"); + return props; +} + +VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() { + set_input_port_default_value(1, Vector3(1, 0, 0)); + set_input_port_default_value(2, 0.0); +} + +// VisualShaderNodeParticleConeVelocity + +String VisualShaderNodeParticleConeVelocity::get_caption() const { + return "ConeVelocity"; +} + +int VisualShaderNodeParticleConeVelocity::get_input_port_count() const { + return 2; +} + +VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_VECTOR; + } else if (p_port == 1) { + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleConeVelocity::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "direction"; + } else if (p_port == 1) { + return "spread(degrees)"; + } + return String(); +} + +int VisualShaderNodeParticleConeVelocity::get_output_port_count() const { + return 1; +} + +VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "velocity"; + } + return String(); +} + +String VisualShaderNodeParticleConeVelocity::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 { + String code; + code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; + code += " __scalar_buff1 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; + code += " __scalar_buff2 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; + code += " __vec3_buff1 = " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + ";\n"; + code += " __scalar_buff1 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.x, __vec3_buff1.z) : sign(__vec3_buff1.x) * (PI / 2.0);\n"; + code += " __scalar_buff2 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.z)) : (__vec3_buff1.x != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.x)) : sign(__vec3_buff1.y) * (PI / 2.0));\n"; + code += " __vec3_buff1 = vec3(sin(__scalar_buff1), 0.0, cos(__scalar_buff1));\n"; + code += " __vec3_buff2 = vec3(0.0, sin(__scalar_buff2), cos(__scalar_buff2));\n"; + code += " __vec3_buff2.z = __vec3_buff2.z / max(0.0001, sqrt(abs(__vec3_buff2.z)));\n"; + code += " " + p_output_vars[0] + " = normalize(vec3(__vec3_buff1.x * __vec3_buff2.z, __vec3_buff2.y, __vec3_buff1.z * __vec3_buff2.z));\n"; + return code; +} + +VisualShaderNodeParticleConeVelocity::VisualShaderNodeParticleConeVelocity() { + set_input_port_default_value(0, Vector3(1, 0, 0)); + set_input_port_default_value(1, 45.0); +} + +// VisualShaderNodeParticleRandomness + +void VisualShaderNodeParticleRandomness::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type); + ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type"); + + BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR); + BIND_ENUM_CONSTANT(OP_TYPE_MAX); +} + +Vector<StringName> VisualShaderNodeParticleRandomness::get_editable_properties() const { + Vector<StringName> props; + props.push_back("op_type"); + return props; +} + +String VisualShaderNodeParticleRandomness::get_caption() const { + return "ParticleRandomness"; +} + +int VisualShaderNodeParticleRandomness::get_output_port_count() const { + return 1; +} + +VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_output_port_type(int p_port) const { + if (op_type == OP_TYPE_VECTOR) { + return PORT_TYPE_VECTOR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleRandomness::get_output_port_name(int p_port) const { + return "random"; +} + +int VisualShaderNodeParticleRandomness::get_input_port_count() const { + return 2; +} + +VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const { + if (op_type == OP_TYPE_VECTOR) { + return PORT_TYPE_VECTOR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "min"; + } else if (p_port == 1) { + return "max"; + } + return String(); +} + +String VisualShaderNodeParticleRandomness::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 { + String code; + if (op_type == OP_TYPE_SCALAR) { + code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]); + } else if (op_type == OP_TYPE_VECTOR) { + code += vformat(" %s = __randv_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]); + } + return code; +} + +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)); + } + } + op_type = p_op_type; + emit_changed(); +} + +VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::get_op_type() const { + return op_type; +} + +VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 1.0); +} + +// VisualShaderNodeParticleAccelerator + +void VisualShaderNodeParticleAccelerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShaderNodeParticleAccelerator::set_mode); + ClassDB::bind_method(D_METHOD("get_mode"), &VisualShaderNodeParticleAccelerator::get_mode); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Linear,Radial,Tangential"), "set_mode", "get_mode"); + + BIND_ENUM_CONSTANT(MODE_LINEAR); + BIND_ENUM_CONSTANT(MODE_RADIAL) + BIND_ENUM_CONSTANT(MODE_TANGENTIAL); + BIND_ENUM_CONSTANT(MODE_MAX); +} + +Vector<StringName> VisualShaderNodeParticleAccelerator::get_editable_properties() const { + Vector<StringName> props; + props.push_back("mode"); + return props; +} + +String VisualShaderNodeParticleAccelerator::get_caption() const { + return "ParticleAccelerator"; +} + +int VisualShaderNodeParticleAccelerator::get_output_port_count() const { + return 1; +} + +VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} + +String VisualShaderNodeParticleAccelerator::get_output_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeParticleAccelerator::get_input_port_count() const { + return 3; +} + +VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_VECTOR; + } else if (p_port == 1) { + return PORT_TYPE_SCALAR; + } else if (p_port == 2) { + return PORT_TYPE_VECTOR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleAccelerator::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "amount"; + } else if (p_port == 1) { + return "randomness"; + } else if (p_port == 2) { + return "axis"; + } + return String(); +} + +String VisualShaderNodeParticleAccelerator::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 { + String code; + switch (mode) { + case MODE_LINEAR: + code += " " + p_output_vars[0] + " = length(VELOCITY) > 0.0 ? " + "normalize(VELOCITY) * " + (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_RADIAL: + code += " " + p_output_vars[0] + " = length(__diff) > 0.0 ? __ndiff * " + (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_TANGENTIAL: + 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; + } + + return code; +} + +void VisualShaderNodeParticleAccelerator::set_mode(Mode p_mode) { + mode = p_mode; + emit_changed(); +} + +VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::get_mode() const { + return mode; +} + +VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() { + set_input_port_default_value(0, Vector3(1, 1, 1)); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, Vector3(0, -9.8, 0)); +} + +// VisualShaderNodeParticleOutput + +String VisualShaderNodeParticleOutput::get_caption() const { + if (shader_type == VisualShader::TYPE_START) { + return "StartOutput"; + } else if (shader_type == VisualShader::TYPE_PROCESS) { + return "ProcessOutput"; + } else if (shader_type == VisualShader::TYPE_COLLIDE) { + return "CollideOutput"; + } else if (shader_type == VisualShader::TYPE_START_CUSTOM) { + return "CustomStartOutput"; + } else if (shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + return "CustomProcessOutput"; + } + return String(); +} + +int VisualShaderNodeParticleOutput::get_input_port_count() const { + if (shader_type == VisualShader::TYPE_START) { + return 8; + } else if (shader_type == VisualShader::TYPE_COLLIDE) { + return 5; + } else if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + return 6; + } else { // TYPE_PROCESS + return 7; + } + return 0; +} + +VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + return PORT_TYPE_VECTOR; // custom.rgb + } + return PORT_TYPE_BOOLEAN; // active + case 1: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + break; // custom.a (scalar) + } + return PORT_TYPE_VECTOR; // velocity + case 2: + return PORT_TYPE_VECTOR; // color & velocity + case 3: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + return PORT_TYPE_VECTOR; // color + } + break; // alpha (scalar) + case 4: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + break; // alpha + } + if (shader_type == VisualShader::TYPE_PROCESS) { + break; // scale + } + if (shader_type == VisualShader::TYPE_COLLIDE) { + return PORT_TYPE_TRANSFORM; // transform + } + return PORT_TYPE_VECTOR; // position + case 5: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + return PORT_TYPE_TRANSFORM; // transform + } + if (shader_type == VisualShader::TYPE_PROCESS) { + return PORT_TYPE_VECTOR; // rotation_axis + } + break; // scale (scalar) + case 6: + if (shader_type == VisualShader::TYPE_START) { + return PORT_TYPE_VECTOR; // rotation_axis + } + break; + case 7: + break; // angle (scalar) + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleOutput::get_input_port_name(int p_port) const { + String port_name; + switch (p_port) { + case 0: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "custom"; + break; + } + port_name = "active"; + break; + case 1: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "custom_alpha"; + break; + } + port_name = "velocity"; + break; + case 2: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "velocity"; + break; + } + port_name = "color"; + break; + case 3: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "color"; + break; + } + port_name = "alpha"; + break; + case 4: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "alpha"; + break; + } + if (shader_type == VisualShader::TYPE_PROCESS) { + port_name = "scale"; + break; + } + if (shader_type == VisualShader::TYPE_COLLIDE) { + port_name = "transform"; + break; + } + port_name = "position"; + break; + case 5: + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + port_name = "transform"; + break; + } + if (shader_type == VisualShader::TYPE_PROCESS) { + port_name = "rotation_axis"; + break; + } + port_name = "scale"; + break; + case 6: + if (shader_type == VisualShader::TYPE_PROCESS) { + port_name = "angle_in_radians"; + break; + } + port_name = "rotation_axis"; + break; + case 7: + port_name = "angle_in_radians"; + break; + default: + break; + } + if (!port_name.is_empty()) { + return port_name.capitalize(); + } + return String(); +} + +bool VisualShaderNodeParticleOutput::is_port_separator(int p_index) const { + if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) { + String name = get_input_port_name(p_index); + return bool(name == "Scale"); + } + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + String name = get_input_port_name(p_index); + return bool(name == "Velocity"); + } + return false; +} + +String VisualShaderNodeParticleOutput::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 { + String code; + String tab = " "; + + if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) { + if (!p_input_vars[0].is_empty()) { // custom.rgb + code += tab + "CUSTOM.rgb = " + p_input_vars[0] + ";\n"; + } + if (!p_input_vars[1].is_empty()) { // custom.a + code += tab + "CUSTOM.a = " + p_input_vars[1] + ";\n"; + } + if (!p_input_vars[2].is_empty()) { // velocity + code += tab + "VELOCITY = " + p_input_vars[2] + ";\n"; + } + if (!p_input_vars[3].is_empty()) { // color.rgb + code += tab + "COLOR.rgb = " + p_input_vars[3] + ";\n"; + } + if (!p_input_vars[4].is_empty()) { // color.a + code += tab + "COLOR.a = " + p_input_vars[4] + ";\n"; + } + if (!p_input_vars[5].is_empty()) { // transform + code += tab + "TRANSFORM = " + p_input_vars[5] + ";\n"; + } + } else { + if (!p_input_vars[0].is_empty()) { // active (begin) + code += tab + "ACTIVE = " + p_input_vars[0] + ";\n"; + code += tab + "if(ACTIVE) {\n"; + tab += " "; + } + if (!p_input_vars[1].is_empty()) { // velocity + code += tab + "VELOCITY = " + p_input_vars[1] + ";\n"; + } + if (!p_input_vars[2].is_empty()) { // color + code += tab + "COLOR.rgb = " + p_input_vars[2] + ";\n"; + } + if (!p_input_vars[3].is_empty()) { // alpha + code += tab + "COLOR.a = " + p_input_vars[3] + ";\n"; + } + + // position + if (shader_type == VisualShader::TYPE_START) { + code += tab + "if (RESTART_POSITION) {\n"; + if (!p_input_vars[4].is_empty()) { + code += tab + " TRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(" + p_input_vars[4] + ", 1.0));\n"; + } else { + code += tab + " TRANSFORM = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + } + code += tab + " if (RESTART_VELOCITY) {\n"; + code += tab + " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; + code += tab + " }\n"; + code += tab + " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; + code += tab + "}\n"; + } else if (shader_type == VisualShader::TYPE_COLLIDE) { // position + if (!p_input_vars[4].is_empty()) { + code += tab + "TRANSFORM = " + p_input_vars[4] + ";\n"; + } + } + + if (shader_type == VisualShader::TYPE_START || shader_type == VisualShader::TYPE_PROCESS) { + int scale = 5; + int rotation_axis = 6; + int rotation = 7; + if (shader_type == VisualShader::TYPE_PROCESS) { + scale = 4; + rotation_axis = 5; + rotation = 6; + } + String op; + if (shader_type == VisualShader::TYPE_START) { + op = "*="; + } else { + op = "="; + } + + if (!p_input_vars[rotation].is_empty()) { // rotation_axis & angle_in_radians + String axis; + if (p_input_vars[rotation_axis].is_empty()) { + axis = "vec3(0, 1, 0)"; + } else { + axis = p_input_vars[rotation_axis]; + } + code += tab + "TRANSFORM " + op + " __build_rotation_mat4(" + axis + ", " + p_input_vars[rotation] + ");\n"; + } + if (!p_input_vars[scale].is_empty()) { // scale + code += tab + "TRANSFORM " + op + " mat4(vec4(" + p_input_vars[scale] + ", 0, 0, 0), vec4(0, " + p_input_vars[scale] + ", 0, 0), vec4(0, 0, " + p_input_vars[scale] + ", 0), vec4(0, 0, 0, 1));\n"; + } + } + if (!p_input_vars[0].is_empty()) { // active (end) + code += " }\n"; + } + } + return code; +} + +VisualShaderNodeParticleOutput::VisualShaderNodeParticleOutput() { +} + +// EmitParticle + +Vector<StringName> VisualShaderNodeParticleEmit::get_editable_properties() const { + Vector<StringName> props; + props.push_back("flags"); + return props; +} + +void VisualShaderNodeParticleEmit::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &VisualShaderNodeParticleEmit::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &VisualShaderNodeParticleEmit::get_flags); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Position,RotScale,Velocity,Color,Custom"), "set_flags", "get_flags"); + + BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION); + BIND_ENUM_CONSTANT(EMIT_FLAG_ROT_SCALE); + BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY); + BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR); + BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM); +} + +String VisualShaderNodeParticleEmit::get_caption() const { + return "EmitParticle"; +} + +int VisualShaderNodeParticleEmit::get_input_port_count() const { + return 7; +} + +VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_BOOLEAN; + case 1: + return PORT_TYPE_TRANSFORM; + case 2: + return PORT_TYPE_VECTOR; + case 3: + return PORT_TYPE_VECTOR; + case 4: + return PORT_TYPE_SCALAR; + case 5: + return PORT_TYPE_VECTOR; + case 6: + return PORT_TYPE_SCALAR; + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleEmit::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "condition"; + case 1: + return "transform"; + case 2: + return "velocity"; + case 3: + return "color"; + case 4: + return "alpha"; + case 5: + return "custom"; + case 6: + return "custom_alpha"; + } + return String(); +} + +int VisualShaderNodeParticleEmit::get_output_port_count() const { + return 0; +} + +VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleEmit::get_output_port_name(int p_port) const { + return String(); +} + +void VisualShaderNodeParticleEmit::add_flag(EmitFlags p_flag) { + flags |= p_flag; + emit_changed(); +} + +bool VisualShaderNodeParticleEmit::has_flag(EmitFlags p_flag) const { + return flags & p_flag; +} + +void VisualShaderNodeParticleEmit::set_flags(EmitFlags p_flags) { + flags = (int)p_flags; + emit_changed(); +} + +VisualShaderNodeParticleEmit::EmitFlags VisualShaderNodeParticleEmit::get_flags() const { + return EmitFlags(flags); +} + +bool VisualShaderNodeParticleEmit::is_show_prop_names() const { + return true; +} + +bool VisualShaderNodeParticleEmit::is_generate_input_var(int p_port) const { + if (p_port == 0) { + if (!is_input_port_connected(0)) { + return false; + } + } + return true; +} + +String VisualShaderNodeParticleEmit::get_input_port_default_hint(int p_port) const { + switch (p_port) { + case 1: + return "default"; + case 2: + return "default"; + case 3: + return "default"; + case 4: + return "default"; + case 5: + return "default"; + case 6: + return "default"; + } + return String(); +} + +String VisualShaderNodeParticleEmit::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 { + String code; + String tab; + bool default_condition = false; + + if (!is_input_port_connected(0)) { + default_condition = true; + if (get_input_port_default_value(0)) { + tab = " "; + } else { + return code; + } + } else { + tab = " "; + } + + String transform; + if (p_input_vars[1].is_empty()) { + transform = "TRANSFORM"; + } else { + transform = p_input_vars[1]; + } + + String velocity; + if (p_input_vars[2].is_empty()) { + velocity = "VELOCITY"; + } else { + velocity = p_input_vars[2]; + } + + String color; + if (p_input_vars[3].is_empty()) { + color = "COLOR.rgb"; + } else { + color = p_input_vars[3]; + } + + String alpha; + if (p_input_vars[4].is_empty()) { + alpha = "COLOR.a"; + } else { + alpha = p_input_vars[4]; + } + + String custom; + if (p_input_vars[5].is_empty()) { + custom = "CUSTOM.rgb"; + } else { + custom = p_input_vars[5]; + } + + String custom_alpha; + if (p_input_vars[6].is_empty()) { + custom_alpha = "CUSTOM.a"; + } else { + custom_alpha = p_input_vars[6]; + } + + List<String> flags_arr; + + if (has_flag(EmitFlags::EMIT_FLAG_POSITION)) { + flags_arr.push_back("FLAG_EMIT_POSITION"); + } + if (has_flag(EmitFlags::EMIT_FLAG_ROT_SCALE)) { + flags_arr.push_back("FLAG_EMIT_ROT_SCALE"); + } + if (has_flag(EmitFlags::EMIT_FLAG_VELOCITY)) { + flags_arr.push_back("FLAG_EMIT_VELOCITY"); + } + if (has_flag(EmitFlags::EMIT_FLAG_COLOR)) { + flags_arr.push_back("FLAG_EMIT_COLOR"); + } + if (has_flag(EmitFlags::EMIT_FLAG_CUSTOM)) { + flags_arr.push_back("FLAG_EMIT_CUSTOM"); + } + + String flags; + + for (int i = 0; i < flags_arr.size(); i++) { + if (i > 0) { + flags += "|"; + } + flags += flags_arr[i]; + } + + if (flags.is_empty()) { + flags = "uint(0)"; + } + + if (!default_condition) { + code += " if (" + p_input_vars[0] + ") {\n"; + } + + code += tab + "emit_subparticle(" + transform + ", " + velocity + ", vec4(" + color + ", " + alpha + "), vec4(" + custom + ", " + custom_alpha + "), " + flags + ");\n"; + + if (!default_condition) { + code += " }\n"; + } + + return code; +} + +VisualShaderNodeParticleEmit::VisualShaderNodeParticleEmit() { + set_input_port_default_value(0, true); +} diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h new file mode 100644 index 0000000000..ecd187a885 --- /dev/null +++ b/scene/resources/visual_shader_particle_nodes.h @@ -0,0 +1,285 @@ +/*************************************************************************/ +/* visual_shader_particle_nodes.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 VISUAL_SHADER_PARTICLE_NODES_H +#define VISUAL_SHADER_PARTICLE_NODES_H + +#include "scene/resources/visual_shader.h" + +// Emit nodes + +class VisualShaderNodeParticleEmitter : public VisualShaderNode { + GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode); + +public: + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + VisualShaderNodeParticleEmitter(); +}; + +class VisualShaderNodeParticleSphereEmitter : public VisualShaderNodeParticleEmitter { + GDCLASS(VisualShaderNodeParticleSphereEmitter, VisualShaderNodeParticleEmitter); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual String generate_global_per_node(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; + + VisualShaderNodeParticleSphereEmitter(); +}; + +class VisualShaderNodeParticleBoxEmitter : public VisualShaderNodeParticleEmitter { + GDCLASS(VisualShaderNodeParticleBoxEmitter, VisualShaderNodeParticleEmitter); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual String generate_global_per_node(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; + + VisualShaderNodeParticleBoxEmitter(); +}; + +class VisualShaderNodeParticleRingEmitter : public VisualShaderNodeParticleEmitter { + GDCLASS(VisualShaderNodeParticleRingEmitter, VisualShaderNodeParticleEmitter); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual String generate_global_per_node(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; + + VisualShaderNodeParticleRingEmitter(); +}; + +class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode { + GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode); + bool degrees_mode = true; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + virtual bool is_show_prop_names() const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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_degrees_mode(bool p_enabled); + bool is_degrees_mode() const; + Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeParticleMultiplyByAxisAngle(); +}; + +class VisualShaderNodeParticleConeVelocity : public VisualShaderNode { + GDCLASS(VisualShaderNodeParticleConeVelocity, VisualShaderNode); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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; + + VisualShaderNodeParticleConeVelocity(); +}; + +class VisualShaderNodeParticleRandomness : public VisualShaderNode { + GDCLASS(VisualShaderNodeParticleRandomness, VisualShaderNode); + +public: + enum OpType { + OP_TYPE_SCALAR, + OP_TYPE_VECTOR, + OP_TYPE_MAX, + }; + +private: + OpType op_type = OP_TYPE_SCALAR; + +protected: + static void _bind_methods(); + +public: + Vector<StringName> get_editable_properties() const override; + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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_op_type(OpType p_type); + OpType get_op_type() const; + + VisualShaderNodeParticleRandomness(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeParticleRandomness::OpType) + +// Process nodes + +class VisualShaderNodeParticleAccelerator : public VisualShaderNodeOutput { + GDCLASS(VisualShaderNodeParticleAccelerator, VisualShaderNodeOutput); + +public: + enum Mode { + MODE_LINEAR, + MODE_RADIAL, + MODE_TANGENTIAL, + MODE_MAX, + }; + +private: + Mode mode = MODE_LINEAR; + +protected: + static void _bind_methods(); + +public: + Vector<StringName> get_editable_properties() const override; + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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_mode(Mode p_mode); + Mode get_mode() const; + + VisualShaderNodeParticleAccelerator(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeParticleAccelerator::Mode) + +// Common nodes + +class VisualShaderNodeParticleOutput : public VisualShaderNodeOutput { + GDCLASS(VisualShaderNodeParticleOutput, VisualShaderNodeOutput); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + virtual bool is_port_separator(int p_index) 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; + + VisualShaderNodeParticleOutput(); +}; + +class VisualShaderNodeParticleEmit : public VisualShaderNode { + GDCLASS(VisualShaderNodeParticleEmit, VisualShaderNode); + +public: + enum EmitFlags { + EMIT_FLAG_POSITION = 1, + EMIT_FLAG_ROT_SCALE = 2, + EMIT_FLAG_VELOCITY = 4, + EMIT_FLAG_COLOR = 8, + EMIT_FLAG_CUSTOM = 16, + }; + +protected: + int flags = EMIT_FLAG_POSITION | EMIT_FLAG_ROT_SCALE | EMIT_FLAG_VELOCITY | EMIT_FLAG_COLOR | EMIT_FLAG_CUSTOM; + static void _bind_methods(); + +public: + Vector<StringName> get_editable_properties() const override; + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + void add_flag(EmitFlags p_flag); + bool has_flag(EmitFlags p_flag) const; + + void set_flags(EmitFlags p_flags); + EmitFlags get_flags() const; + + virtual bool is_show_prop_names() const override; + virtual bool is_generate_input_var(int p_port) const override; + virtual String get_input_port_default_hint(int p_port) 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; + + VisualShaderNodeParticleEmit(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags) + +#endif diff --git a/scene/resources/visual_shader_sdf_nodes.cpp b/scene/resources/visual_shader_sdf_nodes.cpp index d25e32b070..14c655b129 100644 --- a/scene/resources/visual_shader_sdf_nodes.cpp +++ b/scene/resources/visual_shader_sdf_nodes.cpp @@ -61,7 +61,7 @@ String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const { } String VisualShaderNodeSDFToScreenUV::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 { - return "\t" + p_output_vars[0] + " = vec3(sdf_to_screen_uv(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; + return " " + p_output_vars[0] + " = vec3(sdf_to_screen_uv(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; } VisualShaderNodeSDFToScreenUV::VisualShaderNodeSDFToScreenUV() { @@ -105,7 +105,7 @@ String VisualShaderNodeScreenUVToSDF::get_input_port_default_hint(int p_port) co } String VisualShaderNodeScreenUVToSDF::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 { - return "\t" + p_output_vars[0] + " = vec3(screen_uv_to_sdf(" + (p_input_vars[0] == String() ? "SCREEN_UV" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; + return " " + p_output_vars[0] + " = vec3(screen_uv_to_sdf(" + (p_input_vars[0] == String() ? "SCREEN_UV" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; } VisualShaderNodeScreenUVToSDF::VisualShaderNodeScreenUVToSDF() { @@ -142,7 +142,7 @@ String VisualShaderNodeTextureSDF::get_output_port_name(int p_port) const { } String VisualShaderNodeTextureSDF::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 { - return "\t" + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + ");\n"; + return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + ");\n"; } VisualShaderNodeTextureSDF::VisualShaderNodeTextureSDF() { @@ -179,7 +179,7 @@ String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const } String VisualShaderNodeTextureSDFNormal::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 { - return "\t" + p_output_vars[0] + " = vec3(texture_sdf_normal(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; + return " " + p_output_vars[0] + " = vec3(texture_sdf_normal(" + (p_input_vars[0] == String() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n"; } VisualShaderNodeTextureSDFNormal::VisualShaderNodeTextureSDFNormal() { @@ -240,40 +240,40 @@ String VisualShaderNodeSDFRaymarch::get_output_port_name(int p_port) const { String VisualShaderNodeSDFRaymarch::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 { String code; - code += "\t{\n"; + code += " {\n"; if (p_input_vars[0] == String()) { - code += "\t\tvec2 __from_pos = vec2(0.0f);\n"; + code += " vec2 __from_pos = vec2(0.0f);\n"; } else { - code += "\t\tvec2 __from_pos = " + p_input_vars[0] + ".xy;\n"; + code += " vec2 __from_pos = " + p_input_vars[0] + ".xy;\n"; } if (p_input_vars[1] == String()) { - code += "\t\tvec2 __to_pos = vec2(0.0f);\n"; + code += " vec2 __to_pos = vec2(0.0f);\n"; } else { - code += "\t\tvec2 __to_pos = " + p_input_vars[1] + ".xy;\n"; + code += " vec2 __to_pos = " + p_input_vars[1] + ".xy;\n"; } - code += "\n\t\tvec2 __at = __from_pos;\n"; - code += "\t\tfloat __max_dist = distance(__from_pos, __to_pos);\n"; - code += "\t\tvec2 __dir = normalize(__to_pos - __from_pos);\n\n"; - - code += "\t\tfloat __accum = 0.0f;\n"; - code += "\t\twhile(__accum < __max_dist) {\n"; - code += "\t\t\tfloat __d = texture_sdf(__at);\n"; - code += "\t\t\t__accum += __d;\n"; - code += "\t\t\tif (__d < 0.01f) {\n"; - code += "\t\t\t\tbreak;\n"; - code += "\t\t\t}\n"; - code += "\t\t\t__at += __d * __dir;\n"; - code += "\t\t}\n"; - - code += "\t\tfloat __dist = min(__max_dist, __accum);\n"; - code += "\t\t" + p_output_vars[0] + " = __dist;\n"; - code += "\t\t" + p_output_vars[1] + " = __accum < __max_dist;\n"; - code += "\t\t" + p_output_vars[2] + " = vec3(__from_pos + __dir * __dist, 0.0f);\n"; - - code += "\t}\n"; + code += "\n vec2 __at = __from_pos;\n"; + code += " float __max_dist = distance(__from_pos, __to_pos);\n"; + code += " vec2 __dir = normalize(__to_pos - __from_pos);\n\n"; + + code += " float __accum = 0.0f;\n"; + code += " while(__accum < __max_dist) {\n"; + code += " float __d = texture_sdf(__at);\n"; + code += " __accum += __d;\n"; + code += " if (__d < 0.01f) {\n"; + code += " break;\n"; + code += " }\n"; + code += " __at += __d * __dir;\n"; + code += " }\n"; + + code += " float __dist = min(__max_dist, __accum);\n"; + code += " " + p_output_vars[0] + " = __dist;\n"; + code += " " + p_output_vars[1] + " = __accum < __max_dist;\n"; + code += " " + p_output_vars[2] + " = vec3(__from_pos + __dir * __dist, 0.0f);\n"; + + code += " }\n"; return code; } diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 0a0742753f..eceb42ee14 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -32,290 +32,12 @@ #include "core/config/project_settings.h" #include "scene/2d/camera_2d.h" -#include "scene/2d/visibility_notifier_2d.h" +#include "scene/2d/visible_on_screen_notifier_2d.h" #include "scene/main/window.h" #include "servers/navigation_server_2d.h" #include "servers/physics_server_2d.h" #include "servers/rendering_server.h" -struct SpatialIndexer2D { - struct CellRef { - int ref = 0; - - _FORCE_INLINE_ int inc() { - ref++; - return ref; - } - _FORCE_INLINE_ int dec() { - ref--; - return ref; - } - }; - - struct CellKey { - union { - struct { - int32_t x; - int32_t y; - }; - uint64_t key = 0; - }; - - bool operator==(const CellKey &p_key) const { return key == p_key.key; } - _FORCE_INLINE_ bool operator<(const CellKey &p_key) const { - return key < p_key.key; - } - }; - - struct CellData { - Map<VisibilityNotifier2D *, CellRef> notifiers; - }; - - Map<CellKey, CellData> cells; - int cell_size; - - Map<VisibilityNotifier2D *, Rect2> notifiers; - - struct ViewportData { - Map<VisibilityNotifier2D *, uint64_t> notifiers; - Rect2 rect; - }; - - Map<Viewport *, ViewportData> viewports; - - bool changed = false; - - uint64_t pass = 0; - - void _notifier_update_cells(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect, bool p_add) { - Point2i begin = p_rect.position; - begin /= cell_size; - Point2i end = p_rect.position + p_rect.size; - end /= cell_size; - for (int i = begin.x; i <= end.x; i++) { - for (int j = begin.y; j <= end.y; j++) { - CellKey ck; - ck.x = i; - ck.y = j; - Map<CellKey, CellData>::Element *E = cells.find(ck); - - if (p_add) { - if (!E) { - E = cells.insert(ck, CellData()); - } - E->get().notifiers[p_notifier].inc(); - } else { - ERR_CONTINUE(!E); - if (E->get().notifiers[p_notifier].dec() == 0) { - E->get().notifiers.erase(p_notifier); - if (E->get().notifiers.is_empty()) { - cells.erase(E); - } - } - } - } - } - } - - void _notifier_add(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) { - ERR_FAIL_COND(notifiers.has(p_notifier)); - notifiers[p_notifier] = p_rect; - _notifier_update_cells(p_notifier, p_rect, true); - changed = true; - } - - void _notifier_update(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) { - Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - if (E->get() == p_rect) { - return; - } - - _notifier_update_cells(p_notifier, p_rect, true); - _notifier_update_cells(p_notifier, E->get(), false); - E->get() = p_rect; - changed = true; - } - - void _notifier_remove(VisibilityNotifier2D *p_notifier) { - Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - _notifier_update_cells(p_notifier, E->get(), false); - notifiers.erase(p_notifier); - - List<Viewport *> removed; - for (Map<Viewport *, ViewportData>::Element *F = viewports.front(); F; F = F->next()) { - Map<VisibilityNotifier2D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier); - - if (G) { - F->get().notifiers.erase(G); - removed.push_back(F->key()); - } - } - - while (!removed.is_empty()) { - p_notifier->_exit_viewport(removed.front()->get()); - removed.pop_front(); - } - - changed = true; - } - - void _add_viewport(Viewport *p_viewport, const Rect2 &p_rect) { - ERR_FAIL_COND(viewports.has(p_viewport)); - ViewportData vd; - vd.rect = p_rect; - viewports[p_viewport] = vd; - changed = true; - } - - void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect) { - Map<Viewport *, ViewportData>::Element *E = viewports.find(p_viewport); - ERR_FAIL_COND(!E); - if (E->get().rect == p_rect) { - return; - } - E->get().rect = p_rect; - changed = true; - } - - void _remove_viewport(Viewport *p_viewport) { - ERR_FAIL_COND(!viewports.has(p_viewport)); - List<VisibilityNotifier2D *> removed; - for (Map<VisibilityNotifier2D *, uint64_t>::Element *E = viewports[p_viewport].notifiers.front(); E; E = E->next()) { - removed.push_back(E->key()); - } - - while (!removed.is_empty()) { - removed.front()->get()->_exit_viewport(p_viewport); - removed.pop_front(); - } - - viewports.erase(p_viewport); - } - - void _update() { - if (!changed) { - return; - } - - for (Map<Viewport *, ViewportData>::Element *E = viewports.front(); E; E = E->next()) { - Point2i begin = E->get().rect.position; - begin /= cell_size; - Point2i end = E->get().rect.position + E->get().rect.size; - end /= cell_size; - pass++; - List<VisibilityNotifier2D *> added; - List<VisibilityNotifier2D *> removed; - - uint64_t visible_cells = (uint64_t)(end.x - begin.x) * (uint64_t)(end.y - begin.y); - - if (visible_cells > 10000) { - //well you zoomed out a lot, it's your problem. To avoid freezing in the for loops below, we'll manually check cell by cell - - for (Map<CellKey, CellData>::Element *F = cells.front(); F; F = F->next()) { - const CellKey &ck = F->key(); - - if (ck.x < begin.x || ck.x > end.x) { - continue; - } - if (ck.y < begin.y || ck.y > end.y) { - continue; - } - - //notifiers in cell - for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) { - Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key()); - if (!H) { - H = E->get().notifiers.insert(G->key(), pass); - added.push_back(G->key()); - } else { - H->get() = pass; - } - } - } - - } else { - //check cells in grid fashion - for (int i = begin.x; i <= end.x; i++) { - for (int j = begin.y; j <= end.y; j++) { - CellKey ck; - ck.x = i; - ck.y = j; - - Map<CellKey, CellData>::Element *F = cells.find(ck); - if (!F) { - continue; - } - - //notifiers in cell - for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) { - Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key()); - if (!H) { - H = E->get().notifiers.insert(G->key(), pass); - added.push_back(G->key()); - } else { - H->get() = pass; - } - } - } - } - } - - for (Map<VisibilityNotifier2D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) { - if (F->get() != pass) { - removed.push_back(F->key()); - } - } - - while (!added.is_empty()) { - added.front()->get()->_enter_viewport(E->key()); - added.pop_front(); - } - - while (!removed.is_empty()) { - E->get().notifiers.erase(removed.front()->get()); - removed.front()->get()->_exit_viewport(E->key()); - removed.pop_front(); - } - } - - changed = false; - } - - SpatialIndexer2D() { - cell_size = GLOBAL_DEF("world/2d/cell_size", 100); - } -}; - -void World2D::_register_viewport(Viewport *p_viewport, const Rect2 &p_rect) { - indexer->_add_viewport(p_viewport, p_rect); -} - -void World2D::_update_viewport(Viewport *p_viewport, const Rect2 &p_rect) { - indexer->_update_viewport(p_viewport, p_rect); -} - -void World2D::_remove_viewport(Viewport *p_viewport) { - indexer->_remove_viewport(p_viewport); -} - -void World2D::_register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) { - indexer->_notifier_add(p_notifier, p_rect); -} - -void World2D::_update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) { - indexer->_notifier_update(p_notifier, p_rect); -} - -void World2D::_remove_notifier(VisibilityNotifier2D *p_notifier) { - indexer->_notifier_remove(p_notifier); -} - -void World2D::_update() { - indexer->_update(); -} - RID World2D::get_canvas() const { return canvas; } @@ -328,12 +50,6 @@ RID World2D::get_navigation_map() const { return navigation_map; } -void World2D::get_viewport_list(List<Viewport *> *r_viewports) { - for (Map<Viewport *, SpatialIndexer2D::ViewportData>::Element *E = indexer->viewports.front(); E; E = E->next()) { - r_viewports->push_back(E->key()); - } -} - void World2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas); ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space); @@ -341,10 +57,10 @@ void World2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World2D::get_direct_space_state); - ADD_PROPERTY(PropertyInfo(Variant::RID, "canvas", PROPERTY_HINT_NONE, "", 0), "", "get_canvas"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", 0), "", "get_navigation_map"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", 0), "", "get_direct_space_state"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "canvas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_canvas"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", PROPERTY_USAGE_NONE), "", "get_direct_space_state"); } PhysicsDirectSpaceState2D *World2D::get_direct_space_state() { @@ -369,13 +85,10 @@ World2D::World2D() { NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10)); NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5)); - - indexer = memnew(SpatialIndexer2D); } World2D::~World2D() { RenderingServer::get_singleton()->free(canvas); PhysicsServer2D::get_singleton()->free(space); NavigationServer2D::get_singleton()->free(navigation_map); - memdelete(indexer); } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 38abf3d7ad..65f89c8f64 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -35,7 +35,7 @@ #include "core/io/resource.h" #include "servers/physics_server_2d.h" -class VisibilityNotifier2D; +class VisibleOnScreenNotifier2D; class Viewport; struct SpatialIndexer2D; @@ -46,23 +46,15 @@ class World2D : public Resource { RID space; RID navigation_map; - SpatialIndexer2D *indexer; + Set<Viewport *> viewports; protected: static void _bind_methods(); friend class Viewport; - friend class VisibilityNotifier2D; - void _register_viewport(Viewport *p_viewport, const Rect2 &p_rect); - void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect); + void _register_viewport(Viewport *p_viewport); void _remove_viewport(Viewport *p_viewport); - void _register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect); - void _update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect); - void _remove_notifier(VisibilityNotifier2D *p_notifier); - - void _update(); - public: RID get_canvas() const; RID get_space() const; @@ -70,7 +62,7 @@ public: PhysicsDirectSpaceState2D *get_direct_space_state(); - void get_viewport_list(List<Viewport *> *r_viewports); + _FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; } World2D(); ~World2D(); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index e811cbf57a..42047f104f 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -33,210 +33,19 @@ #include "core/math/camera_matrix.h" #include "core/math/octree.h" #include "scene/3d/camera_3d.h" -#include "scene/3d/visibility_notifier_3d.h" +#include "scene/3d/visible_on_screen_notifier_3d.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" -struct SpatialIndexer { - Octree<VisibilityNotifier3D> octree; - - struct NotifierData { - AABB aabb; - OctreeElementID id; - }; - - Map<VisibilityNotifier3D *, NotifierData> notifiers; - struct CameraData { - Map<VisibilityNotifier3D *, uint64_t> notifiers; - }; - - Map<Camera3D *, CameraData> cameras; - - enum { - VISIBILITY_CULL_MAX = 32768 - }; - - Vector<VisibilityNotifier3D *> cull; - - bool changed; - uint64_t pass; - uint64_t last_frame; - - void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - ERR_FAIL_COND(notifiers.has(p_notifier)); - notifiers[p_notifier].aabb = p_rect; - notifiers[p_notifier].id = octree.create(p_notifier, p_rect); - changed = true; - } - - void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - if (E->get().aabb == p_rect) { - return; - } - - E->get().aabb = p_rect; - octree.move(E->get().id, E->get().aabb); - changed = true; - } - - void _notifier_remove(VisibilityNotifier3D *p_notifier) { - Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - - octree.erase(E->get().id); - notifiers.erase(p_notifier); - - List<Camera3D *> removed; - for (Map<Camera3D *, CameraData>::Element *F = cameras.front(); F; F = F->next()) { - Map<VisibilityNotifier3D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier); - - if (G) { - F->get().notifiers.erase(G); - removed.push_back(F->key()); - } - } - - while (!removed.is_empty()) { - p_notifier->_exit_camera(removed.front()->get()); - removed.pop_front(); - } - - changed = true; - } - - void _add_camera(Camera3D *p_camera) { - ERR_FAIL_COND(cameras.has(p_camera)); - CameraData vd; - cameras[p_camera] = vd; - changed = true; - } - - void _update_camera(Camera3D *p_camera) { - Map<Camera3D *, CameraData>::Element *E = cameras.find(p_camera); - ERR_FAIL_COND(!E); - changed = true; - } - - void _remove_camera(Camera3D *p_camera) { - ERR_FAIL_COND(!cameras.has(p_camera)); - List<VisibilityNotifier3D *> removed; - for (Map<VisibilityNotifier3D *, uint64_t>::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) { - removed.push_back(E->key()); - } - - while (!removed.is_empty()) { - removed.front()->get()->_exit_camera(p_camera); - removed.pop_front(); - } - - cameras.erase(p_camera); - } - - void _update(uint64_t p_frame) { - if (p_frame == last_frame) { - return; - } - last_frame = p_frame; - - if (!changed) { - return; - } - - for (Map<Camera3D *, CameraData>::Element *E = cameras.front(); E; E = E->next()) { - pass++; - - Camera3D *c = E->key(); - - Vector<Plane> planes = c->get_frustum(); - - int culled = octree.cull_convex(planes, cull.ptrw(), cull.size()); - - VisibilityNotifier3D **ptr = cull.ptrw(); - - List<VisibilityNotifier3D *> added; - List<VisibilityNotifier3D *> removed; - - for (int i = 0; i < culled; i++) { - //notifiers in frustum - - Map<VisibilityNotifier3D *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]); - if (!H) { - E->get().notifiers.insert(ptr[i], pass); - added.push_back(ptr[i]); - } else { - H->get() = pass; - } - } - - for (Map<VisibilityNotifier3D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) { - if (F->get() != pass) { - removed.push_back(F->key()); - } - } - - while (!added.is_empty()) { - added.front()->get()->_enter_camera(E->key()); - added.pop_front(); - } - - while (!removed.is_empty()) { - E->get().notifiers.erase(removed.front()->get()); - removed.front()->get()->_exit_camera(E->key()); - removed.pop_front(); - } - } - changed = false; - } - - SpatialIndexer() { - pass = 0; - last_frame = 0; - changed = false; - cull.resize(VISIBILITY_CULL_MAX); - } -}; - void World3D::_register_camera(Camera3D *p_camera) { #ifndef _3D_DISABLED - indexer->_add_camera(p_camera); -#endif -} - -void World3D::_update_camera(Camera3D *p_camera) { -#ifndef _3D_DISABLED - indexer->_update_camera(p_camera); + cameras.insert(p_camera); #endif } void World3D::_remove_camera(Camera3D *p_camera) { #ifndef _3D_DISABLED - indexer->_remove_camera(p_camera); -#endif -} - -void World3D::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { -#ifndef _3D_DISABLED - indexer->_notifier_add(p_notifier, p_rect); -#endif -} - -void World3D::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { -#ifndef _3D_DISABLED - indexer->_notifier_update(p_notifier, p_rect); -#endif -} - -void World3D::_remove_notifier(VisibilityNotifier3D *p_notifier) { -#ifndef _3D_DISABLED - indexer->_notifier_remove(p_notifier); -#endif -} - -void World3D::_update(uint64_t p_frame) { -#ifndef _3D_DISABLED - indexer->_update(p_frame); + cameras.erase(p_camera); #endif } @@ -307,12 +116,6 @@ PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { return PhysicsServer3D::get_singleton()->space_get_direct_state(space); } -void World3D::get_camera_list(List<Camera3D *> *r_cameras) { - for (Map<Camera3D *, SpatialIndexer::CameraData>::Element *E = indexer->cameras.front(); E; E = E->next()) { - r_cameras->push_back(E->key()); - } -} - void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space); ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map); @@ -327,10 +130,10 @@ void World3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", 0), "", "get_space"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", 0), "", "get_navigation_map"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", 0), "", "get_scenario"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", 0), "", "get_direct_space_state"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_scenario"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState3D", PROPERTY_USAGE_NONE), "", "get_direct_space_state"); } World3D::World3D() { @@ -349,20 +152,10 @@ World3D::World3D() { NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3)); NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3)); - -#ifdef _3D_DISABLED - indexer = nullptr; -#else - indexer = memnew(SpatialIndexer); -#endif } World3D::~World3D() { PhysicsServer3D::get_singleton()->free(space); RenderingServer::get_singleton()->free(scenario); NavigationServer3D::get_singleton()->free(navigation_map); - -#ifndef _3D_DISABLED - memdelete(indexer); -#endif } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 4e2717a2bb..2c5be35609 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -38,7 +38,7 @@ #include "servers/rendering_server.h" class Camera3D; -class VisibilityNotifier3D; +class VisibleOnScreenNotifier3D; struct SpatialIndexer; class World3D : public Resource { @@ -48,27 +48,21 @@ private: RID space; RID navigation_map; RID scenario; - SpatialIndexer *indexer; + Ref<Environment> environment; Ref<Environment> fallback_environment; Ref<CameraEffects> camera_effects; + Set<Camera3D *> cameras; + protected: static void _bind_methods(); friend class Camera3D; - friend class VisibilityNotifier3D; void _register_camera(Camera3D *p_camera); - void _update_camera(Camera3D *p_camera); void _remove_camera(Camera3D *p_camera); - void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _remove_notifier(VisibilityNotifier3D *p_notifier); - friend class Viewport; - void _update(uint64_t p_frame); - public: RID get_space() const; RID get_navigation_map() const; @@ -83,7 +77,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - void get_camera_list(List<Camera3D *> *r_cameras); + _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; } PhysicsDirectSpaceState3D *get_direct_space_state(); |