diff options
Diffstat (limited to 'scene/resources')
142 files changed, 11684 insertions, 4236 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index bc533ff022..19545167c8 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2302,7 +2302,7 @@ Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a } Quaternion Animation::_cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const { - return p_a.cubic_slerp(p_b, p_pre_a, p_post_b, p_c); + return p_a.spherical_cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); } Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const { @@ -2363,7 +2363,7 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a Quaternion pa = p_pre_a; Quaternion pb = p_post_b; - return a.cubic_slerp(b, pa, pb, p_c); + return a.spherical_cubic_interpolate(b, pa, pb, p_c); } case Variant::AABB: { AABB a = p_a; @@ -2413,7 +2413,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol real_t c = 0.0; // prepare for all cases of interpolation - if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) { + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { // loop if (!p_backward) { // no backward @@ -2567,11 +2567,19 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol case INTERPOLATION_CUBIC: { int pre = idx - 1; if (pre < 0) { - pre = 0; + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + pre = len - 1; + } else { + pre = 0; + } } int post = next + 1; if (post >= len) { - post = next; + if (loop_mode == LOOP_LINEAR && p_loop_wrap) { + post = 0; + } else { + post = next; + } } return _cubic_interpolate(p_keys[pre].value, p_keys[idx].value, p_keys[next].value, p_keys[post].value, c); @@ -3371,17 +3379,6 @@ Vector2 Animation::bezier_track_get_key_out_handle(int p_track, int p_index) con return bt->values[p_index].value.out_handle; } -static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) { - /* Formula from Wikipedia article on Bezier curves. */ - real_t omt = (1.0 - t); - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; - - return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; -} - real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { //this uses a different interpolation scheme ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); @@ -3420,7 +3417,6 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { real_t duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes real_t low = 0.0; // 0% of the current animation segment real_t high = 1.0; // 100% of the current animation segment - real_t middle; Vector2 start(0, bt->values[idx].value.value); Vector2 start_out = start + bt->values[idx].value.out_handle; @@ -3429,9 +3425,9 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { //narrow high and low as much as possible for (int i = 0; i < iterations; i++) { - middle = (low + high) / 2; + real_t middle = (low + high) / 2; - Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end); + Vector2 interp = start.bezier_interpolate(start_out, end_in, end, middle); if (interp.x < t) { low = middle; @@ -3441,14 +3437,14 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { } //interpolate the result: - Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end); - Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end); + Vector2 low_pos = start.bezier_interpolate(start_out, end_in, end, low); + Vector2 high_pos = start.bezier_interpolate(start_out, end_in, end, high); real_t c = (t - low_pos.x) / (high_pos.x - low_pos.x); return low_pos.lerp(high_pos, c).y; } -int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset, real_t p_end_offset) { +int Animation::audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset, real_t p_end_offset) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1); @@ -3474,7 +3470,7 @@ int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_s return key; } -void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream) { +void Animation::audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_AUDIO); @@ -3524,14 +3520,14 @@ void Animation::audio_track_set_key_end_offset(int p_track, int p_key, real_t p_ emit_changed(); } -RES Animation::audio_track_get_key_stream(int p_track, int p_key) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), RES()); +Ref<Resource> Animation::audio_track_get_key_stream(int p_track, int p_key) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), Ref<Resource>()); const Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_AUDIO, RES()); + ERR_FAIL_COND_V(t->type != TYPE_AUDIO, Ref<Resource>()); const AudioTrack *at = static_cast<const AudioTrack *>(t); - ERR_FAIL_INDEX_V(p_key, at->values.size(), RES()); + ERR_FAIL_INDEX_V(p_key, at->values.size(), Ref<Resource>()); return at->values[p_key].value.stream; } @@ -3820,9 +3816,9 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0)); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001,suffix:s"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001,suffix:s"), "set_step", "get_step"); ADD_SIGNAL(MethodInfo("tracks_changed")); @@ -4339,7 +4335,7 @@ struct AnimationCompressionDataState { if (temp_packets.size() == 0) { return; //nohing to do } -#define DEBUG_PACKET_PUSH +//#define DEBUG_PACKET_PUSH #ifdef DEBUG_PACKET_PUSH #ifndef _MSC_VER #warning Debugging packet push, disable this code in production to gain a bit more import performance. @@ -4370,7 +4366,7 @@ struct AnimationCompressionDataState { header_bytes += 2; } - while (header_bytes % 4 != 0) { + while (header_bytes < 8 && header_bytes % 4 != 0) { // First cond needed to silence wrong GCC warning. header[header_bytes++] = 0; } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index f9a33da428..abbcaa92bc 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -180,7 +180,7 @@ private: /* AUDIO TRACK */ struct AudioKey { - RES stream; + Ref<Resource> stream; real_t start_offset = 0.0; //offset from start real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite AudioKey() { @@ -436,11 +436,11 @@ public: real_t bezier_track_interpolate(int p_track, double p_time) const; - int audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0); - void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream); + int audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0); + void audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream); void audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset); void audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset); - RES audio_track_get_key_stream(int p_track, int p_key) const; + Ref<Resource> audio_track_get_key_stream(int p_track, int p_key) const; real_t audio_track_get_key_start_offset(int p_track, int p_key) const; real_t audio_track_get_key_end_offset(int p_track, int p_key) const; @@ -488,4 +488,4 @@ VARIANT_ENUM_CAST(Animation::UpdateMode); VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::LoopMode); -#endif +#endif // ANIMATION_H diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp new file mode 100644 index 0000000000..5f725b2fbe --- /dev/null +++ b/scene/resources/animation_library.cpp @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* animation_library.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "animation_library.h" + +bool AnimationLibrary::is_valid_animation_name(const String &p_name) { + return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); +} + +bool AnimationLibrary::is_valid_library_name(const String &p_name) { + return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); +} + +String AnimationLibrary::validate_library_name(const String &p_name) { + String name = p_name; + const char *characters = "/:,["; + for (const char *p = characters; *p; p++) { + name = name.replace(String::chr(*p), "_"); + } + return name; +} + +Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) { + ERR_FAIL_COND_V_MSG(!is_valid_animation_name(p_name), ERR_INVALID_PARAMETER, "Invalid animation name: '" + String(p_name) + "'."); + ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); + + if (animations.has(p_name)) { + animations.erase(p_name); + emit_signal(SNAME("animation_removed"), p_name); + } + + animations.insert(p_name, p_animation); + emit_signal(SNAME("animation_added"), p_name); + notify_property_list_changed(); + return OK; +} + +void AnimationLibrary::remove_animation(const StringName &p_name) { + ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name)); + + animations.erase(p_name); + emit_signal(SNAME("animation_removed"), p_name); + notify_property_list_changed(); +} + +void AnimationLibrary::rename_animation(const StringName &p_name, const StringName &p_new_name) { + ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name)); + ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); + ERR_FAIL_COND_MSG(animations.has(p_new_name), vformat("Animation name \"%s\" already exists in library.", p_new_name)); + + animations.insert(p_new_name, animations[p_name]); + animations.erase(p_name); + emit_signal(SNAME("animation_renamed"), p_name, p_new_name); +} + +bool AnimationLibrary::has_animation(const StringName &p_name) const { + return animations.has(p_name); +} + +Ref<Animation> AnimationLibrary::get_animation(const StringName &p_name) const { + ERR_FAIL_COND_V(!animations.has(p_name), Ref<Animation>()); + + return animations[p_name]; +} + +TypedArray<StringName> AnimationLibrary::_get_animation_list() const { + TypedArray<StringName> ret; + List<StringName> names; + get_animation_list(&names); + for (const StringName &K : names) { + ret.push_back(K); + } + return ret; +} + +void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const { + List<StringName> anims; + + for (const KeyValue<StringName, Ref<Animation>> &E : animations) { + anims.push_back(E.key); + } + + anims.sort_custom<StringName::AlphCompare>(); + + for (const StringName &E : anims) { + p_animations->push_back(E); + } +} + +void AnimationLibrary::_set_data(const Dictionary &p_data) { + animations.clear(); + List<Variant> keys; + p_data.get_key_list(&keys); + for (const Variant &K : keys) { + add_animation(K, p_data[K]); + } +} + +Dictionary AnimationLibrary::_get_data() const { + Dictionary ret; + for (const KeyValue<StringName, Ref<Animation>> &K : animations) { + ret[K.key] = K.value; + } + return ret; +} + +void AnimationLibrary::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationLibrary::add_animation); + ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationLibrary::remove_animation); + ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationLibrary::rename_animation); + ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation); + ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation); + ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data"); + ADD_SIGNAL(MethodInfo("animation_added", PropertyInfo(Variant::STRING_NAME, "name"))); + ADD_SIGNAL(MethodInfo("animation_removed", PropertyInfo(Variant::STRING_NAME, "name"))); + ADD_SIGNAL(MethodInfo("animation_renamed", PropertyInfo(Variant::STRING_NAME, "name"), PropertyInfo(Variant::STRING_NAME, "to_name"))); +} +AnimationLibrary::AnimationLibrary() { +} diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h new file mode 100644 index 0000000000..d63807b6d7 --- /dev/null +++ b/scene/resources/animation_library.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* animation_library.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 ANIMATION_LIBRARY_H +#define ANIMATION_LIBRARY_H + +#include "core/variant/typed_array.h" +#include "scene/resources/animation.h" + +class AnimationLibrary : public Resource { + GDCLASS(AnimationLibrary, Resource) + + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + + TypedArray<StringName> _get_animation_list() const; + + friend class AnimationPlayer; //for faster access + HashMap<StringName, Ref<Animation>> animations; + +protected: + static void _bind_methods(); + +public: + static bool is_valid_animation_name(const String &p_name); + static bool is_valid_library_name(const String &p_name); + static String validate_library_name(const String &p_name); + + Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation); + void remove_animation(const StringName &p_name); + void rename_animation(const StringName &p_name, const StringName &p_new_name); + bool has_animation(const StringName &p_name) const; + Ref<Animation> get_animation(const StringName &p_name) const; + void get_animation_list(List<StringName> *p_animations) const; + + AnimationLibrary(); +}; + +#endif // ANIMATION_LIBRARY_H diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 56786ac4b1..dcd36284d4 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -406,6 +406,10 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int return p_frames; } +void AudioStreamPlaybackSample::tag_used_streams() { + base->tag_used(get_playback_position()); +} + AudioStreamPlaybackSample::AudioStreamPlaybackSample() {} ///////////////////// @@ -556,9 +560,9 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { file_path += ".wav"; } - FileAccessRef file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present - ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_WRITE); // Create WAV Header file->store_string("RIFF"); //ChunkID @@ -596,12 +600,10 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { break; } - file->close(); - return OK; } -Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { +Ref<AudioStreamPlayback> AudioStreamSample::instantiate_playback() { Ref<AudioStreamPlaybackSample> sample; sample.instantiate(); sample->base = Ref<AudioStreamSample>(this); diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index 043a62ff70..2e694cffe2 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -75,6 +75,8 @@ public: virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; + virtual void tag_used_streams() override; + AudioStreamPlaybackSample(); }; @@ -89,6 +91,7 @@ public: FORMAT_IMA_ADPCM }; + // Keep the ResourceImporterWAV `edit/loop_mode` enum hint in sync with these options. enum LoopMode { LOOP_DISABLED, LOOP_FORWARD, @@ -143,7 +146,7 @@ public: Error save_to_wav(const String &p_path); - virtual Ref<AudioStreamPlayback> instance_playback() override; + virtual Ref<AudioStreamPlayback> instantiate_playback() override; virtual String get_stream_name() const override; AudioStreamSample(); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index c2988c2e8c..1ff72825ac 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -170,8 +170,8 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) int curx = startx; int cury = starty; unsigned int count = 0; - Set<Point2i> case9s; - Set<Point2i> case6s; + HashSet<Point2i> case9s; + HashSet<Point2i> case6s; Vector<Vector2> _points; do { int sv = 0; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp new file mode 100644 index 0000000000..aff917b2d4 --- /dev/null +++ b/scene/resources/bone_map.cpp @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* bone_map.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "bone_map.h" + +bool BoneMap::_set(const StringName &p_path, const Variant &p_value) { + String path = p_path; + if (path.begins_with("bone_map/")) { + String which = path.get_slicec('/', 1); + set_skeleton_bone_name(which, p_value); + return true; + } + return true; +} + +bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + if (path.begins_with("bone_map/")) { + String which = path.get_slicec('/', 1); + r_ret = get_skeleton_bone_name(which); + return true; + } + return true; +} + +void BoneMap::_get_property_list(List<PropertyInfo> *p_list) const { + HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); + while (E) { + p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_map/" + E->key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + ++E; + } +} + +Ref<SkeletonProfile> BoneMap::get_profile() const { + return profile; +} + +void BoneMap::set_profile(const Ref<SkeletonProfile> &p_profile) { + bool is_changed = profile != p_profile; + if (is_changed) { + if (!profile.is_null() && profile->is_connected("profile_updated", callable_mp(this, &BoneMap::_update_profile))) { + profile->disconnect("profile_updated", callable_mp(this, &BoneMap::_update_profile)); + } + profile = p_profile; + if (!profile.is_null()) { + profile->connect("profile_updated", callable_mp(this, &BoneMap::_update_profile)); + } + _update_profile(); + } + notify_property_list_changed(); +} + +StringName BoneMap::get_skeleton_bone_name(StringName p_profile_bone_name) const { + ERR_FAIL_COND_V(!bone_map.has(p_profile_bone_name), StringName()); + return bone_map.get(p_profile_bone_name); +} + +void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { + ERR_FAIL_COND(!bone_map.has(p_profile_bone_name)); + bone_map.insert(p_profile_bone_name, p_skeleton_bone_name); + emit_signal("bone_map_updated"); +} + +StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { + StringName profile_bone_name = StringName(); + HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); + while (E) { + if (E->value == p_skeleton_bone_name) { + profile_bone_name = E->key; + break; + } + ++E; + } + return profile_bone_name; +} + +int BoneMap::get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const { + int count = 0; + HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); + while (E) { + if (E->value == p_skeleton_bone_name) { + ++count; + } + ++E; + } + return count; +} + +void BoneMap::_update_profile() { + _validate_bone_map(); + emit_signal("profile_updated"); +} + +void BoneMap::_validate_bone_map() { + Ref<SkeletonProfile> current_profile = get_profile(); + if (current_profile.is_valid()) { + // Insert missing profile bones into bone map. + int len = current_profile->get_bone_size(); + StringName profile_bone_name; + for (int i = 0; i < len; i++) { + profile_bone_name = current_profile->get_bone_name(i); + if (!bone_map.has(profile_bone_name)) { + bone_map.insert(profile_bone_name, StringName()); + } + } + // Remove bones that do not exist in the profile from the map. + Vector<StringName> delete_bones; + StringName k; + HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); + while (E) { + k = E->key; + if (!current_profile->has_bone(k)) { + delete_bones.push_back(k); + } + ++E; + } + len = delete_bones.size(); + for (int i = 0; i < len; i++) { + bone_map.erase(delete_bones[i]); + } + } else { + bone_map.clear(); + } + emit_signal("retarget_option_updated"); +} + +void BoneMap::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_profile"), &BoneMap::get_profile); + ClassDB::bind_method(D_METHOD("set_profile", "profile"), &BoneMap::set_profile); + + ClassDB::bind_method(D_METHOD("get_skeleton_bone_name", "profile_bone_name"), &BoneMap::get_skeleton_bone_name); + ClassDB::bind_method(D_METHOD("set_skeleton_bone_name", "profile_bone_name", "skeleton_bone_name"), &BoneMap::set_skeleton_bone_name); + + ClassDB::bind_method(D_METHOD("find_profile_bone_name", "skeleton_bone_name"), &BoneMap::find_profile_bone_name); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile"); + ADD_ARRAY("bonemap", "bonemap"); + + ADD_SIGNAL(MethodInfo("bone_map_updated")); + ADD_SIGNAL(MethodInfo("profile_updated")); +} + +void BoneMap::_validate_property(PropertyInfo &property) const { + // +} + +BoneMap::BoneMap() { + _validate_bone_map(); +} + +BoneMap::~BoneMap() { +} + +////////////////////////////////////// diff --git a/scene/resources/bone_map.h b/scene/resources/bone_map.h new file mode 100644 index 0000000000..17452dfc73 --- /dev/null +++ b/scene/resources/bone_map.h @@ -0,0 +1,70 @@ +/*************************************************************************/ +/* bone_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 BONE_MAP_H +#define BONE_MAP_H + +#include "skeleton_profile.h" + +class BoneMap : public Resource { + GDCLASS(BoneMap, Resource); + + Ref<SkeletonProfile> profile; + HashMap<StringName, StringName> bone_map; + + void _update_profile(); + void _validate_bone_map(); + +protected: + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + virtual void _validate_property(PropertyInfo &property) const override; + void _get_property_list(List<PropertyInfo> *p_list) const; + static void _bind_methods(); + +public: + int get_profile_type() const; + void set_profile_type(const int p_profile_type); + + Ref<SkeletonProfile> get_profile() const; + void set_profile(const Ref<SkeletonProfile> &p_profile); + + int get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const; + + StringName get_skeleton_bone_name(StringName p_profile_bone_name) const; + void set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); + + StringName find_profile_bone_name(StringName p_skeleton_bone_name) const; + + BoneMap(); + ~BoneMap(); +}; + +#endif // BONE_MAP_H diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp index a1ec9a2230..aac334b4be 100644 --- a/scene/resources/box_shape_3d.cpp +++ b/scene/resources/box_shape_3d.cpp @@ -77,6 +77,7 @@ bool BoxShape3D::_get(const StringName &p_name, Variant &r_property) const { #endif // DISABLE_DEPRECATED void BoxShape3D::set_size(const Vector3 &p_size) { + ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0 || p_size.z < 0, "BoxShape3D size cannot be negative."); size = p_size; _update_shape(); notify_change_to_owners(); @@ -90,7 +91,7 @@ void BoxShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxShape3D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &BoxShape3D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); } BoxShape3D::BoxShape3D() : diff --git a/scene/resources/box_shape_3d.h b/scene/resources/box_shape_3d.h index 4e6db893af..3dd0bb6cb7 100644 --- a/scene/resources/box_shape_3d.h +++ b/scene/resources/box_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BOX_SHAPE_H -#define BOX_SHAPE_H +#ifndef BOX_SHAPE_3D_H +#define BOX_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -56,4 +56,4 @@ public: BoxShape3D(); }; -#endif // BOX_SHAPE_H +#endif // BOX_SHAPE_3D_H diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp index ebe2aa4dba..97617adbae 100644 --- a/scene/resources/camera_effects.cpp +++ b/scene/resources/camera_effects.cpp @@ -175,10 +175,10 @@ 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_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_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "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_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_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "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"); diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index 2d668cdf7f..aa6cc4aded 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -34,7 +34,7 @@ Mutex CanvasItemMaterial::material_mutex; SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr; -Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map; +HashMap<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData, CanvasItemMaterial::MaterialKey> CanvasItemMaterial::shader_map; CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr; void CanvasItemMaterial::init_shaders() { diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index e40e4392cb..160c67d6b1 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -63,8 +63,11 @@ private: uint32_t key = 0; - bool operator<(const MaterialKey &p_key) const { - return key < p_key.key; + static uint32_t hash(const MaterialKey &p_key) { + return hash_murmur3_one_32(p_key.key); + } + bool operator==(const MaterialKey &p_key) const { + return key == p_key.key; } }; @@ -81,7 +84,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; @@ -107,10 +110,10 @@ private: LightMode light_mode = LIGHT_MODE_NORMAL; bool particles_animation = false; - // Initialized in the constructor. - int particles_anim_h_frames; - int particles_anim_v_frames; - bool particles_anim_loop; + // Proper values set in constructor. + int particles_anim_h_frames = 0; + int particles_anim_v_frames = 0; + bool particles_anim_loop = false; protected: static void _bind_methods(); diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 4d2698d27d..eb27ffaf35 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -59,6 +59,7 @@ void CapsuleShape2D::_update_shape() { } void CapsuleShape2D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape2D radius cannot be negative."); radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -71,6 +72,7 @@ real_t CapsuleShape2D::get_radius() const { } void CapsuleShape2D::set_height(real_t p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape2D height cannot be negative."); height = p_height; if (radius > height * 0.5) { radius = height * 0.5; @@ -107,8 +109,8 @@ void CapsuleShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape2D::set_height); ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape2D::get_height); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:px"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:px"), "set_height", "get_height"); ADD_LINKED_PROPERTY("radius", "height"); ADD_LINKED_PROPERTY("height", "radius"); } diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp index c16ddad984..214004824f 100644 --- a/scene/resources/capsule_shape_3d.cpp +++ b/scene/resources/capsule_shape_3d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "capsule_shape_3d.h" + #include "servers/physics_server_3d.h" Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const { @@ -79,6 +80,7 @@ void CapsuleShape3D::_update_shape() { } void CapsuleShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape3D radius cannot be negative."); radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -92,6 +94,7 @@ float CapsuleShape3D::get_radius() const { } void CapsuleShape3D::set_height(float p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape3D height cannot be negative."); height = p_height; if (radius > height * 0.5) { radius = height * 0.5; @@ -110,8 +113,8 @@ void CapsuleShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_height", "height"), &CapsuleShape3D::set_height); ClassDB::bind_method(D_METHOD("get_height"), &CapsuleShape3D::get_height); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height"); ADD_LINKED_PROPERTY("radius", "height"); ADD_LINKED_PROPERTY("height", "radius"); } diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h index 4c039ab326..ad7f2e5b74 100644 --- a/scene/resources/capsule_shape_3d.h +++ b/scene/resources/capsule_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CAPSULE_SHAPE_H -#define CAPSULE_SHAPE_H +#ifndef CAPSULE_SHAPE_3D_H +#define CAPSULE_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -55,4 +55,4 @@ public: CapsuleShape3D(); }; -#endif // CAPSULE_SHAPE_H +#endif // CAPSULE_SHAPE_3D_H diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index 9c16ac2eed..ff60162180 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -43,6 +43,7 @@ void CircleShape2D::_update_shape() { } void CircleShape2D::set_radius(real_t p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CircleShape2D radius cannot be negative."); radius = p_radius; _update_shape(); } @@ -55,7 +56,7 @@ void CircleShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CircleShape2D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &CircleShape2D::get_radius); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,16384,0.5"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:px"), "set_radius", "get_radius"); } Rect2 CircleShape2D::get_rect() const { diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index 0f49a0d80f..d350d753b0 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -52,4 +52,4 @@ public: ConcavePolygonShape2D(); }; -#endif +#endif // CONCAVE_POLYGON_SHAPE_2D_H diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index 3e178108c4..b91f0e4f1c 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -33,7 +33,7 @@ #include "servers/physics_server_3d.h" Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { - Set<DrawEdge> edges; + HashSet<DrawEdge, DrawEdge> edges; int index_count = faces.size(); ERR_FAIL_COND_V((index_count % 3) != 0, Vector<Vector3>()); @@ -50,9 +50,9 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { Vector<Vector3> points; points.resize(edges.size() * 2); int idx = 0; - for (Set<DrawEdge>::Element *E = edges.front(); E; E = E->next()) { - points.write[idx + 0] = E->get().a; - points.write[idx + 1] = E->get().b; + for (const DrawEdge &E : edges) { + points.write[idx + 0] = E.a; + points.write[idx + 1] = E.b; idx += 2; } diff --git a/scene/resources/concave_polygon_shape_3d.h b/scene/resources/concave_polygon_shape_3d.h index 5337deb5fb..68feacfa25 100644 --- a/scene/resources/concave_polygon_shape_3d.h +++ b/scene/resources/concave_polygon_shape_3d.h @@ -42,12 +42,12 @@ class ConcavePolygonShape3D : public Shape3D { struct DrawEdge { Vector3 a; Vector3 b; - bool operator<(const DrawEdge &p_edge) const { - if (a == p_edge.a) { - return b < p_edge.b; - } else { - return a < p_edge.a; - } + static uint32_t hash(const DrawEdge &p_edge) { + uint32_t h = hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.a)); + return hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.b), h); + } + bool operator==(const DrawEdge &p_edge) const { + return (a == p_edge.a && b == p_edge.b); } DrawEdge(const Vector3 &p_a = Vector3(), const Vector3 &p_b = Vector3()) { @@ -77,4 +77,4 @@ public: ConcavePolygonShape3D(); }; -#endif // CONCAVE_POLYGON_SHAPE_H +#endif // CONCAVE_POLYGON_SHAPE_3D_H diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 667399ee75..c416a03f38 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -82,7 +82,7 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) { if (is_collision_outline_enabled()) { RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0, true); } } diff --git a/scene/resources/convex_polygon_shape_3d.h b/scene/resources/convex_polygon_shape_3d.h index 930edb015d..5ca4d2a713 100644 --- a/scene/resources/convex_polygon_shape_3d.h +++ b/scene/resources/convex_polygon_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CONVEX_POLYGON_SHAPE_H -#define CONVEX_POLYGON_SHAPE_H +#ifndef CONVEX_POLYGON_SHAPE_3D_H +#define CONVEX_POLYGON_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -52,4 +52,4 @@ public: ConvexPolygonShape3D(); }; -#endif // CONVEX_POLYGON_SHAPE_H +#endif // CONVEX_POLYGON_SHAPE_3D_H diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 6485c1ac77..da26a0261f 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -32,24 +32,25 @@ #include "core/core_string_names.h" -template <class T> -static _FORCE_INLINE_ T _bezier_interp(real_t p_t, T p_start, T p_control_1, T p_control_2, T p_end) { - /* Formula from Wikipedia article on Bezier curves. */ - real_t omt = (1.0 - p_t); - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = p_t * p_t; - real_t t3 = t2 * p_t; - - return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; -} - const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed"; Curve::Curve() { } -int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) { +void Curve::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (_points.size() >= p_count) { + _points.resize(p_count); + mark_dirty(); + } else { + for (int i = p_count - _points.size(); i > 0; i--) { + _add_point(Vector2()); + } + } + notify_property_list_changed(); +} + +int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) { // Add a point and preserve order // Curve bounds is in 0..1 @@ -100,6 +101,13 @@ int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_t return ret; } +int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) { + int ret = _add_point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode); + notify_property_list_changed(); + + return ret; +} + int Curve::get_index(real_t p_offset) const { // Lower-bound float binary search @@ -205,15 +213,21 @@ Curve::TangentMode Curve::get_point_right_mode(int p_index) const { return _points[p_index].right_mode; } -void Curve::remove_point(int p_index) { +void Curve::_remove_point(int p_index) { ERR_FAIL_INDEX(p_index, _points.size()); _points.remove_at(p_index); mark_dirty(); } +void Curve::remove_point(int p_index) { + _remove_point(p_index); + notify_property_list_changed(); +} + void Curve::clear_points() { _points.clear(); mark_dirty(); + notify_property_list_changed(); } void Curve::set_point_value(int p_index, real_t p_position) { @@ -226,8 +240,8 @@ void Curve::set_point_value(int p_index, real_t p_position) { int Curve::set_point_offset(int p_index, real_t p_offset) { ERR_FAIL_INDEX_V(p_index, _points.size(), -1); Point p = _points[p_index]; - remove_point(p_index); - int i = add_point(Vector2(p_offset, p.position.y)); + _remove_point(p_index); + int i = _add_point(Vector2(p_offset, p.position.y)); _points.write[i].left_tangent = p.left_tangent; _points.write[i].right_tangent = p.right_tangent; _points.write[i].left_mode = p.left_mode; @@ -350,7 +364,7 @@ real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) cons real_t yac = a.position.y + d * a.right_tangent; real_t ybc = b.position.y - d * b.left_tangent; - real_t y = _bezier_interp(p_local_offset, a.position.y, yac, ybc, b.position.y); + real_t y = Math::bezier_interpolate(a.position.y, yac, ybc, b.position.y, p_local_offset); return y; } @@ -409,7 +423,6 @@ void Curve::set_data(const Array p_input) { p.position = p_input[i]; p.left_tangent = p_input[i + 1]; p.right_tangent = p_input[i + 2]; - // TODO For some reason the compiler won't convert from Variant to enum int left_mode = p_input[i + 3]; int right_mode = p_input[i + 4]; p.left_mode = (TangentMode)left_mode; @@ -417,6 +430,7 @@ void Curve::set_data(const Array p_input) { } mark_dirty(); + notify_property_list_changed(); } void Curve::bake() { @@ -490,8 +504,91 @@ void Curve::ensure_default_setup(real_t p_min, real_t p_max) { } } +bool Curve::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + Vector2 position = p_value.operator Vector2(); + set_point_offset(point_index, position.x); + set_point_value(point_index, position.y); + return true; + } else if (property == "left_tangent") { + set_point_left_tangent(point_index, p_value); + return true; + } else if (property == "left_mode") { + int mode = p_value; + set_point_left_mode(point_index, (TangentMode)mode); + return true; + } else if (property == "right_tangent") { + set_point_right_tangent(point_index, p_value); + return true; + } else if (property == "right_mode") { + int mode = p_value; + set_point_right_mode(point_index, (TangentMode)mode); + return true; + } + } + return false; +} + +bool Curve::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "left_tangent") { + r_ret = get_point_left_tangent(point_index); + return true; + } else if (property == "left_mode") { + r_ret = get_point_left_mode(point_index); + return true; + } else if (property == "right_tangent") { + r_ret = get_point_right_tangent(point_index); + return true; + } else if (property == "right_mode") { + r_ret = get_point_right_mode(point_index); + return true; + } + } + return false; +} + +void Curve::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < _points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/left_tangent", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("point_%d/left_mode", i), PROPERTY_HINT_ENUM, "Free,Linear"); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != _points.size() - 1) { + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/right_tangent", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("point_%d/right_mode", i), PROPERTY_HINT_ENUM, "Free,Linear"); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + } +} + void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE)); ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points); @@ -523,6 +620,7 @@ void Curve::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED)); @@ -535,7 +633,20 @@ int Curve2D::get_point_count() const { return points.size(); } -void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) { +void Curve2D::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (points.size() >= p_count) { + points.resize(p_count); + mark_dirty(); + } else { + for (int i = p_count - points.size(); i > 0; i--) { + _add_point(Vector2()); + } + } + notify_property_list_changed(); +} + +void Curve2D::_add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) { Point n; n.position = p_position; n.in = p_in; @@ -546,16 +657,19 @@ void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Ve points.push_back(n); } - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); +} + +void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) { + _add_point(p_position, p_in, p_out, p_atpos); + notify_property_list_changed(); } void Curve2D::set_point_position(int p_index, const Vector2 &p_position) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].position = p_position; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_position(int p_index) const { @@ -567,8 +681,7 @@ void Curve2D::set_point_in(int p_index, const Vector2 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].in = p_in; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_in(int p_index) const { @@ -580,8 +693,7 @@ void Curve2D::set_point_out(int p_index, const Vector2 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].out = p_out; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_out(int p_index) const { @@ -589,18 +701,22 @@ Vector2 Curve2D::get_point_out(int p_index) const { return points[p_index].out; } -void Curve2D::remove_point(int p_index) { +void Curve2D::_remove_point(int p_index) { ERR_FAIL_INDEX(p_index, points.size()); points.remove_at(p_index); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); +} + +void Curve2D::remove_point(int p_index) { + _remove_point(p_index); + notify_property_list_changed(); } void Curve2D::clear_points() { if (!points.is_empty()) { points.clear(); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); + notify_property_list_changed(); } } @@ -619,7 +735,7 @@ Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const { Vector2 p3 = points[p_index + 1].position; Vector2 p2 = p3 + points[p_index + 1].in; - return _bezier_interp(p_offset, p0, p1, p2, p3); + return p0.bezier_interpolate(p1, p2, p3, p_offset); } Vector2 Curve2D::interpolatef(real_t p_findex) const { @@ -632,11 +748,16 @@ Vector2 Curve2D::interpolatef(real_t p_findex) const { return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } -void Curve2D::_bake_segment2d(Map<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { +void Curve2D::mark_dirty() { + baked_cache_dirty = true; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { real_t mp = p_begin + (p_end - p_begin) * 0.5; - Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b); - Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b); - Vector2 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b); + Vector2 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); + Vector2 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); + Vector2 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); Vector2 na = (mid - beg).normalized(); Vector2 nb = (end - mid).normalized(); @@ -681,7 +802,8 @@ void Curve2D::_bake() const { List<Vector2> pointlist; List<real_t> distlist; - pointlist.push_back(position); //start always from origin + // Start always from origin. + pointlist.push_back(position); distlist.push_back(0.0); for (int i = 0; i < points.size() - 1; i++) { @@ -694,7 +816,7 @@ void Curve2D::_bake() const { np = 1.0; } - Vector2 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position); + Vector2 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np); real_t d = position.distance_to(npp); if (d > bake_interval) { @@ -707,7 +829,7 @@ void Curve2D::_bake() const { real_t mid = low + (hi - low) * 0.5; for (int j = 0; j < iterations; j++) { - npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position); + npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid); d = position.distance_to(npp); if (bake_interval < d) { @@ -728,15 +850,18 @@ void Curve2D::_bake() const { p = np; } } - } - Vector2 lastpos = points[points.size() - 1].position; + Vector2 npp = points[i + 1].position; + real_t d = position.distance_to(npp); + + position = npp; + dist += d; + + pointlist.push_back(position); + distlist.push_back(dist); + } - real_t rem = position.distance_to(lastpos); - dist += rem; baked_max_ofs = dist; - pointlist.push_back(lastpos); - distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); baked_dist_cache.resize(distlist.size()); @@ -763,7 +888,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); @@ -771,18 +896,19 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { return baked_point_cache.get(0); } - int bpc = baked_point_cache.size(); const Vector2 *r = baked_point_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int start = 0, end = bpc, idx = (end + start) / 2; - // binary search to find baked points + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. while (start < idx) { real_t offset = baked_dist_cache[idx]; if (p_offset <= offset) { @@ -803,7 +929,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { if (p_cubic) { Vector2 pre = idx > 0 ? r[idx - 1] : r[idx]; - Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; + Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1]; return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac); } else { return r[idx].lerp(r[idx + 1], frac); @@ -820,8 +946,7 @@ PackedVector2Array Curve2D::get_baked_points() const { void Curve2D::set_bake_interval(real_t p_tolerance) { bake_interval = p_tolerance; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve2D::get_bake_interval() const { @@ -829,13 +954,13 @@ real_t Curve2D::get_bake_interval() const { } Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); @@ -867,13 +992,13 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { } real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve2D."); @@ -940,7 +1065,8 @@ void Curve2D::_set_data(const Dictionary &p_data) { points.write[i].position = r[i * 3 + 2]; } - baked_cache_dirty = true; + mark_dirty(); + notify_property_list_changed(); } PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) const { @@ -949,7 +1075,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con if (points.size() == 0) { return tess; } - Vector<Map<real_t, Vector2>> midpoints; + + // The current implementation requires a sorted map. + Vector<RBMap<real_t, Vector2>> midpoints; midpoints.resize(points.size() - 1); @@ -978,8 +1106,67 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +bool Curve2D::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + set_point_position(point_index, p_value); + return true; + } else if (property == "in") { + set_point_in(point_index, p_value); + return true; + } else if (property == "out") { + set_point_out(point_index, p_value); + return true; + } + } + return false; +} + +bool Curve2D::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "in") { + r_ret = get_point_in(point_index); + return true; + } else if (property == "out") { + r_ret = get_point_out(point_index); + return true; + } + } + return false; +} + +void Curve2D::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/in", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != points.size() - 1) { + pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/out", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + } +} + void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve2D::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position); @@ -1007,13 +1194,10 @@ void Curve2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); } -Curve2D::Curve2D() { - /* add_point(Vector2(-1,0,0)); - add_point(Vector2(0,2,0)); - add_point(Vector2(0,3,5));*/ -} +Curve2D::Curve2D() {} /***********************************************************************************/ /***********************************************************************************/ @@ -1026,7 +1210,20 @@ int Curve3D::get_point_count() const { return points.size(); } -void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) { +void Curve3D::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (points.size() >= p_count) { + points.resize(p_count); + mark_dirty(); + } else { + for (int i = p_count - points.size(); i > 0; i--) { + _add_point(Vector3()); + } + } + notify_property_list_changed(); +} + +void Curve3D::_add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) { Point n; n.position = p_position; n.in = p_in; @@ -1037,16 +1234,19 @@ void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Ve points.push_back(n); } - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); +} + +void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) { + _add_point(p_position, p_in, p_out, p_atpos); + notify_property_list_changed(); } void Curve3D::set_point_position(int p_index, const Vector3 &p_position) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].position = p_position; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_position(int p_index) const { @@ -1058,8 +1258,7 @@ void Curve3D::set_point_tilt(int p_index, real_t p_tilt) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].tilt = p_tilt; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve3D::get_point_tilt(int p_index) const { @@ -1071,8 +1270,7 @@ void Curve3D::set_point_in(int p_index, const Vector3 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].in = p_in; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_in(int p_index) const { @@ -1084,8 +1282,7 @@ void Curve3D::set_point_out(int p_index, const Vector3 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].out = p_out; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_out(int p_index) const { @@ -1093,18 +1290,22 @@ Vector3 Curve3D::get_point_out(int p_index) const { return points[p_index].out; } -void Curve3D::remove_point(int p_index) { +void Curve3D::_remove_point(int p_index) { ERR_FAIL_INDEX(p_index, points.size()); points.remove_at(p_index); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); +} + +void Curve3D::remove_point(int p_index) { + _remove_point(p_index); + notify_property_list_changed(); } void Curve3D::clear_points() { if (!points.is_empty()) { points.clear(); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); + notify_property_list_changed(); } } @@ -1123,7 +1324,7 @@ Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const { Vector3 p3 = points[p_index + 1].position; Vector3 p2 = p3 + points[p_index + 1].in; - return _bezier_interp(p_offset, p0, p1, p2, p3); + return p0.bezier_interpolate(p1, p2, p3, p_offset); } Vector3 Curve3D::interpolatef(real_t p_findex) const { @@ -1136,11 +1337,16 @@ Vector3 Curve3D::interpolatef(real_t p_findex) const { return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } -void Curve3D::_bake_segment3d(Map<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { +void Curve3D::mark_dirty() { + baked_cache_dirty = true; + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { real_t mp = p_begin + (p_end - p_begin) * 0.5; - Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b); - Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b); - Vector3 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b); + Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); + Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); + Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); Vector3 na = (mid - beg).normalized(); Vector3 nb = (end - mid).normalized(); @@ -1194,6 +1400,7 @@ void Curve3D::_bake() const { List<Plane> pointlist; List<real_t> distlist; + // Start always from origin. pointlist.push_back(Plane(position, points[0].tilt)); distlist.push_back(0.0); @@ -1207,7 +1414,7 @@ void Curve3D::_bake() const { np = 1.0; } - Vector3 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position); + Vector3 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np); real_t d = position.distance_to(npp); if (d > bake_interval) { @@ -1220,7 +1427,7 @@ void Curve3D::_bake() const { real_t mid = low + (hi - low) * 0.5; for (int j = 0; j < iterations; j++) { - npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position); + npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid); d = position.distance_to(npp); if (bake_interval < d) { @@ -1244,16 +1451,22 @@ void Curve3D::_bake() const { p = np; } } - } - Vector3 lastpos = points[points.size() - 1].position; - real_t lastilt = points[points.size() - 1].tilt; + Vector3 npp = points[i + 1].position; + real_t d = position.distance_to(npp); + + position = npp; + Plane post; + post.normal = position; + post.d = points[i + 1].tilt; + + dist += d; + + pointlist.push_back(post); + distlist.push_back(dist); + } - real_t rem = position.distance_to(lastpos); - dist += rem; baked_max_ofs = dist; - pointlist.push_back(Plane(lastpos, lastilt)); - distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); Vector3 *w = baked_point_cache.ptrw(); @@ -1328,7 +1541,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); @@ -1336,18 +1549,19 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { return baked_point_cache.get(0); } - int bpc = baked_point_cache.size(); const Vector3 *r = baked_point_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int start = 0, end = bpc, idx = (end + start) / 2; - // binary search to find baked points + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. while (start < idx) { real_t offset = baked_dist_cache[idx]; if (p_offset <= offset) { @@ -1368,7 +1582,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { if (p_cubic) { Vector3 pre = idx > 0 ? r[idx - 1] : r[idx]; - Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; + Vector3 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1]; return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac); } else { return r[idx].lerp(r[idx + 1], frac); @@ -1380,7 +1594,7 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { _bake(); } - //validate// + // Validate: Curve may not have baked tilts. int pc = baked_tilt_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); @@ -1388,29 +1602,37 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { return baked_tilt_cache.get(0); } - int bpc = baked_tilt_cache.size(); const real_t *r = baked_tilt_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int idx = Math::floor((double)p_offset / (double)bake_interval); - real_t frac = Math::fmod(p_offset, bake_interval); - - if (idx >= bpc - 1) { - return r[bpc - 1]; - } else if (idx == bpc - 2) { - if (frac > 0) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. + while (start < idx) { + real_t offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; } - } else { - frac /= bake_interval; + idx = (end + start) / 2; } + real_t offset_begin = baked_dist_cache[idx]; + real_t offset_end = baked_dist_cache[idx + 1]; + + real_t idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment"); + + real_t frac = (p_offset - offset_begin) / idx_interval; + return Math::lerp(r[idx], r[idx + 1], (real_t)frac); } @@ -1419,8 +1641,7 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) _bake(); } - //validate// - // curve may not have baked up vectors + // Validate: Curve may not have baked up vectors. int count = baked_up_vector_cache.size(); ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D."); @@ -1432,10 +1653,27 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const Vector3 *rp = baked_point_cache.ptr(); const real_t *rt = baked_tilt_cache.ptr(); - real_t offset = CLAMP(p_offset, 0.0f, baked_max_ofs); + int start = 0; + int end = count; + int idx = (end + start) / 2; + // Binary search to find baked points. + while (start < idx) { + real_t offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; + } + idx = (end + start) / 2; + } + + real_t offset_begin = baked_dist_cache[idx]; + real_t offset_end = baked_dist_cache[idx + 1]; + + real_t idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment"); - int idx = Math::floor((double)offset / (double)bake_interval); - real_t frac = Math::fmod(offset, bake_interval) / bake_interval; + real_t frac = (p_offset - offset_begin) / idx_interval; if (idx == count - 1) { return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx]; @@ -1486,13 +1724,13 @@ PackedVector3Array Curve3D::get_baked_up_vectors() const { } Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); @@ -1524,13 +1762,13 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { } real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve3D."); @@ -1566,8 +1804,7 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { void Curve3D::set_bake_interval(real_t p_tolerance) { bake_interval = p_tolerance; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve3D::get_bake_interval() const { @@ -1576,8 +1813,7 @@ real_t Curve3D::get_bake_interval() const { void Curve3D::set_up_vector_enabled(bool p_enable) { up_vector_enabled = p_enable; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } bool Curve3D::is_up_vector_enabled() const { @@ -1626,7 +1862,8 @@ void Curve3D::_set_data(const Dictionary &p_data) { points.write[i].tilt = rt[i]; } - baked_cache_dirty = true; + mark_dirty(); + notify_property_list_changed(); } PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) const { @@ -1635,7 +1872,7 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con if (points.size() == 0) { return tess; } - Vector<Map<real_t, Vector3>> midpoints; + Vector<RBMap<real_t, Vector3>> midpoints; midpoints.resize(points.size() - 1); @@ -1664,8 +1901,77 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +bool Curve3D::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + set_point_position(point_index, p_value); + return true; + } else if (property == "in") { + set_point_in(point_index, p_value); + return true; + } else if (property == "out") { + set_point_out(point_index, p_value); + return true; + } else if (property == "tilt") { + set_point_tilt(point_index, p_value); + return true; + } + } + return false; +} + +bool Curve3D::_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("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "in") { + r_ret = get_point_in(point_index); + return true; + } else if (property == "out") { + r_ret = get_point_out(point_index); + return true; + } else if (property == "tilt") { + r_ret = get_point_tilt(point_index); + return true; + } + } + return false; +} + +void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != points.size() - 1) { + pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/tilt", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } +} + void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve3D::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position); @@ -1700,13 +2006,10 @@ void Curve3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); ADD_GROUP("Up Vector", "up_vector_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled"); } -Curve3D::Curve3D() { - /* add_point(Vector3(-1,0,0)); - add_point(Vector3(0,2,0)); - add_point(Vector3(0,3,5));*/ -} +Curve3D::Curve3D() {} diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 767900b843..08807b1b6e 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -76,12 +76,13 @@ public: int get_point_count() const { return _points.size(); } + void set_point_count(int p_count); + int add_point(Vector2 p_position, real_t left_tangent = 0, real_t right_tangent = 0, TangentMode left_mode = TANGENT_FREE, TangentMode right_mode = TANGENT_FREE); - void remove_point(int p_index); void clear_points(); @@ -126,11 +127,21 @@ public: void ensure_default_setup(real_t p_min, real_t p_max); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + protected: static void _bind_methods(); private: void mark_dirty(); + int _add_point(Vector2 p_position, + real_t left_tangent = 0, + real_t right_tangent = 0, + TangentMode left_mode = TANGENT_FREE, + TangentMode right_mode = TANGENT_FREE); + void _remove_point(int p_index); Vector<Point> _points; bool _baked_cache_dirty = false; @@ -164,19 +175,29 @@ class Curve2D : public Resource { mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; + void mark_dirty(); + void _bake() const; real_t bake_interval = 5.0; - void _bake_segment2d(Map<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + void _add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1); + void _remove_point(int p_index); + protected: static void _bind_methods(); public: int get_point_count() const; + void set_point_count(int p_count); void add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1); void set_point_position(int p_index, const Vector2 &p_position); Vector2 get_point_position(int p_index) const; @@ -228,20 +249,30 @@ class Curve3D : public Resource { mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; + void mark_dirty(); + void _bake() const; real_t bake_interval = 0.2; bool up_vector_enabled = true; - void _bake_segment3d(Map<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + void _add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1); + void _remove_point(int p_index); + protected: static void _bind_methods(); public: int get_point_count() const; + void set_point_count(int p_count); void add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1); void set_point_position(int p_index, const Vector3 &p_position); Vector3 get_point_position(int p_index) const; diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp index 5eeb62d17b..345df5ffed 100644 --- a/scene/resources/cylinder_shape_3d.cpp +++ b/scene/resources/cylinder_shape_3d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "cylinder_shape_3d.h" + #include "servers/physics_server_3d.h" Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const { @@ -72,6 +73,7 @@ void CylinderShape3D::_update_shape() { } void CylinderShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "CylinderShape3D radius cannot be negative."); radius = p_radius; _update_shape(); notify_change_to_owners(); @@ -82,6 +84,7 @@ float CylinderShape3D::get_radius() const { } void CylinderShape3D::set_height(float p_height) { + ERR_FAIL_COND_MSG(p_height < 0, "CylinderShape3D height cannot be negative."); height = p_height; _update_shape(); notify_change_to_owners(); @@ -97,8 +100,8 @@ void CylinderShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape3D::set_height); ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape3D::get_height); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,4096,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); } CylinderShape3D::CylinderShape3D() : diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h index 65427423c8..f554358044 100644 --- a/scene/resources/cylinder_shape_3d.h +++ b/scene/resources/cylinder_shape_3d.h @@ -54,4 +54,4 @@ public: CylinderShape3D(); }; -#endif // CYLINDER_SHAPE_H +#endif // CYLINDER_SHAPE_3D_H diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index f27bd9144e..5bef7e5a6c 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -8,10 +8,10 @@ import default_theme_icons_builders env.add_source_files(env.scene_sources, "*.cpp") -env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.ttf") +env.Depends("#scene/resources/default_theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.woff2") env.CommandNoCache( "#scene/resources/default_theme/default_font.gen.h", - "#thirdparty/fonts/OpenSans_SemiBold.ttf", + "#thirdparty/fonts/OpenSans_SemiBold.woff2", run_in_subprocess(default_theme_builders.make_fonts_header), ) diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 61114333fb..520a0a04ed 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -76,7 +76,6 @@ static Ref<StyleBoxFlat> sb_expand(Ref<StyleBoxFlat> p_sbox, float p_left, float // See also `editor_generate_icon()` in `editor/editor_themes.cpp`. static Ref<ImageTexture> generate_icon(int p_index) { - Ref<ImageTexture> icon = memnew(ImageTexture); Ref<Image> img = memnew(Image); #ifdef MODULE_SVG_ENABLED @@ -87,9 +86,8 @@ static Ref<ImageTexture> generate_icon(int p_index) { ImageLoaderSVG img_loader; img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, false); #endif - icon->create_from_image(img); - return icon; + return ImageTexture::create_from_image(img); } static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { @@ -103,7 +101,7 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margi return style; } -void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) { +void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) { scale = p_scale; // Font colors @@ -177,7 +175,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4)); - theme->set_constant("hseparation", "Button", 2 * scale); + theme->set_constant("h_separation", "Button", 2 * scale); // LinkButton @@ -230,7 +228,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color); theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "OptionButton", 2 * scale); + theme->set_constant("h_separation", "OptionButton", 2 * scale); theme->set_constant("arrow_margin", "OptionButton", 4 * scale); theme->set_constant("outline_size", "OptionButton", 0); @@ -252,7 +250,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3)); theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "MenuButton", 3 * scale); + theme->set_constant("h_separation", "MenuButton", 3 * scale); theme->set_constant("outline_size", "MenuButton", 0); // CheckBox @@ -295,8 +293,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color); theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1)); - theme->set_constant("hseparation", "CheckBox", 4 * scale); - theme->set_constant("check_vadjust", "CheckBox", 0 * scale); + theme->set_constant("h_separation", "CheckBox", 4 * scale); + theme->set_constant("check_v_adjust", "CheckBox", 0 * scale); theme->set_constant("outline_size", "CheckBox", 0); // CheckButton @@ -335,8 +333,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color); theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "CheckButton", 4 * scale); - theme->set_constant("check_vadjust", "CheckButton", 0 * scale); + theme->set_constant("h_separation", "CheckButton", 4 * scale); + theme->set_constant("check_v_adjust", "CheckButton", 0 * scale); theme->set_constant("outline_size", "CheckButton", 0); // Label @@ -467,6 +465,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27)); theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13)); theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.29)); + theme->set_color("completion_scroll_hovered_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.4)); theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67)); theme->set_color("font_color", "CodeEdit", control_font_color); theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0)); @@ -570,7 +569,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te // Window theme->set_stylebox("embedded_border", "Window", sb_expand(make_flat_stylebox(style_popup_color, 10, 28, 10, 8), 8, 32, 8, 6)); - theme->set_constant("scaleborder_size", "Window", 4 * scale); theme->set_font("title_font", "Window", Ref<Font>()); theme->set_font_size("title_font_size", "Window", -1); @@ -582,8 +580,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_icon("close", "Window", icons["close"]); theme->set_icon("close_pressed", "Window", icons["close_hl"]); - theme->set_constant("close_h_ofs", "Window", 18 * scale); - theme->set_constant("close_v_ofs", "Window", 24 * scale); + theme->set_constant("close_h_offset", "Window", 18 * scale); + theme->set_constant("close_v_offset", "Window", 24 * scale); // Dialogs @@ -605,7 +603,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_icon("file", "FileDialog", icons["file"]); theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1)); theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1)); - theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7)); + theme->set_color("files_disabled", "FileDialog", Color(1, 1, 1, 0.25)); // Popup @@ -649,11 +647,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_icon("unchecked", "PopupMenu", icons["unchecked"]); theme->set_icon("radio_checked", "PopupMenu", icons["radio_checked"]); theme->set_icon("radio_unchecked", "PopupMenu", icons["radio_unchecked"]); - theme->set_icon("submenu", "PopupMenu", icons["arrow_right"]); - theme->set_icon("submenu_mirrored", "PopupMenu", icons["arrow_left"]); + theme->set_icon("submenu", "PopupMenu", icons["popup_menu_arrow_right"]); + theme->set_icon("submenu_mirrored", "PopupMenu", icons["popup_menu_arrow_left"]); theme->set_font("font", "PopupMenu", Ref<Font>()); + theme->set_font("font_separator", "PopupMenu", Ref<Font>()); theme->set_font_size("font_size", "PopupMenu", -1); + theme->set_font_size("font_separator_size", "PopupMenu", -1); theme->set_color("font_color", "PopupMenu", control_font_color); theme->set_color("font_accelerator_color", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8)); @@ -661,10 +661,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_hover_color", "PopupMenu", control_font_color); theme->set_color("font_separator_color", "PopupMenu", control_font_color); theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1)); + theme->set_color("font_separator_outline_color", "PopupMenu", Color(1, 1, 1)); - theme->set_constant("hseparation", "PopupMenu", 4 * scale); - theme->set_constant("vseparation", "PopupMenu", 4 * scale); + theme->set_constant("h_separation", "PopupMenu", 4 * scale); + theme->set_constant("v_separation", "PopupMenu", 4 * scale); theme->set_constant("outline_size", "PopupMenu", 0); + theme->set_constant("separator_outline_size", "PopupMenu", 0); theme->set_constant("item_start_padding", "PopupMenu", 2 * scale); theme->set_constant("item_end_padding", "PopupMenu", 2 * scale); @@ -682,13 +684,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te graphnode_breakpoint->set_border_color(Color(0.9, 0.29, 0.3)); Ref<StyleBoxFlat> graphnode_position = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4); graphnode_position->set_border_color(Color(0.98, 0.89, 0.27)); + Ref<StyleBoxEmpty> graphnode_slot = make_empty_stylebox(0, 0, 0, 0); theme->set_stylebox("frame", "GraphNode", graphnode_normal); - theme->set_stylebox("selectedframe", "GraphNode", graphnode_selected); + theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected); theme->set_stylebox("comment", "GraphNode", graphnode_comment_normal); - theme->set_stylebox("commentfocus", "GraphNode", graphnode_comment_selected); + theme->set_stylebox("comment_focus", "GraphNode", graphnode_comment_selected); theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint); theme->set_stylebox("position", "GraphNode", graphnode_position); + theme->set_stylebox("slot", "GraphNode", graphnode_slot); theme->set_icon("port", "GraphNode", icons["graph_port"]); theme->set_icon("close", "GraphNode", icons["close"]); @@ -700,6 +704,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_constant("separation", "GraphNode", 2 * scale); theme->set_constant("title_offset", "GraphNode", 26 * scale); theme->set_constant("close_offset", "GraphNode", 22 * scale); + theme->set_constant("close_h_offset", "GraphNode", 22 * scale); theme->set_constant("port_offset", "GraphNode", 0); // Tree @@ -742,8 +747,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te 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); - theme->set_constant("vseparation", "Tree", 4 * scale); + theme->set_constant("h_separation", "Tree", 4 * scale); + theme->set_constant("v_separation", "Tree", 4 * scale); theme->set_constant("item_margin", "Tree", 16 * scale); theme->set_constant("button_margin", "Tree", 4 * scale); theme->set_constant("draw_relationship_lines", "Tree", 0); @@ -760,8 +765,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_stylebox("bg", "ItemList", make_flat_stylebox(style_normal_color)); theme->set_stylebox("bg_focus", "ItemList", focus); - theme->set_constant("hseparation", "ItemList", 4); - theme->set_constant("vseparation", "ItemList", 2); + theme->set_constant("h_separation", "ItemList", 4); + theme->set_constant("v_separation", "ItemList", 2); theme->set_constant("icon_margin", "ItemList", 4); theme->set_constant("line_separation", "ItemList", 2 * scale); @@ -801,6 +806,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_icon("increment_highlight", "TabContainer", icons["scroll_button_right_hl"]); theme->set_icon("decrement", "TabContainer", icons["scroll_button_left"]); theme->set_icon("decrement_highlight", "TabContainer", icons["scroll_button_left_hl"]); + theme->set_icon("drop_mark", "TabContainer", icons["tabs_drop_mark"]); theme->set_icon("menu", "TabContainer", icons["tabs_menu"]); theme->set_icon("menu_highlight", "TabContainer", icons["tabs_menu_hl"]); @@ -811,6 +817,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_unselected_color", "TabContainer", control_font_low_color); theme->set_color("font_disabled_color", "TabContainer", control_font_disabled_color); theme->set_color("font_outline_color", "TabContainer", Color(1, 1, 1)); + theme->set_color("drop_mark_color", "TabContainer", Color(1, 1, 1)); theme->set_constant("side_margin", "TabContainer", 8 * scale); theme->set_constant("icon_separation", "TabContainer", 4 * scale); @@ -828,6 +835,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_icon("increment_highlight", "TabBar", icons["scroll_button_right_hl"]); theme->set_icon("decrement", "TabBar", icons["scroll_button_left"]); theme->set_icon("decrement_highlight", "TabBar", icons["scroll_button_left_hl"]); + theme->set_icon("drop_mark", "TabBar", icons["tabs_drop_mark"]); theme->set_icon("close", "TabBar", icons["close"]); theme->set_font("font", "TabBar", Ref<Font>()); @@ -837,8 +845,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_unselected_color", "TabBar", control_font_low_color); theme->set_color("font_disabled_color", "TabBar", control_font_disabled_color); theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1)); + theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1)); - theme->set_constant("hseparation", "TabBar", 4 * scale); + theme->set_constant("h_separation", "TabBar", 4 * scale); theme->set_constant("outline_size", "TabBar", 0); // Separators @@ -888,7 +897,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "ColorPickerButton", 2 * scale); + theme->set_constant("h_separation", "ColorPickerButton", 2 * scale); theme->set_constant("outline_size", "ColorPickerButton", 0); // ColorPresetButton @@ -907,8 +916,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_stylebox("panel", "TooltipPanel", make_flat_stylebox(Color(0, 0, 0, 0.5), 2 * default_margin, 0.5 * default_margin, 2 * default_margin, 0.5 * default_margin)); - theme->set_font("font", "TooltipLabel", Ref<Font>()); theme->set_font_size("font_size", "TooltipLabel", -1); + theme->set_font("font", "TooltipLabel", Ref<Font>()); theme->set_color("font_color", "TooltipLabel", control_font_color); theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0)); @@ -924,11 +933,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_stylebox("normal", "RichTextLabel", make_empty_stylebox(0, 0, 0, 0)); theme->set_font("normal_font", "RichTextLabel", Ref<Font>()); - theme->set_font("bold_font", "RichTextLabel", Ref<Font>()); - theme->set_font("italics_font", "RichTextLabel", Ref<Font>()); - theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>()); + theme->set_font("bold_font", "RichTextLabel", bold_font); + theme->set_font("italics_font", "RichTextLabel", italics_font); + theme->set_font("bold_italics_font", "RichTextLabel", bold_italics_font); theme->set_font("mono_font", "RichTextLabel", Ref<Font>()); - theme->set_font_size("normal_font_size", "RichTextLabel", -1); theme->set_font_size("bold_font_size", "RichTextLabel", -1); theme->set_font_size("italics_font_size", "RichTextLabel", -1); @@ -948,8 +956,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale); theme->set_constant("line_separation", "RichTextLabel", 0 * scale); - theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale); - theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale); + theme->set_constant("table_h_separation", "RichTextLabel", 3 * scale); + theme->set_constant("table_v_separation", "RichTextLabel", 3 * scale); theme->set_constant("outline_size", "RichTextLabel", 0); @@ -968,16 +976,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_constant("margin_top", "MarginContainer", 0 * scale); theme->set_constant("margin_right", "MarginContainer", 0 * scale); theme->set_constant("margin_bottom", "MarginContainer", 0 * scale); - theme->set_constant("hseparation", "GridContainer", 4 * scale); - theme->set_constant("vseparation", "GridContainer", 4 * scale); + theme->set_constant("h_separation", "GridContainer", 4 * scale); + theme->set_constant("v_separation", "GridContainer", 4 * scale); theme->set_constant("separation", "HSplitContainer", 12 * scale); theme->set_constant("separation", "VSplitContainer", 12 * scale); theme->set_constant("autohide", "HSplitContainer", 1 * scale); theme->set_constant("autohide", "VSplitContainer", 1 * scale); - theme->set_constant("hseparation", "HFlowContainer", 4 * scale); - theme->set_constant("vseparation", "HFlowContainer", 4 * scale); - theme->set_constant("hseparation", "VFlowContainer", 4 * scale); - theme->set_constant("vseparation", "VFlowContainer", 4 * scale); + theme->set_constant("h_separation", "HFlowContainer", 4 * scale); + theme->set_constant("v_separation", "HFlowContainer", 4 * scale); + theme->set_constant("h_separation", "VFlowContainer", 4 * scale); + theme->set_constant("v_separation", "VFlowContainer", 4 * scale); theme->set_stylebox("panel", "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); @@ -993,13 +1001,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3)); theme->set_color("selection_stroke", "GraphEdit", Color(1, 1, 1, 0.8)); theme->set_color("activity", "GraphEdit", Color(1, 1, 1)); - theme->set_constant("bezier_len_pos", "GraphEdit", 80 * scale); - theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale); // Visual Node Ports - theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale); - theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale); + theme->set_constant("port_hotzone_inner_extent", "GraphEdit", 22 * scale); + theme->set_constant("port_hotzone_outer_extent", "GraphEdit", 26 * scale); theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0)); Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0); @@ -1018,13 +1024,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2); } -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa) { +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel, TextServer::Hinting p_font_hinting, bool p_font_antialiased, bool p_font_msdf, bool p_font_generate_mipmaps) { Ref<Theme> t; t.instantiate(); Ref<StyleBox> default_style; Ref<Texture2D> default_icon; Ref<Font> default_font; + Ref<FontVariation> bold_font; + Ref<FontVariation> bold_italics_font; + Ref<FontVariation> italics_font; float default_scale = CLAMP(p_scale, 0.5, 8.0); if (p_font.is_valid()) { @@ -1034,21 +1043,34 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos // Use the default DynamicFont (separate from the editor 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; + Ref<FontFile> dynamic_font; dynamic_font.instantiate(); - - Ref<FontData> dynamic_font_data; - dynamic_font_data.instantiate(); - dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); - dynamic_font_data->set_subpixel_positioning(p_subpixel); - dynamic_font_data->set_hinting(p_hinting); - dynamic_font_data->set_antialiased(p_aa); - dynamic_font->add_data(dynamic_font_data); + dynamic_font->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); + dynamic_font->set_subpixel_positioning(p_font_subpixel); + dynamic_font->set_hinting(p_font_hinting); + dynamic_font->set_antialiased(p_font_antialiased); + dynamic_font->set_multichannel_signed_distance_field(p_font_msdf); + dynamic_font->set_generate_mipmaps(p_font_generate_mipmaps); default_font = dynamic_font; } - fill_default_theme(t, default_font, default_icon, default_style, default_scale); + if (default_font.is_valid()) { + bold_font.instantiate(); + bold_font->set_base_font(default_font); + bold_font->set_variation_embolden(1.2); + + bold_italics_font.instantiate(); + bold_italics_font->set_base_font(default_font); + bold_italics_font->set_variation_embolden(1.2); + bold_italics_font->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); + + italics_font.instantiate(); + italics_font->set_base_font(default_font); + italics_font->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); + } + + fill_default_theme(t, default_font, bold_font, bold_italics_font, italics_font, default_icon, default_style, default_scale); Theme::set_default(t); Theme::set_fallback_base_scale(default_scale); diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index 28afd5f5e1..9b070a90cc 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -35,8 +35,8 @@ const int default_font_size = 16; -void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa); +void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, bool p_font_antialiased = true, bool p_font_msdf = false, bool p_font_generate_mipmaps = false); void clear_default_theme(); -#endif +#endif // DEFAULT_THEME_H diff --git a/scene/resources/default_theme/popup_menu_arrow_left.svg b/scene/resources/default_theme/popup_menu_arrow_left.svg new file mode 100644 index 0000000000..8fae265a3b --- /dev/null +++ b/scene/resources/default_theme/popup_menu_arrow_left.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m5.4999793 11-3-3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg> diff --git a/scene/resources/default_theme/popup_menu_arrow_right.svg b/scene/resources/default_theme/popup_menu_arrow_right.svg new file mode 100644 index 0000000000..03f05fc46e --- /dev/null +++ b/scene/resources/default_theme/popup_menu_arrow_right.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m2.5000207 11 3-3-3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg> diff --git a/scene/resources/default_theme/tabs_drop_mark.svg b/scene/resources/default_theme/tabs_drop_mark.svg new file mode 100644 index 0000000000..b1415bec45 --- /dev/null +++ b/scene/resources/default_theme/tabs_drop_mark.svg @@ -0,0 +1 @@ +<svg height="32" viewBox="0 0 16 32" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1h6v30h-6z" fill="#d3d3d3"/></svg> diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 82d8ad4444..a8d4903dad 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -189,6 +189,7 @@ void Environment::_update_ambient_light() { void Environment::set_tonemapper(ToneMapper p_tone_mapper) { tone_mapper = p_tone_mapper; _update_tonemap(); + notify_property_list_changed(); } Environment::ToneMapper Environment::get_tonemapper() const { @@ -1049,6 +1050,10 @@ void Environment::_validate_property(PropertyInfo &property) const { } } + if (property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } + if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) { property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } @@ -1079,8 +1084,9 @@ void Environment::_validate_property(PropertyInfo &property) const { "fog_", "volumetric_fog_", "auto_exposure_", - "ss_reflections_", + "ssr_", "ssao_", + "ssil_", "sdfgi_", "glow_", "adjustment_", @@ -1090,8 +1096,7 @@ void Environment::_validate_property(PropertyInfo &property) const { static const char *high_end_prefixes[] = { "auto_exposure_", - "tonemap_", - "ss_reflections_", + "ssr_", "ssao_", nullptr @@ -1173,8 +1178,8 @@ void Environment::_bind_methods() { ADD_GROUP("Sky", "sky_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_sky", "get_sky"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_custom_fov", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_sky_custom_fov", "get_sky_custom_fov"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "sky_rotation"), "set_sky_rotation", "get_sky_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_custom_fov", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_sky_custom_fov", "get_sky_custom_fov"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "sky_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_sky_rotation", "get_sky_rotation"); // Ambient light @@ -1241,12 +1246,12 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ssr_depth_tolerance", "depth_tolerance"), &Environment::set_ssr_depth_tolerance); ClassDB::bind_method(D_METHOD("get_ssr_depth_tolerance"), &Environment::get_ssr_depth_tolerance); - ADD_GROUP("SS Reflections", "ss_reflections_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ss_reflections_enabled"), "set_ssr_enabled", "is_ssr_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "ss_reflections_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ss_reflections_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance"); + ADD_GROUP("SSR", "ssr_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssr_enabled"), "set_ssr_enabled", "is_ssr_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ssr_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssr_depth_tolerance", PROPERTY_HINT_RANGE, "0.01,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance"); // SSAO ClassDB::bind_method(D_METHOD("set_ssao_enabled", "enabled"), &Environment::set_ssao_enabled); @@ -1293,7 +1298,7 @@ void Environment::_bind_methods() { ADD_GROUP("SSIL", "ssil_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssil_enabled"), "set_ssil_enabled", "is_ssil_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater"), "set_ssil_radius", "get_ssil_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_radius", PROPERTY_HINT_RANGE, "0.01,16,0.01,or_greater,suffix:m"), "set_ssil_radius", "get_ssil_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_intensity", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_ssil_intensity", "get_ssil_intensity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_sharpness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_sharpness", "get_ssil_sharpness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssil_normal_rejection", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ssil_normal_rejection", "get_ssil_normal_rejection"); @@ -1418,9 +1423,9 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_fog_density", "get_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater,suffix:m"), "set_fog_height", "get_fog_height"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density"); ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); @@ -1461,7 +1466,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.0,0.999,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.5,0.99,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount"); // Adjustment diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp index a05ef0c779..39ade85af6 100644 --- a/scene/resources/fog_material.cpp +++ b/scene/resources/fog_material.cpp @@ -148,11 +148,11 @@ void FogMaterial::_update_shader() { shader_type fog; uniform float density : hint_range(0, 1, 0.0001) = 1.0; -uniform vec4 albedo : hint_color = vec4(1.0); -uniform vec4 emission : hint_color = vec4(0, 0, 0, 1); +uniform vec4 albedo : source_color = vec4(1.0); +uniform vec4 emission : source_color = vec4(0, 0, 0, 1); uniform float height_falloff = 0.0; uniform float edge_fade = 0.1; -uniform sampler3D density_texture: hint_white; +uniform sampler3D density_texture: hint_default_white; void fog() { diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 5b57e93950..c469946b45 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -30,419 +30,566 @@ #include "font.h" +#include "core/core_string_names.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/string/translation.h" +#include "core/templates/hash_map.h" #include "core/templates/hashfuncs.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" +#include "scene/resources/theme.h" -_FORCE_INLINE_ void FontData::_clear_cache() { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->free(cache[i]); - cache.write[i] = RID(); +/*************************************************************************/ +/* Font */ +/*************************************************************************/ + +void Font::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_fallbacks", "fallbacks"), &Font::set_fallbacks); + ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks); + + // Output. + ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D())); + ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); + + // Font metrics. + ClassDB::bind_method(D_METHOD("get_height", "font_size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_ascent", "font_size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_descent", "font_size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_position", "font_size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "font_size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); + + ClassDB::bind_method(D_METHOD("get_font_name"), &Font::get_font_name); + ClassDB::bind_method(D_METHOD("get_font_style_name"), &Font::get_font_style_name); + ClassDB::bind_method(D_METHOD("get_font_style"), &Font::get_font_style); + + ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features); + + // Drawing string. + ClassDB::bind_method(D_METHOD("set_cache_capacity", "single_line", "multi_line"), &Font::set_cache_capacity); + + ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "jst_flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "brk_flags", "jst_flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "jst_flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "jst_flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + + // Drawing char. + ClassDB::bind_method(D_METHOD("get_char_size", "char", "font_size"), &Font::get_char_size); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "font_size", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "font_size", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); + + // Helper functions. + ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + + ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &Font::is_language_supported); + ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &Font::is_script_supported); + + ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &Font::get_supported_feature_list); + ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &Font::get_supported_variation_list); + ClassDB::bind_method(D_METHOD("get_face_count"), &Font::get_face_count); +} + +void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND(p_depth > MAX_FALLBACK_DEPTH); + if (p_f.is_valid()) { + RID rid = p_f->_get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); + } + const TypedArray<Font> &_fallbacks = p_f->get_fallbacks(); + for (int i = 0; i < _fallbacks.size(); i++) { + _update_rids_fb(_fallbacks[i], p_depth + 1); } } } -_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const { - if (unlikely(p_cache_index >= cache.size())) { - cache.resize(p_cache_index + 1); - } - if (unlikely(!cache[p_cache_index].is_valid())) { - cache.write[p_cache_index] = TS->create_font(); - TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); - TS->font_set_antialiased(cache[p_cache_index], antialiased); - TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); - TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); - TS->font_set_msdf_size(cache[p_cache_index], msdf_size); - TS->font_set_fixed_size(cache[p_cache_index], fixed_size); - TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); - TS->font_set_hinting(cache[p_cache_index], hinting); - TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); - TS->font_set_oversampling(cache[p_cache_index], oversampling); - } +void Font::_update_rids() const { + rids.clear(); + _update_rids_fb(const_cast<Font *>(this), 0); + dirty_rids = false; } -void FontData::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontData::load_bitmap_font); - ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontData::load_dynamic_font); +void Font::_invalidate_rids() { + rids.clear(); + dirty_rids = true; - ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data); + cache.clear(); + cache_wrap.clear(); - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); - ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased); + emit_changed(); +} - ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name); - ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name); +bool Font::_is_cyclic(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND_V(p_depth > MAX_FALLBACK_DEPTH, false); + if (p_f.is_null()) { + return false; + } + for (int i = 0; i < p_f->fallbacks.size(); i++) { + const Ref<Font> &f = p_f->fallbacks[i]; + if (f == this) { + return true; + } + return _is_cyclic(f, p_depth + 1); + } + return false; +} - ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name); - ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name); +void Font::reset_state() { + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style); - ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style); +// Fallbacks. +void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { + ERR_FAIL_COND(_is_cyclic(this, 0)); + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids)); + } + } + fallbacks = p_fallbacks; + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + } + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field); - ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field); +TypedArray<Font> Font::get_fallbacks() const { + return fallbacks; +} - ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range); - ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range); +// Output. +TypedArray<RID> Font::get_rids() const { + if (dirty_rids) { + _update_rids(); + } + return rids; +} - ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size); - ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size); +// Drawing string. +real_t Font::get_height(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size) + TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM) + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontData::set_fixed_size); - ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontData::get_fixed_size); +real_t Font::get_ascent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter); - ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter); +real_t Font::get_descent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM); +} - ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting); - ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting); +real_t Font::get_underline_position(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_position(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontData::set_subpixel_positioning); - ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontData::get_subpixel_positioning); +real_t Font::get_underline_thickness(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_font_size)); + } + return ret; +} - ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling); - ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling); +String Font::get_font_name() const { + return TS->font_get_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache); +String Font::get_font_style_name() const { + return TS->font_get_style_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count); - ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache); - ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache); +BitField<TextServer::FontStyle> Font::get_font_style() const { + return TS->font_get_style(_get_rid()); +} - ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list); - ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache); - ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache); +Dictionary Font::get_opentype_features() const { + return Dictionary(); +} - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates); +// Drawing string. +void Font::set_cache_capacity(int p_single_line, int p_multi_line) { + cache.set_capacity(p_single_line); + cache_wrap.set_capacity(p_multi_line); +} - ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent); - ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent); +Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent); - ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_horizontal_alignment(p_alignment); + buffer->set_width(p_width); + buffer->set_flags(p_jst_flags); + } + cache.insert(hash, buffer); + } + return buffer->get_size(); +} - ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position); - ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position); +Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale); - ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing); + return lines_buffer->get_size(); +} - ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count); - ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures); - ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture); +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image); - ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets); - ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list); - ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs); - ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } - ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance); - ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance); + buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset); - ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset); +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size); - ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect); - ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx); - ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list); - ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map); - ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning); + lines_buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning); - ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning); +void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + } + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range); - ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported); - ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override); - ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + buffer->set_flags(p_jst_flags); + } - ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported); - ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override); - ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides); + buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontData::set_opentype_feature_overrides); - ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontData::get_opentype_feature_overrides); +void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_brk_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_jst_flags.operator uint32_t(), hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_break_flags(p_brk_flags); + lines_buffer->set_justification_flags(p_jst_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list); - ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); + lines_buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); } -bool FontData::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - set_language_support_override(lang, p_value); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - set_script_support_override(script, p_value); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - set_variation_coordinates(cache_index, p_value); - return true; - } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - set_ascent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "descent") { - set_descent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_position") { - set_underline_position(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_thickness") { - set_underline_thickness(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "scale") { - set_scale(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "spacing_glyph") { - set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value); - return true; - } else if (tokens[4] == "spacing_space") { - set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - set_texture_image(cache_index, sz, texture_index, p_value); - return true; - } else if (tokens[6] == "offsets") { - set_texture_offsets(cache_index, sz, texture_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - set_glyph_advance(cache_index, sz.x, glyph_index, p_value); - return true; - } else if (tokens[6] == "offset") { - set_glyph_offset(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "size") { - set_glyph_size(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "uv_rect") { - set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "texture_idx") { - set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - set_kerning(cache_index, sz.x, gp, p_value); - return true; - } +// Drawing char. +Size2 Font::get_char_size(char32_t p_char, int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + return Size2(TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x, get_height(p_font_size)); } } - return false; + return Size2(); } -bool FontData::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - r_ret = get_language_support_override(lang); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - r_ret = get_script_support_override(script); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - r_ret = get_variation_coordinates(cache_index); - return true; +real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph(rids[i], p_canvas_item, p_font_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - r_ret = get_ascent(cache_index, sz.x); - return true; - } else if (tokens[4] == "descent") { - r_ret = get_descent(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_position") { - r_ret = get_underline_position(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_thickness") { - r_ret = get_underline_thickness(cache_index, sz.x); - return true; - } else if (tokens[4] == "scale") { - r_ret = get_scale(cache_index, sz.x); - return true; - } else if (tokens[4] == "spacing_glyph") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH); - return true; - } else if (tokens[4] == "spacing_space") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - r_ret = get_texture_image(cache_index, sz, texture_index); - return true; - } else if (tokens[6] == "offsets") { - r_ret = get_texture_offsets(cache_index, sz, texture_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); - return true; - } else if (tokens[6] == "offset") { - r_ret = get_glyph_offset(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "size") { - r_ret = get_glyph_size(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "uv_rect") { - r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "texture_idx") { - r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - r_ret = get_kerning(cache_index, sz.x, gp); - return true; - } + } + return 0.f; +} + +real_t Font::draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, int p_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_font_size, p_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } } - return false; + return 0.f; } -void FontData::_get_property_list(List<PropertyInfo> *p_list) const { - Vector<String> lang_over = get_language_support_overrides(); - for (int i = 0; i < lang_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); +// Helper functions. +bool Font::has_char(char32_t p_char) const { + if (dirty_rids) { + _update_rids(); } - Vector<String> scr_over = get_script_support_overrides(); - for (int i = 0; i < scr_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + return true; + } } - for (int i = 0; i < cache.size(); i++) { - String prefix = "cache/" + itos(i) + "/"; - Array sizes = get_size_cache_list(i); - p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - for (int j = 0; j < sizes.size(); j++) { - Vector2i sz = sizes[j]; - String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } + return false; +} - int tx_cnt = get_texture_count(i, sz); - for (int k = 0; k < tx_cnt; k++) { - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); - } - Array glyphs = get_glyph_list(i, sz); - for (int k = 0; k < glyphs.size(); k++) { - const int32_t &gl = glyphs[k]; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - if (sz.y == 0) { - Array kerning_map = get_kerning_list(i, sz.x); - for (int k = 0; k < kerning_map.size(); k++) { - const Vector2i &gl_pair = kerning_map[k]; - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } +String Font::get_supported_chars() const { + if (dirty_rids) { + _update_rids(); + } + String chars; + for (int i = 0; i < rids.size(); i++) { + String data_chars = TS->font_get_supported_chars(rids[i]); + for (int j = 0; j < data_chars.length(); j++) { + if (chars.find_char(data_chars[j]) == -1) { + chars += data_chars[j]; } } } + return chars; } -void FontData::reset_state() { - _clear_cache(); - data.clear(); - data_ptr = nullptr; - data_size = 0; - cache.clear(); +bool Font::is_language_supported(const String &p_language) const { + return TS->font_is_language_supported(_get_rid(), p_language); +} - antialiased = true; - msdf = false; - force_autohinter = false; - hinting = TextServer::HINTING_LIGHT; - subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; - msdf_pixel_range = 14; - msdf_size = 128; - fixed_size = 0; - oversampling = 0.f; +bool Font::is_script_supported(const String &p_script) const { + return TS->font_is_script_supported(_get_rid(), p_script); +} + +Dictionary Font::get_supported_feature_list() const { + return TS->font_supported_feature_list(_get_rid()); +} + +Dictionary Font::get_supported_variation_list() const { + return TS->font_supported_variation_list(_get_rid()); +} + +int64_t Font::get_face_count() const { + return TS->font_get_face_count(_get_rid()); +} + +Font::Font() { + cache.set_capacity(64); + cache_wrap.set_capacity(16); +} + +Font::~Font() { + reset_state(); +} + +/*************************************************************************/ +/* FontFile */ +/*************************************************************************/ + +_FORCE_INLINE_ void FontFile::_clear_cache() { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->free_rid(cache[i]); + cache.write[i] = RID(); + } + } } -void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { +_FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index) const { + if (unlikely(p_cache_index >= cache.size())) { + cache.resize(p_cache_index + 1); + } + if (unlikely(!cache[p_cache_index].is_valid())) { + cache.write[p_cache_index] = TS->create_font(); + TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); + TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); + TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); + TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); + TS->font_set_msdf_size(cache[p_cache_index], msdf_size); + TS->font_set_fixed_size(cache[p_cache_index], fixed_size); + TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); + TS->font_set_hinting(cache[p_cache_index], hinting); + TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); + TS->font_set_oversampling(cache[p_cache_index], oversampling); + } +} + +void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -489,7 +636,7 @@ void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a); } -void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -589,7 +736,7 @@ void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao); } -void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -645,7 +792,7 @@ void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o); } -void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -674,7 +821,7 @@ void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g); } -void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -717,27 +864,494 @@ void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o); } +void FontFile::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontFile::load_bitmap_font); + ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontFile::load_dynamic_font); + + ClassDB::bind_method(D_METHOD("set_data", "data"), &FontFile::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &FontFile::get_data); + + ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontFile::set_font_name); + ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontFile::set_font_style_name); + ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontFile::set_font_style); + + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontFile::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &FontFile::is_antialiased); + + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontFile::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontFile::get_generate_mipmaps); + + ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontFile::set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontFile::is_multichannel_signed_distance_field); + + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontFile::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontFile::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontFile::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontFile::get_msdf_size); + + ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size); + ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size); + + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontFile::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontFile::is_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontFile::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &FontFile::get_hinting); + + ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontFile::set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontFile::get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontFile::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &FontFile::get_oversampling); + + ClassDB::bind_method(D_METHOD("get_cache_count"), &FontFile::get_cache_count); + ClassDB::bind_method(D_METHOD("clear_cache"), &FontFile::clear_cache); + ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontFile::remove_cache); + + ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontFile::get_size_cache_list); + ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontFile::clear_size_cache); + ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontFile::remove_size_cache); + + ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontFile::set_variation_coordinates); + ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontFile::get_variation_coordinates); + + ClassDB::bind_method(D_METHOD("set_embolden", "cache_index", "strength"), &FontFile::set_embolden); + ClassDB::bind_method(D_METHOD("get_embolden", "cache_index"), &FontFile::get_embolden); + + ClassDB::bind_method(D_METHOD("set_transform", "cache_index", "transform"), &FontFile::set_transform); + ClassDB::bind_method(D_METHOD("get_transform", "cache_index"), &FontFile::get_transform); + + ClassDB::bind_method(D_METHOD("set_face_index", "cache_index", "face_index"), &FontFile::set_face_index); + ClassDB::bind_method(D_METHOD("get_face_index", "cache_index"), &FontFile::get_face_index); + + ClassDB::bind_method(D_METHOD("set_cache_ascent", "cache_index", "size", "ascent"), &FontFile::set_cache_ascent); + ClassDB::bind_method(D_METHOD("get_cache_ascent", "cache_index", "size"), &FontFile::get_cache_ascent); + + ClassDB::bind_method(D_METHOD("set_cache_descent", "cache_index", "size", "descent"), &FontFile::set_cache_descent); + ClassDB::bind_method(D_METHOD("get_cache_descent", "cache_index", "size"), &FontFile::get_cache_descent); + + ClassDB::bind_method(D_METHOD("set_cache_underline_position", "cache_index", "size", "underline_position"), &FontFile::set_cache_underline_position); + ClassDB::bind_method(D_METHOD("get_cache_underline_position", "cache_index", "size"), &FontFile::get_cache_underline_position); + + ClassDB::bind_method(D_METHOD("set_cache_underline_thickness", "cache_index", "size", "underline_thickness"), &FontFile::set_cache_underline_thickness); + ClassDB::bind_method(D_METHOD("get_cache_underline_thickness", "cache_index", "size"), &FontFile::get_cache_underline_thickness); + + ClassDB::bind_method(D_METHOD("set_cache_scale", "cache_index", "size", "scale"), &FontFile::set_cache_scale); + ClassDB::bind_method(D_METHOD("get_cache_scale", "cache_index", "size"), &FontFile::get_cache_scale); + + ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontFile::get_texture_count); + ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontFile::clear_textures); + ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontFile::remove_texture); + + ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontFile::set_texture_image); + ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontFile::get_texture_image); + + ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontFile::set_texture_offsets); + ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontFile::get_texture_offsets); + + ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontFile::get_glyph_list); + ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontFile::clear_glyphs); + ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontFile::remove_glyph); + + ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontFile::set_glyph_advance); + ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontFile::get_glyph_advance); + + ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontFile::set_glyph_offset); + ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontFile::get_glyph_offset); + + ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontFile::set_glyph_size); + ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontFile::get_glyph_size); + + ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontFile::set_glyph_uv_rect); + ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontFile::get_glyph_uv_rect); + + ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontFile::set_glyph_texture_idx); + ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontFile::get_glyph_texture_idx); + + ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontFile::get_kerning_list); + ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontFile::clear_kerning_map); + ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontFile::remove_kerning); + + ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontFile::set_kerning); + ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontFile::get_kerning); + + ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontFile::render_range); + ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontFile::render_glyph); + + ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontFile::set_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontFile::get_language_support_override); + ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontFile::remove_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontFile::get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontFile::set_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontFile::get_script_support_override); + ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontFile::remove_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontFile::get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontFile::set_opentype_feature_overrides); + ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontFile::get_opentype_feature_overrides); + + ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontFile::get_glyph_index); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic,Fixed Size", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); +} + +bool FontFile::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> tokens = p_name.operator String().split("/"); + +#ifndef DISABLE_DEPRECATED + if (tokens.size() == 1 && tokens[0] == "font_path") { + // Compatibility, DynamicFontData. + load_dynamic_font(p_value); + } else if (tokens.size() == 1 && tokens[0] == "override_oversampling") { + set_oversampling(p_value); + } + if (tokens.size() == 1 && tokens[0] == "font_data") { + // Compatibility, DynamicFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 2 && tokens[0] == "fallback") { + // Compatibility, DynamicFont. + Ref<FontFile> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 1 && tokens[0] == "textures") { + // Compatibility, BitmapFont. + set_fixed_size(16); + Array textures = p_value; + for (int i = 0; i < textures.size(); i++) { + Ref<ImageTexture> tex = textures[i]; + ERR_CONTINUE(!tex.is_valid()); + set_texture_image(0, Vector2i(16, 0), i, tex->get_image()); + } + } else if (tokens.size() == 1 && tokens[0] == "chars") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 9, false); + if (!len) { + return false; + } + int chars = len / 9; + for (int i = 0; i < chars; i++) { + const int32_t *data = &arr[i * 9]; + char32_t c = data[0]; + set_glyph_texture_idx(0, Vector2i(16, 0), c, data[1]); + set_glyph_uv_rect(0, Vector2i(16, 0), c, Rect2(data[2], data[3], data[4], data[5])); + set_glyph_offset(0, Vector2i(16, 0), c, Size2(data[6], data[7])); + set_glyph_advance(0, 16, c, Vector2(data[8], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "kernings") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 3, false); + if (!len) { + return false; + } + for (int i = 0; i < len / 3; i++) { + const int32_t *data = &arr[i * 3]; + set_kerning(0, 16, Vector2i(data[0], data[1]), Vector2(data[2], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "height") { + // Compatibility, BitmapFont. + bmp_height = p_value; + set_fixed_size(16); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "ascent") { + // Compatibility, BitmapFont. + bmp_ascent = p_value; + set_fixed_size(16); + set_cache_ascent(0, 16, bmp_ascent); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "fallback") { + // Compatibility, BitmapFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } +#endif // DISABLE_DEPRECATED + + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + set_language_support_override(lang, p_value); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + set_script_support_override(script, p_value); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + set_variation_coordinates(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + set_embolden(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + set_face_index(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + set_transform(cache_index, p_value); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + set_cache_ascent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "descent") { + set_cache_descent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_position") { + set_cache_underline_position(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_thickness") { + set_cache_underline_thickness(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "scale") { + set_cache_scale(cache_index, sz.x, p_value); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + set_texture_image(cache_index, sz, texture_index, p_value); + return true; + } else if (tokens[6] == "offsets") { + set_texture_offsets(cache_index, sz, texture_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + set_glyph_advance(cache_index, sz.x, glyph_index, p_value); + return true; + } else if (tokens[6] == "offset") { + set_glyph_offset(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "size") { + set_glyph_size(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "uv_rect") { + set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "texture_idx") { + set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + set_kerning(cache_index, sz.x, gp, p_value); + return true; + } + } + } + return false; +} + +bool FontFile::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + r_ret = get_language_support_override(lang); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + r_ret = get_script_support_override(script); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + r_ret = get_variation_coordinates(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + r_ret = get_embolden(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + r_ret = get_face_index(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + r_ret = get_transform(cache_index); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + r_ret = get_cache_ascent(cache_index, sz.x); + return true; + } else if (tokens[4] == "descent") { + r_ret = get_cache_descent(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_position") { + r_ret = get_cache_underline_position(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_thickness") { + r_ret = get_cache_underline_thickness(cache_index, sz.x); + return true; + } else if (tokens[4] == "scale") { + r_ret = get_cache_scale(cache_index, sz.x); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + r_ret = get_texture_image(cache_index, sz, texture_index); + return true; + } else if (tokens[6] == "offsets") { + r_ret = get_texture_offsets(cache_index, sz, texture_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); + return true; + } else if (tokens[6] == "offset") { + r_ret = get_glyph_offset(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "size") { + r_ret = get_glyph_size(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "uv_rect") { + r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "texture_idx") { + r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + r_ret = get_kerning(cache_index, sz.x, gp); + return true; + } + } + } + return false; +} + +void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { + Vector<String> lang_over = get_language_support_overrides(); + for (int i = 0; i < lang_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + Vector<String> scr_over = get_script_support_overrides(); + for (int i = 0; i < scr_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + for (int i = 0; i < cache.size(); i++) { + String prefix = "cache/" + itos(i) + "/"; + Array sizes = get_size_cache_list(i); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + + for (int j = 0; j < sizes.size(); j++) { + Vector2i sz = sizes[j]; + String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + + int tx_cnt = get_texture_count(i, sz); + for (int k = 0; k < tx_cnt; k++) { + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); + } + Array glyphs = get_glyph_list(i, sz); + for (int k = 0; k < glyphs.size(); k++) { + const int32_t &gl = glyphs[k]; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + if (sz.y == 0) { + Array kerning_map = get_kerning_list(i, sz.x); + for (int k = 0; k < kerning_map.size(); k++) { + const Vector2i &gl_pair = kerning_map[k]; + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + } + } + } +} + +void FontFile::reset_state() { + _clear_cache(); + data.clear(); + data_ptr = nullptr; + data_size = 0; + cache.clear(); + + antialiased = true; + mipmaps = false; + msdf = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + msdf_pixel_range = 14; + msdf_size = 128; + fixed_size = 0; + oversampling = 0.f; + + Font::reset_state(); +} + /*************************************************************************/ -Error FontData::load_bitmap_font(const String &p_path) { +Error FontFile::load_bitmap_font(const String &p_path) { reset_state(); antialiased = false; + mipmaps = false; msdf = false; force_autohinter = false; hinting = TextServer::HINTING_NONE; oversampling = 1.0f; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - if (f == nullptr) { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + p_path + "\"."); - } + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, vformat(RTR("Cannot open font from file: %s."), p_path)); int base_size = 16; int height = 0; int ascent = 0; int outline = 0; - uint32_t st_flags = 0; + BitField<TextServer::FontStyle> st_flags = 0; String font_name; bool packed = false; @@ -750,7 +1364,7 @@ Error FontData::load_bitmap_font(const String &p_path) { f->get_buffer((unsigned char *)&magic, 4); if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') { // Binary BMFont file. - ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3])); + ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(RTR("Version %d of BMFont is not supported."), (int)magic[3])); uint8_t block_type = f->get_8(); uint32_t block_size = f->get_32(); @@ -758,15 +1372,15 @@ Error FontData::load_bitmap_font(const String &p_path) { uint64_t off = f->get_position(); switch (block_type) { case 1: /* info */ { - ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size.")); + ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size.")); base_size = f->get_16(); uint8_t flags = f->get_8(); - ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported.")); + ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported.")); if (flags & (1 << 3)) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } if (flags & (1 << 2)) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } f->get_8(); // non-unicode charset, skip f->get_16(); // stretch_h, skip @@ -782,7 +1396,7 @@ Error FontData::load_bitmap_font(const String &p_path) { set_fixed_size(base_size); } break; case 2: /* common */ { - ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size.")); + ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, RTR("Invalid BMFont common block size.")); height = f->get_16(); ascent = f->get_16(); f->get_32(); // scale, skip @@ -817,40 +1431,40 @@ Error FontData::load_bitmap_font(const String &p_path) { Ref<Image> img; img.instantiate(); Error err = ImageLoader::load_image(file, img); - ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\"."); + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file)); if (packed) { if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_packed_8bit(img, page, base_size); } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_packed_4bit(img, page, base_size); } else { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } else { if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); set_texture_image(0, Vector2i(base_size, 0), page, img); } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_rgba_4bit(img, page, base_size); } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_8bit(img, page, first_gl_ch, base_size, 0); _convert_mono_8bit(img, page, first_ol_ch, base_size, 1); } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_4bit(img, page, first_cm_ch, base_size, 1); } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_8bit(img, page, first_gl_ch, base_size, 0); } else { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } } @@ -887,7 +1501,7 @@ Error FontData::load_bitmap_font(const String &p_path) { int texture_idx = f->get_8(); uint8_t channel = f->get_8(); - ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel.")); + ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel.")); int ch_off = 0; switch (channel) { case 1: @@ -929,7 +1543,7 @@ Error FontData::load_bitmap_font(const String &p_path) { } } break; default: { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type.")); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Invalid BMFont block type.")); } break; } f->seek(off + block_size); @@ -946,7 +1560,7 @@ Error FontData::load_bitmap_font(const String &p_path) { int delimiter = line.find(" "); String type = line.substr(0, delimiter); int pos = delimiter + 1; - Map<String, String> keys; + HashMap<String, String> keys; while (pos < line.size() && line[pos] == ' ') { pos++; @@ -993,18 +1607,18 @@ Error FontData::load_bitmap_font(const String &p_path) { } if (keys.has("bold")) { if (keys["bold"].to_int()) { - st_flags |= TextServer::FONT_BOLD; + st_flags.set_flag(TextServer::FONT_BOLD); } } if (keys.has("italic")) { if (keys["italic"].to_int()) { - st_flags |= TextServer::FONT_ITALIC; + st_flags.set_flag(TextServer::FONT_ITALIC); } } if (keys.has("face")) { font_name = keys["face"]; } - ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported.")); + ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported.")); } else if (type == "common") { if (keys.has("lineHeight")) { height = keys["lineHeight"].to_int(); @@ -1050,39 +1664,39 @@ Error FontData::load_bitmap_font(const String &p_path) { Ref<Image> img; img.instantiate(); Error err = ImageLoader::load_image(file, img); - ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\"."); + ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file)); if (packed) { if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_packed_8bit(img, page, base_size); } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_packed_4bit(img, page, base_size); } else { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } else { if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); set_texture_image(0, Vector2i(base_size, 0), page, img); } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_rgba_4bit(img, page, base_size); } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_8bit(img, page, first_gl_ch, base_size, 0); _convert_mono_8bit(img, page, first_ol_ch, base_size, 1); } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_4bit(img, page, first_cm_ch, base_size, 1); } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline outline = 0; - ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); _convert_mono_8bit(img, page, first_gl_ch, base_size, 0); } else { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format.")); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } } @@ -1132,7 +1746,7 @@ Error FontData::load_bitmap_font(const String &p_path) { channel = keys["chnl"].to_int(); } - ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel.")); + ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel.")); int ch_off = 0; switch (channel) { case 1: @@ -1183,13 +1797,13 @@ Error FontData::load_bitmap_font(const String &p_path) { set_font_name(font_name); set_font_style(st_flags); - set_ascent(0, base_size, ascent); - set_descent(0, base_size, height - ascent); + set_cache_ascent(0, base_size, ascent); + set_cache_descent(0, base_size, height - ascent); return OK; } -Error FontData::load_dynamic_font(const String &p_path) { +Error FontFile::load_dynamic_font(const String &p_path) { reset_state(); Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); @@ -1198,7 +1812,7 @@ Error FontData::load_dynamic_font(const String &p_path) { return OK; } -void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { +void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { data.clear(); data_ptr = p_data; data_size = p_size; @@ -1212,7 +1826,7 @@ void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { } } -void FontData::set_data(const PackedByteArray &p_data) { +void FontFile::set_data(const PackedByteArray &p_data) { data = p_data; data_ptr = data.ptr(); data_size = data.size(); @@ -1226,7 +1840,7 @@ void FontData::set_data(const PackedByteArray &p_data) { } } -PackedByteArray FontData::get_data() const { +PackedByteArray FontFile::get_data() const { if (unlikely((size_t)data.size() != data_size)) { PackedByteArray *data_w = const_cast<PackedByteArray *>(&data); data_w->resize(data_size); @@ -1235,37 +1849,22 @@ PackedByteArray FontData::get_data() const { return data; } -void FontData::set_font_name(const String &p_name) { +void FontFile::set_font_name(const String &p_name) { _ensure_rid(0); TS->font_set_name(cache[0], p_name); } -String FontData::get_font_name() const { - _ensure_rid(0); - return TS->font_get_name(cache[0]); -} - -void FontData::set_font_style_name(const String &p_name) { +void FontFile::set_font_style_name(const String &p_name) { _ensure_rid(0); TS->font_set_style_name(cache[0], p_name); } -String FontData::get_font_style_name() const { - _ensure_rid(0); - return TS->font_get_style_name(cache[0]); -} - -void FontData::set_font_style(uint32_t p_style) { +void FontFile::set_font_style(BitField<TextServer::FontStyle> p_style) { _ensure_rid(0); TS->font_set_style(cache[0], p_style); } -uint32_t FontData::get_font_style() const { - _ensure_rid(0); - return TS->font_get_style(cache[0]); -} - -void FontData::set_antialiased(bool p_antialiased) { +void FontFile::set_antialiased(bool p_antialiased) { if (antialiased != p_antialiased) { antialiased = p_antialiased; for (int i = 0; i < cache.size(); i++) { @@ -1276,11 +1875,26 @@ void FontData::set_antialiased(bool p_antialiased) { } } -bool FontData::is_antialiased() const { +bool FontFile::is_antialiased() const { return antialiased; } -void FontData::set_multichannel_signed_distance_field(bool p_msdf) { +void FontFile::set_generate_mipmaps(bool p_generate_mipmaps) { + if (mipmaps != p_generate_mipmaps) { + mipmaps = p_generate_mipmaps; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_generate_mipmaps(cache[i], mipmaps); + } + emit_changed(); + } +} + +bool FontFile::get_generate_mipmaps() const { + return mipmaps; +} + +void FontFile::set_multichannel_signed_distance_field(bool p_msdf) { if (msdf != p_msdf) { msdf = p_msdf; for (int i = 0; i < cache.size(); i++) { @@ -1291,11 +1905,11 @@ void FontData::set_multichannel_signed_distance_field(bool p_msdf) { } } -bool FontData::is_multichannel_signed_distance_field() const { +bool FontFile::is_multichannel_signed_distance_field() const { return msdf; } -void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { +void FontFile::set_msdf_pixel_range(int p_msdf_pixel_range) { if (msdf_pixel_range != p_msdf_pixel_range) { msdf_pixel_range = p_msdf_pixel_range; for (int i = 0; i < cache.size(); i++) { @@ -1306,11 +1920,11 @@ void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { } } -int FontData::get_msdf_pixel_range() const { +int FontFile::get_msdf_pixel_range() const { return msdf_pixel_range; } -void FontData::set_msdf_size(int p_msdf_size) { +void FontFile::set_msdf_size(int p_msdf_size) { if (msdf_size != p_msdf_size) { msdf_size = p_msdf_size; for (int i = 0; i < cache.size(); i++) { @@ -1321,11 +1935,11 @@ void FontData::set_msdf_size(int p_msdf_size) { } } -int FontData::get_msdf_size() const { +int FontFile::get_msdf_size() const { return msdf_size; } -void FontData::set_fixed_size(int p_fixed_size) { +void FontFile::set_fixed_size(int p_fixed_size) { if (fixed_size != p_fixed_size) { fixed_size = p_fixed_size; for (int i = 0; i < cache.size(); i++) { @@ -1336,11 +1950,11 @@ void FontData::set_fixed_size(int p_fixed_size) { } } -int FontData::get_fixed_size() const { +int FontFile::get_fixed_size() const { return fixed_size; } -void FontData::set_force_autohinter(bool p_force_autohinter) { +void FontFile::set_force_autohinter(bool p_force_autohinter) { if (force_autohinter != p_force_autohinter) { force_autohinter = p_force_autohinter; for (int i = 0; i < cache.size(); i++) { @@ -1351,11 +1965,11 @@ void FontData::set_force_autohinter(bool p_force_autohinter) { } } -bool FontData::is_force_autohinter() const { +bool FontFile::is_force_autohinter() const { return force_autohinter; } -void FontData::set_hinting(TextServer::Hinting p_hinting) { +void FontFile::set_hinting(TextServer::Hinting p_hinting) { if (hinting != p_hinting) { hinting = p_hinting; for (int i = 0; i < cache.size(); i++) { @@ -1366,11 +1980,11 @@ void FontData::set_hinting(TextServer::Hinting p_hinting) { } } -TextServer::Hinting FontData::get_hinting() const { +TextServer::Hinting FontFile::get_hinting() const { return hinting; } -void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { +void FontFile::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { if (subpixel_positioning != p_subpixel) { subpixel_positioning = p_subpixel; for (int i = 0; i < cache.size(); i++) { @@ -1381,11 +1995,11 @@ void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpix } } -TextServer::SubpixelPositioning FontData::get_subpixel_positioning() const { +TextServer::SubpixelPositioning FontFile::get_subpixel_positioning() const { return subpixel_positioning; } -void FontData::set_oversampling(real_t p_oversampling) { +void FontFile::set_oversampling(real_t p_oversampling) { if (oversampling != p_oversampling) { oversampling = p_oversampling; for (int i = 0; i < cache.size(); i++) { @@ -1396,17 +2010,20 @@ void FontData::set_oversampling(real_t p_oversampling) { } } -real_t FontData::get_oversampling() const { +real_t FontFile::get_oversampling() const { return oversampling; } -RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { +RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { // Find existing variation cache. const Dictionary &supported_coords = get_supported_variation_list(); for (int i = 0; i < cache.size(); i++) { if (cache[i].is_valid()) { const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]); bool match = true; + match = match && (TS->font_get_face_index(cache[i]) == p_face_index); + match = match && (TS->font_get_embolden(cache[i]) == p_strength); + match = match && (TS->font_get_transform(cache[i]) == p_transform); for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) { const Vector3 &def = supported_coords[*V]; @@ -1442,967 +2059,1008 @@ RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { int idx = cache.size(); _ensure_rid(idx); TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates); + TS->font_set_face_index(cache[idx], p_face_index); + TS->font_set_embolden(cache[idx], p_strength); + TS->font_set_transform(cache[idx], p_transform); return cache[idx]; } -int FontData::get_cache_count() const { +RID FontFile::_get_rid() const { + _ensure_rid(0); + return cache[0]; +} + +int FontFile::get_cache_count() const { return cache.size(); } -void FontData::clear_cache() { +void FontFile::clear_cache() { _clear_cache(); cache.clear(); + emit_changed(); } -void FontData::remove_cache(int p_cache_index) { +void FontFile::remove_cache(int p_cache_index) { ERR_FAIL_INDEX(p_cache_index, cache.size()); if (cache[p_cache_index].is_valid()) { - TS->free(cache.write[p_cache_index]); + TS->free_rid(cache.write[p_cache_index]); } cache.remove_at(p_cache_index); emit_changed(); } -Array FontData::get_size_cache_list(int p_cache_index) const { +Array FontFile::get_size_cache_list(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_size_cache_list(cache[p_cache_index]); } -void FontData::clear_size_cache(int p_cache_index) { +void FontFile::clear_size_cache(int p_cache_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_size_cache(cache[p_cache_index]); } -void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) { +void FontFile::remove_size_cache(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_size_cache(cache[p_cache_index], p_size); } -void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { +void FontFile::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates); - emit_changed(); } -Dictionary FontData::get_variation_coordinates(int p_cache_index) const { +Dictionary FontFile::get_variation_coordinates(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Dictionary()); _ensure_rid(p_cache_index); return TS->font_get_variation_coordinates(cache[p_cache_index]); } -void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) { +void FontFile::set_embolden(int p_cache_index, float p_strength) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_embolden(cache[p_cache_index], p_strength); +} + +float FontFile::get_embolden(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_embolden(cache[p_cache_index]); +} + +void FontFile::set_transform(int p_cache_index, Transform2D p_transform) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_transform(cache[p_cache_index], p_transform); +} + +Transform2D FontFile::get_transform(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Transform2D()); + _ensure_rid(p_cache_index); + return TS->font_get_transform(cache[p_cache_index]); +} + +void FontFile::set_face_index(int p_cache_index, int64_t p_index) { + ERR_FAIL_COND(p_cache_index < 0); + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= 0x7FFF); + + _ensure_rid(p_cache_index); + TS->font_set_face_index(cache[p_cache_index], p_index); +} + +int64_t FontFile::get_face_index(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_face_index(cache[p_cache_index]); +} + +void FontFile::set_cache_ascent(int p_cache_index, int p_size, real_t p_ascent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent); } -real_t FontData::get_ascent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_ascent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_ascent(cache[p_cache_index], p_size); } -void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) { +void FontFile::set_cache_descent(int p_cache_index, int p_size, real_t p_descent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_descent(cache[p_cache_index], p_size, p_descent); } -real_t FontData::get_descent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_descent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_descent(cache[p_cache_index], p_size); } -void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { +void FontFile::set_cache_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position); } -real_t FontData::get_underline_position(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_position(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_position(cache[p_cache_index], p_size); } -void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { +void FontFile::set_cache_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness); } -real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_thickness(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_thickness(cache[p_cache_index], p_size); } -void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) { +void FontFile::set_cache_scale(int p_cache_index, int p_size, real_t p_scale) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_scale(cache[p_cache_index], p_size, p_scale); } -real_t FontData::get_scale(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_scale(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_scale(cache[p_cache_index], p_size); } -void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) { - ERR_FAIL_COND(p_cache_index < 0); - _ensure_rid(p_cache_index); - TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value); -} - -int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const { - ERR_FAIL_COND_V(p_cache_index < 0, 0); - _ensure_rid(p_cache_index); - return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing); -} - -int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const { +int FontFile::get_texture_count(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_texture_count(cache[p_cache_index], p_size); } -void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_textures(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_textures(cache[p_cache_index], p_size); } -void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { +void FontFile::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { +void FontFile::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image); } -Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +Ref<Image> FontFile::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>()); _ensure_rid(p_cache_index); return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { +void FontFile::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset); } -PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +PackedInt32Array FontFile::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); _ensure_rid(p_cache_index); return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); } -Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { +Array FontFile::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_glyph_list(cache[p_cache_index], p_size); } -void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_glyphs(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_glyphs(cache[p_cache_index], p_size); } -void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { +void FontFile::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { +void FontFile::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance); } -Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { +void FontFile::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset); } -Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { +void FontFile::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size); } -Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { +void FontFile::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect); } -Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Rect2 FontFile::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Rect2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { +void FontFile::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx); } -int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +int FontFile::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); } -Array FontData::get_kerning_list(int p_cache_index, int p_size) const { +Array FontFile::get_kerning_list(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_kerning_list(cache[p_cache_index], p_size); } -void FontData::clear_kerning_map(int p_cache_index, int p_size) { +void FontFile::clear_kerning_map(int p_cache_index, int p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_kerning_map(cache[p_cache_index], p_size); } -void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { +void FontFile::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { +void FontFile::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning); } -Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { +Vector2 FontFile::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { +void FontFile::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end); } -void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { +void FontFile::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_glyph(cache[p_cache_index], p_size, p_index); } -RID FontData::get_cache_rid(int p_cache_index) const { - ERR_FAIL_COND_V(p_cache_index < 0, RID()); - _ensure_rid(p_cache_index); - return cache[p_cache_index]; -} - -bool FontData::is_language_supported(const String &p_language) const { - _ensure_rid(0); - return TS->font_is_language_supported(cache[0], p_language); -} - -void FontData::set_language_support_override(const String &p_language, bool p_supported) { +void FontFile::set_language_support_override(const String &p_language, bool p_supported) { _ensure_rid(0); TS->font_set_language_support_override(cache[0], p_language, p_supported); } -bool FontData::get_language_support_override(const String &p_language) const { +bool FontFile::get_language_support_override(const String &p_language) const { _ensure_rid(0); return TS->font_get_language_support_override(cache[0], p_language); } -void FontData::remove_language_support_override(const String &p_language) { +void FontFile::remove_language_support_override(const String &p_language) { _ensure_rid(0); TS->font_remove_language_support_override(cache[0], p_language); } -Vector<String> FontData::get_language_support_overrides() const { +Vector<String> FontFile::get_language_support_overrides() const { _ensure_rid(0); return TS->font_get_language_support_overrides(cache[0]); } -bool FontData::is_script_supported(const String &p_script) const { - _ensure_rid(0); - return TS->font_is_script_supported(cache[0], p_script); -} - -void FontData::set_script_support_override(const String &p_script, bool p_supported) { +void FontFile::set_script_support_override(const String &p_script, bool p_supported) { _ensure_rid(0); TS->font_set_script_support_override(cache[0], p_script, p_supported); } -bool FontData::get_script_support_override(const String &p_script) const { +bool FontFile::get_script_support_override(const String &p_script) const { _ensure_rid(0); return TS->font_get_script_support_override(cache[0], p_script); } -void FontData::remove_script_support_override(const String &p_script) { +void FontFile::remove_script_support_override(const String &p_script) { _ensure_rid(0); TS->font_remove_script_support_override(cache[0], p_script); } -Vector<String> FontData::get_script_support_overrides() const { +Vector<String> FontFile::get_script_support_overrides() const { _ensure_rid(0); return TS->font_get_script_support_overrides(cache[0]); } -void FontData::set_opentype_feature_overrides(const Dictionary &p_overrides) { +void FontFile::set_opentype_feature_overrides(const Dictionary &p_overrides) { _ensure_rid(0); TS->font_set_opentype_feature_overrides(cache[0], p_overrides); } -Dictionary FontData::get_opentype_feature_overrides() const { +Dictionary FontFile::get_opentype_feature_overrides() const { _ensure_rid(0); return TS->font_get_opentype_feature_overrides(cache[0]); } -bool FontData::has_char(char32_t p_char) const { - _ensure_rid(0); - return TS->font_has_char(cache[0], p_char); -} - -String FontData::get_supported_chars() const { - _ensure_rid(0); - return TS->font_get_supported_chars(cache[0]); -} - -int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { +int32_t FontFile::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { _ensure_rid(0); return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector); } -Dictionary FontData::get_supported_feature_list() const { - _ensure_rid(0); - return TS->font_supported_feature_list(cache[0]); -} - -Dictionary FontData::get_supported_variation_list() const { - _ensure_rid(0); - return TS->font_supported_variation_list(cache[0]); -} - -FontData::FontData() { +FontFile::FontFile() { /* NOP */ } -FontData::~FontData() { - _clear_cache(); +FontFile::~FontFile() { + reset_state(); } /*************************************************************************/ +/* FontVariation */ +/*************************************************************************/ -void Font::_data_changed() { - for (int i = 0; i < rids.size(); i++) { - rids.write[i] = RID(); - } - emit_changed(); -} +void FontVariation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_base_font", "font"), &FontVariation::set_base_font); + ClassDB::bind_method(D_METHOD("get_base_font"), &FontVariation::get_base_font); -void Font::_ensure_rid(int p_index) const { - // Find or create cache record. - if (!rids[p_index].is_valid() && data[p_index].is_valid()) { - rids.write[p_index] = data[p_index]->find_cache(variation_coordinates); - } -} + ClassDB::bind_method(D_METHOD("set_variation_opentype", "coords"), &FontVariation::set_variation_opentype); + ClassDB::bind_method(D_METHOD("get_variation_opentype"), &FontVariation::get_variation_opentype); -void Font::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data); - ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data); - ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count); - ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data); - ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid); - ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data); - ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); - - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates"); - - ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("set_variation_embolden", "strength"), &FontVariation::set_variation_embolden); + ClassDB::bind_method(D_METHOD("get_variation_embolden"), &FontVariation::get_variation_embolden); - ADD_GROUP("Extra Spacing", "spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); + ClassDB::bind_method(D_METHOD("set_variation_face_index", "face_index"), &FontVariation::set_variation_face_index); + ClassDB::bind_method(D_METHOD("get_variation_face_index"), &FontVariation::get_variation_face_index); - ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("set_variation_transform", "transform"), &FontVariation::set_variation_transform); + ClassDB::bind_method(D_METHOD("get_variation_transform"), &FontVariation::get_variation_transform); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "alignment", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_opentype_features", "features"), &FontVariation::set_opentype_features); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); - ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + ADD_GROUP("Variation", "variation"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "variation_face_index"), "set_variation_face_index", "get_variation_face_index"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "variation_embolden", PROPERTY_HINT_RANGE, "-2,2,0.01"), "set_variation_embolden", "get_variation_embolden"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "variation_transform", PROPERTY_HINT_NONE, "suffix:px"), "set_variation_transform", "get_variation_transform"); - ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); + ADD_GROUP("OpenType Features", "opentype"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_features"), "set_opentype_features", "get_opentype_features"); + + ADD_GROUP("Extra Spacing", "spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_glyph", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_GLYPH); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_space", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_SPACE); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); } -bool Font::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); -#ifndef DISABLE_DEPRECATED - if (tokens.size() == 1 && tokens[0] == "font_data") { - // Compatibility, DynamicFont main data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 2 && tokens[0] == "fallback") { - // Compatibility, DynamicFont fallback data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 1 && tokens[0] == "fallback") { - // Compatibility, BitmapFont fallback data. - Ref<Font> f = p_value; - if (f.is_valid()) { - for (int i = 0; i < f->get_data_count(); i++) { - add_data(f->get_data(i)); - } - return true; +void FontVariation::_update_rids() const { + Ref<Font> f = _get_base_font_or_default(); + + rids.clear(); + if (fallbacks.is_empty() && f.is_valid()) { + RID rid = _get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); } - return false; - } -#endif /* DISABLE_DEPRECATED */ - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - if (idx == data.size()) { - add_data(fd); - return true; - } else if (idx >= 0 && idx < data.size()) { - set_data(idx, fd); - return true; - } else { - return false; - } - } else if (idx >= 0 && idx < data.size()) { - remove_data(idx); - return true; + + const TypedArray<Font> &base_fallbacks = f->get_fallbacks(); + for (int i = 0; i < base_fallbacks.size(); i++) { + _update_rids_fb(base_fallbacks[i], 0); } + } else { + _update_rids_fb(const_cast<FontVariation *>(this), 0); } - return false; + dirty_rids = false; } -bool Font::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); +void FontVariation::reset_state() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); + } - if (idx == data.size()) { - r_ret = Ref<FontData>(); - return true; - } else if (idx >= 0 && idx < data.size()) { - r_ret = get_data(idx); - return true; - } + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font.unref(); } - return false; -} + variation = Variation(); + opentype_features = Dictionary(); -void Font::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < data.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; } - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + + Font::reset_state(); } -void Font::reset_state() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); +void FontVariation::set_base_font(const Ref<Font> &p_font) { + if (base_font != p_font) { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); } + base_font = p_font; + if (base_font.is_valid()) { + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + _invalidate_rids(); + notify_property_list_changed(); } - cache.clear(); - cache_wrap.clear(); - data.clear(); - rids.clear(); +} - variation_coordinates.clear(); - spacing_bottom = 0; - spacing_top = 0; +Ref<Font> FontVariation::get_base_font() const { + return base_font; } -Dictionary Font::get_feature_list() const { - Dictionary out; - for (int i = 0; i < data.size(); i++) { - Dictionary data_ftrs = data[i]->get_supported_feature_list(); - for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { - out[*ftr] = data_ftrs[*ftr]; - } +Ref<Font> FontVariation::_get_base_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids)); + theme_font.unref(); } - return out; -} -void Font::add_data(const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - data.push_back(p_data); - rids.push_back(RID()); + if (base_font.is_valid()) { + return base_font; + } + + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - if (data[data.size() - 1].is_valid()) { - data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; } } } - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); -} - -void Font::set_data(int p_idx, const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - ERR_FAIL_INDEX(p_idx, data.size()); - - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); - } + // Lastly, fall back on the items defined in the default Theme, if they exist. + if (Theme::get_default().is_valid()) { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - data.write[p_idx] = p_data; - rids.write[p_idx] = RID(); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } } - } - if (data[p_idx].is_valid()) { - data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; } - cache.clear(); - cache_wrap.clear(); + return Ref<Font>(); +} - emit_changed(); - notify_property_list_changed(); +void FontVariation::set_variation_opentype(const Dictionary &p_coords) { + if (variation.opentype != p_coords) { + variation.opentype = p_coords; + _invalidate_rids(); + } } -int Font::get_data_count() const { - return data.size(); +Dictionary FontVariation::get_variation_opentype() const { + return variation.opentype; } -Ref<FontData> Font::get_data(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>()); - return data[p_idx]; +void FontVariation::set_variation_embolden(float p_strength) { + if (variation.embolden != p_strength) { + variation.embolden = p_strength; + _invalidate_rids(); + } } -RID Font::get_data_rid(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), RID()); - _ensure_rid(p_idx); - return rids[p_idx]; +float FontVariation::get_variation_embolden() const { + return variation.embolden; } -void Font::clear_data() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } +void FontVariation::set_variation_transform(Transform2D p_transform) { + if (variation.transform != p_transform) { + variation.transform = p_transform; + _invalidate_rids(); } - data.clear(); - rids.clear(); } -void Font::remove_data(int p_idx) { - ERR_FAIL_INDEX(p_idx, data.size()); +Transform2D FontVariation::get_variation_transform() const { + return variation.transform; +} - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); +void FontVariation::set_variation_face_index(int p_face_index) { + if (variation.face_index != p_face_index) { + variation.face_index = p_face_index; + _invalidate_rids(); } - - data.remove_at(p_idx); - rids.remove_at(p_idx); - - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); } -void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) { - _data_changed(); - variation_coordinates = p_variation_coordinates; +int FontVariation::get_variation_face_index() const { + return variation.face_index; } -Dictionary Font::get_variation_coordinates() const { - return variation_coordinates; +void FontVariation::set_opentype_features(const Dictionary &p_features) { + if (opentype_features != p_features) { + opentype_features = p_features; + _invalidate_rids(); + } } -void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) { - _data_changed(); - switch (p_spacing) { - case TextServer::SPACING_TOP: { - spacing_top = p_value; - } break; - case TextServer::SPACING_BOTTOM: { - spacing_bottom = p_value; - } break; - default: { - ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); - } break; - } +Dictionary FontVariation::get_opentype_features() const { + return opentype_features; } -int Font::get_spacing(TextServer::SpacingType p_spacing) const { - switch (p_spacing) { - case TextServer::SPACING_TOP: { - return spacing_top; - } break; - case TextServer::SPACING_BOTTOM: { - return spacing_bottom; - } break; - default: { - ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); - } break; +void FontVariation::set_spacing(TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_INDEX((int)p_spacing, TextServer::SPACING_MAX); + if (extra_spacing[p_spacing] != p_value) { + extra_spacing[p_spacing] = p_value; + _invalidate_rids(); } } -real_t Font::get_height(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); - } - return ret + spacing_bottom + spacing_top; +int FontVariation::get_spacing(TextServer::SpacingType p_spacing) const { + ERR_FAIL_INDEX_V((int)p_spacing, TextServer::SPACING_MAX, 0); + return extra_spacing[p_spacing]; } -real_t Font::get_ascent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size)); +RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform); } - return ret + spacing_top; + return RID(); } -real_t Font::get_descent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_descent(rids[i], p_size)); +RID FontVariation::_get_rid() const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform); } - return ret + spacing_bottom; + return RID(); } -real_t Font::get_underline_position(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size)); +FontVariation::FontVariation() { + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; } - return ret + spacing_top; } -real_t Font::get_underline_thickness(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size)); - } - return ret; +FontVariation::~FontVariation() { + reset_state(); } -Size2 Font::get_string_size(const String &p_text, int p_size, HorizontalAlignment p_alignment, float p_width, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); +/*************************************************************************/ +/* SystemFont */ +/*************************************************************************/ - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } +void SystemFont::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &SystemFont::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &SystemFont::is_antialiased); - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - } - hash = hash_djb2_one_64(p_size, hash); + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps); - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting); + + ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling); + + ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names); + ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names); + + ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); +} + +void SystemFont::_update_rids() const { + Ref<Font> f = _get_base_font_or_default(); + + rids.clear(); + if (fallbacks.is_empty() && f.is_valid()) { + RID rid = _get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); + } + + const TypedArray<Font> &base_fallbacks = f->get_fallbacks(); + for (int i = 0; i < base_fallbacks.size(); i++) { + _update_rids_fb(base_fallbacks[i], 0); + } } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); + _update_rids_fb(const_cast<SystemFont *>(this), 0); } - return buffer->get_size(); + dirty_rids = false; } -Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); +void SystemFont::_update_base_font() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); } - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); + face_indeces.clear(); + ftr_weight = 0; + ftr_italic = 0; + for (const String &E : names) { + if (E.is_empty()) { + continue; + } - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); - } + String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC); + if (path.is_empty()) { + continue; + } + Ref<FontFile> file; + file.instantiate(); + Error err = file->load_dynamic_font(path); + if (err != OK) { + continue; + } - Size2 ret; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ret.x = MAX(ret.x, line_size.x); - ret.y += line_size.y; - } else { - ret.y = MAX(ret.y, line_size.y); - ret.x += line_size.x; + // If it's a font collection check all faces to match requested style. + for (int i = 0; i < file->get_face_count(); i++) { + file->set_face_index(0, i); + if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) { + face_indeces.push_back(i); + } } - } - return ret; -} + if (face_indeces.is_empty()) { + face_indeces.push_back(0); + } + file->set_face_index(0, face_indeces[0]); -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); + // If it's a variable font, apply weight and italic coordinates to match requested style. + Dictionary ftr = file->get_supported_variation_list(); + if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) { + ftr_weight = 700; + } + if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) { + ftr_italic = 1; + } + + // Apply font rendering settings. + file->set_antialiased(antialiased); + file->set_generate_mipmaps(mipmaps); + file->set_force_autohinter(force_autohinter); + file->set_hinting(hinting); + file->set_subpixel_positioning(subpixel_positioning); + file->set_oversampling(oversampling); - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); + base_font = file; } - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); + if (base_font.is_valid()) { + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); } - hash = hash_djb2_one_64(p_size, hash); - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); - } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); + _invalidate_rids(); + notify_property_list_changed(); +} + +void SystemFont::reset_state() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); } - Vector2 ofs = p_pos; - if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y -= buffer->get_line_ascent(); - } else { - ofs.x -= buffer->get_line_ascent(); + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font.unref(); } - buffer->set_width(p_width); - buffer->set_horizontal_alignment(p_alignment); - buffer->set_flags(p_flags); + names.clear(); + face_indeces.clear(); + ftr_weight = 0; + ftr_italic = 0; + style = 0; + antialiased = true; + mipmaps = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + oversampling = 0.f; - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate); - } - buffer->draw(p_canvas_item, ofs, p_modulate); + Font::reset_state(); } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); +Ref<Font> SystemFont::_get_base_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids)); + theme_font.unref(); + } - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); + if (base_font.is_valid()) { + return base_font; } - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } } - lines_buffer->set_alignment(p_alignment); + // Lastly, fall back on the items defined in the default Theme, if they exist. + if (Theme::get_default().is_valid()) { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - Vector2 lofs = p_pos; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - if (i == 0) { - lofs.y -= lines_buffer->get_line_ascent(0); - } - } else { - if (i == 0) { - lofs.x -= lines_buffer->get_line_ascent(0); + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; } } - if (p_width > 0) { - lines_buffer->set_alignment(p_alignment); + + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); } + return f; + } - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate); + return Ref<Font>(); +} + +void SystemFont::set_antialiased(bool p_antialiased) { + if (antialiased != p_antialiased) { + antialiased = p_antialiased; + if (base_font.is_valid()) { + base_font->set_antialiased(antialiased); } - lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate); + emit_changed(); + } +} - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - lofs.y += line_size.y; - } else { - lofs.x += line_size.x; +bool SystemFont::is_antialiased() const { + return antialiased; +} + +void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) { + if (mipmaps != p_generate_mipmaps) { + mipmaps = p_generate_mipmaps; + if (base_font.is_valid()) { + base_font->set_generate_mipmaps(mipmaps); } + emit_changed(); + } +} + +bool SystemFont::get_generate_mipmaps() const { + return mipmaps; +} - if ((p_max_lines > 0) && (i >= p_max_lines)) { - return; +void SystemFont::set_force_autohinter(bool p_force_autohinter) { + if (force_autohinter != p_force_autohinter) { + force_autohinter = p_force_autohinter; + if (base_font.is_valid()) { + base_font->set_force_autohinter(force_autohinter); } + emit_changed(); } } -Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } - return ret; +bool SystemFont::is_force_autohinter() const { + return force_autohinter; +} + +void SystemFont::set_hinting(TextServer::Hinting p_hinting) { + if (hinting != p_hinting) { + hinting = p_hinting; + if (base_font.is_valid()) { + base_font->set_hinting(hinting); } + emit_changed(); } - return Size2(); } -real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x; - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } +TextServer::Hinting SystemFont::get_hinting() const { + return hinting; +} - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate); - } - TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate); - return ret; +void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { + if (subpixel_positioning != p_subpixel) { + subpixel_positioning = p_subpixel; + if (base_font.is_valid()) { + base_font->set_subpixel_positioning(subpixel_positioning); } + emit_changed(); } - return 0; } -bool Font::has_char(char32_t p_char) const { - for (int i = 0; i < data.size(); i++) { - if (data[i]->has_char(p_char)) { - return true; +TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const { + return subpixel_positioning; +} + +void SystemFont::set_oversampling(real_t p_oversampling) { + if (oversampling != p_oversampling) { + oversampling = p_oversampling; + if (base_font.is_valid()) { + base_font->set_oversampling(oversampling); } + emit_changed(); } - return false; } -String Font::get_supported_chars() const { - String chars; - for (int i = 0; i < data.size(); i++) { - String data_chars = data[i]->get_supported_chars(); - for (int j = 0; j < data_chars.length(); j++) { - if (chars.find_char(data_chars[j]) == -1) { - chars += data_chars[j]; - } +real_t SystemFont::get_oversampling() const { + return oversampling; +} + +void SystemFont::set_font_names(const PackedStringArray &p_names) { + if (names != p_names) { + names = p_names; + _update_base_font(); + } +} + +PackedStringArray SystemFont::get_font_names() const { + return names; +} + +void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) { + if (style != p_style) { + style = p_style; + _update_base_font(); + } +} + +BitField<TextServer::FontStyle> SystemFont::get_font_style() const { + return style; +} + +int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const { + if (base_font.is_valid()) { + return base_font->get_spacing(p_spacing); + } else { + return 0; + } +} + +RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + Dictionary var = p_variation_coordinates; + if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) { + var[TS->name_to_tag("weight")] = ftr_weight; + } + if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) { + var[TS->name_to_tag("italic")] = ftr_italic; + } + + if (!face_indeces.is_empty()) { + int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1); + return f->find_variation(var, face_indeces[face_index], p_strength, p_transform); + } else { + return f->find_variation(var, 0, p_strength, p_transform); } } - return chars; + return RID(); } -Vector<RID> Font::get_rids() const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); +RID SystemFont::_get_rid() const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + if (!face_indeces.is_empty()) { + Dictionary var; + if (ftr_weight > 0) { + var[TS->name_to_tag("weight")] = ftr_weight; + } + if (ftr_italic > 0) { + var[TS->name_to_tag("italic")] = ftr_italic; + } + return f->find_variation(var, face_indeces[0]); + } else { + return f->_get_rid(); + } } - return rids; + return RID(); } -void Font::update_changes() { - emit_changed(); +int64_t SystemFont::get_face_count() const { + return face_indeces.size(); } -Font::Font() { - cache.set_capacity(128); - cache_wrap.set_capacity(32); +SystemFont::SystemFont() { + /* NOP */ } -Font::~Font() { - clear_data(); - cache.clear(); - cache_wrap.clear(); +SystemFont::~SystemFont() { + reset_state(); } diff --git a/scene/resources/font.h b/scene/resources/font.h index aaf0a7fe7b..260b4e521f 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -33,14 +33,107 @@ #include "core/io/resource.h" #include "core/templates/lru.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "scene/resources/texture.h" #include "servers/text_server.h" +class TextLine; +class TextParagraph; + +/*************************************************************************/ +/* Font */ +/*************************************************************************/ + +class Font : public Resource { + GDCLASS(Font, Resource); + + // Shaped string cache. + mutable LRUCache<uint64_t, Ref<TextLine>> cache; + mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; + +protected: + // Output. + mutable TypedArray<RID> rids; + mutable bool dirty_rids = true; + + // Fallbacks. + static constexpr int MAX_FALLBACK_DEPTH = 64; + TypedArray<Font> fallbacks; + + static void _bind_methods(); + + virtual void _update_rids_fb(const Ref<Font> &p_f, int p_depth) const; + virtual void _update_rids() const; + virtual bool _is_cyclic(const Ref<Font> &p_f, int p_depth) const; + + virtual void reset_state() override; + +public: + virtual void _invalidate_rids(); + + static constexpr int DEFAULT_FONT_SIZE = 16; + + // Fallbacks. + virtual void set_fallbacks(const TypedArray<Font> &p_fallbacks); + virtual TypedArray<Font> get_fallbacks() const; + + // Output. + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const { return RID(); }; + virtual RID _get_rid() const { return RID(); }; + virtual TypedArray<RID> get_rids() const; + + // Font metrics. + virtual real_t get_height(int p_font_size) const; + virtual real_t get_ascent(int p_font_size) const; + virtual real_t get_descent(int p_font_size) const; + virtual real_t get_underline_position(int p_font_size) const; + virtual real_t get_underline_thickness(int p_font_size) const; + + virtual String get_font_name() const; + virtual String get_font_style_name() const; + virtual BitField<TextServer::FontStyle> get_font_style() const; + + virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }; + virtual Dictionary get_opentype_features() const; + + // Drawing string. + virtual void set_cache_capacity(int p_single_line, int p_multi_line); + + virtual Size2 get_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual Size2 get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + virtual void draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + // Drawing char. + virtual Size2 get_char_size(char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE) const; + virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; + virtual real_t draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; + + // Helper functions. + virtual bool has_char(char32_t p_char) const; + virtual String get_supported_chars() const; + + virtual bool is_language_supported(const String &p_language) const; + virtual bool is_script_supported(const String &p_script) const; + + virtual Dictionary get_supported_feature_list() const; + virtual Dictionary get_supported_variation_list() const; + virtual int64_t get_face_count() const; + + Font(); + ~Font(); +}; + +/*************************************************************************/ +/* FontFile */ /*************************************************************************/ -class FontData : public Resource { - GDCLASS(FontData, Resource); +class FontFile : public Font { + GDCLASS(FontFile, Font); RES_BASE_EXTENSION("fontdata"); // Font source data. @@ -49,6 +142,7 @@ class FontData : public Resource { PackedByteArray data; bool antialiased = true; + bool mipmaps = false; bool msdf = false; int msdf_pixel_range = 16; int msdf_size = 48; @@ -58,6 +152,11 @@ class FontData : public Resource { TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; +#ifndef DISABLE_DEPRECATED + real_t bmp_height = 0.0; + real_t bmp_ascent = 0.0; +#endif + // Cache. mutable Vector<RID> cache; @@ -90,17 +189,15 @@ public: // Common properties. virtual void set_font_name(const String &p_name); - virtual String get_font_name() const; - virtual void set_font_style_name(const String &p_name); - virtual String get_font_style_name() const; - - virtual void set_font_style(uint32_t p_style); - virtual uint32_t get_font_style() const; + virtual void set_font_style(BitField<TextServer::FontStyle> p_style); virtual void set_antialiased(bool p_antialiased); virtual bool is_antialiased() const; + virtual void set_generate_mipmaps(bool p_generate_mipmaps); + virtual bool get_generate_mipmaps() const; + virtual void set_multichannel_signed_distance_field(bool p_msdf); virtual bool is_multichannel_signed_distance_field() const; @@ -126,7 +223,8 @@ public: virtual real_t get_oversampling() const; // Cache. - virtual RID find_cache(const Dictionary &p_variation_coordinates) const; + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; virtual int get_cache_count() const; virtual void clear_cache(); @@ -139,23 +237,29 @@ public: virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates); virtual Dictionary get_variation_coordinates(int p_cache_index) const; - virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent); - virtual real_t get_ascent(int p_cache_index, int p_size) const; + virtual void set_embolden(int p_cache_index, float p_strength); + virtual float get_embolden(int p_cache_index) const; + + virtual void set_transform(int p_cache_index, Transform2D p_transform); + virtual Transform2D get_transform(int p_cache_index) const; + + virtual void set_face_index(int p_cache_index, int64_t p_index); + virtual int64_t get_face_index(int p_cache_index) const; - virtual void set_descent(int p_cache_index, int p_size, real_t p_descent); - virtual real_t get_descent(int p_cache_index, int p_size) const; + virtual void set_cache_ascent(int p_cache_index, int p_size, real_t p_ascent); + virtual real_t get_cache_ascent(int p_cache_index, int p_size) const; - virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position); - virtual real_t get_underline_position(int p_cache_index, int p_size) const; + virtual void set_cache_descent(int p_cache_index, int p_size, real_t p_descent); + virtual real_t get_cache_descent(int p_cache_index, int p_size) const; - virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness); - virtual real_t get_underline_thickness(int p_cache_index, int p_size) const; + virtual void set_cache_underline_position(int p_cache_index, int p_size, real_t p_underline_position); + virtual real_t get_cache_underline_position(int p_cache_index, int p_size) const; - virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts). - virtual real_t get_scale(int p_cache_index, int p_size) const; + virtual void set_cache_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness); + virtual real_t get_cache_underline_thickness(int p_cache_index, int p_size) const; - virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value); - virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const; + virtual void set_cache_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts). + virtual real_t get_cache_scale(int p_cache_index, int p_size) const; virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const; virtual void clear_textures(int p_cache_index, const Vector2i &p_size); @@ -196,16 +300,12 @@ public: virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end); virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index); - virtual RID get_cache_rid(int p_cache_index) const; - // Language/script support override. - virtual bool is_language_supported(const String &p_language) const; virtual void set_language_support_override(const String &p_language, bool p_supported); virtual bool get_language_support_override(const String &p_language) const; virtual void remove_language_support_override(const String &p_language); virtual Vector<String> get_language_support_overrides() const; - virtual bool is_script_supported(const String &p_script) const; virtual void set_script_support_override(const String &p_script, bool p_supported); virtual bool get_script_support_override(const String &p_script) const; virtual void remove_script_support_override(const String &p_script); @@ -215,100 +315,140 @@ public: virtual Dictionary get_opentype_feature_overrides() const; // Base font properties. - virtual bool has_char(char32_t p_char) const; - virtual String get_supported_chars() const; - virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const; - virtual Dictionary get_supported_feature_list() const; - virtual Dictionary get_supported_variation_list() const; - - FontData(); - ~FontData(); + FontFile(); + ~FontFile(); }; /*************************************************************************/ +/* FontVariation */ +/*************************************************************************/ -class TextLine; -class TextParagraph; +class FontVariation : public Font { + GDCLASS(FontVariation, Font); -class Font : public Resource { - GDCLASS(Font, Resource); - - // Shaped string cache. - mutable LRUCache<uint64_t, Ref<TextLine>> cache; - mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; + struct Variation { + Dictionary opentype; + real_t embolden = 0.f; + int face_index = 0; + Transform2D transform; + }; - // Font data cache. - Vector<Ref<FontData>> data; - mutable Vector<RID> rids; + mutable Ref<Font> theme_font; - // Font config. - Dictionary variation_coordinates; - int spacing_bottom = 0; - int spacing_top = 0; + Ref<Font> base_font; - _FORCE_INLINE_ void _data_changed(); - _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record. + Variation variation; + Dictionary opentype_features; + int extra_spacing[TextServer::SPACING_MAX]; protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _update_rids() const override; virtual void reset_state() override; public: - static const int DEFAULT_FONT_SIZE = 16; + virtual void set_base_font(const Ref<Font> &p_font); + virtual Ref<Font> get_base_font() const; + virtual Ref<Font> _get_base_font_or_default() const; - Dictionary get_feature_list() const; + virtual void set_variation_opentype(const Dictionary &p_coords); + virtual Dictionary get_variation_opentype() const; - // Font data. - virtual void add_data(const Ref<FontData> &p_data); - virtual void set_data(int p_idx, const Ref<FontData> &p_data); - virtual int get_data_count() const; - virtual Ref<FontData> get_data(int p_idx) const; - virtual RID get_data_rid(int p_idx) const; - virtual void clear_data(); - virtual void remove_data(int p_idx); + virtual void set_variation_embolden(float p_strength); + virtual float get_variation_embolden() const; - // Font configuration. - virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates); - virtual Dictionary get_variation_coordinates() const; + virtual void set_variation_transform(Transform2D p_transform); + virtual Transform2D get_variation_transform() const; + + virtual void set_variation_face_index(int p_face_index); + virtual int get_variation_face_index() const; + + virtual void set_opentype_features(const Dictionary &p_features); + virtual Dictionary get_opentype_features() const override; virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value); - virtual int get_spacing(TextServer::SpacingType p_spacing) const; + virtual int get_spacing(TextServer::SpacingType p_spacing) const override; - // Font metrics. - virtual real_t get_height(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_ascent(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_descent(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_underline_position(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) const; + // Output. + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; - // Drawing string. - virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; + FontVariation(); + ~FontVariation(); +}; - virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; +/*************************************************************************/ +/* SystemFont */ +/*************************************************************************/ - // Helper functions. - virtual bool has_char(char32_t p_char) const; - virtual String get_supported_chars() const; +class SystemFont : public Font { + GDCLASS(SystemFont, Font); - // Drawing char. - virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + PackedStringArray names; + BitField<TextServer::FontStyle> style = 0; - Vector<RID> get_rids() const; + mutable Ref<Font> theme_font; - void update_changes(); + Ref<FontFile> base_font; + Vector<int> face_indeces; + int ftr_weight = 0; + int ftr_italic = 0; - Font(); - ~Font(); + bool antialiased = true; + bool mipmaps = false; + bool force_autohinter = false; + TextServer::Hinting hinting = TextServer::HINTING_LIGHT; + TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; + real_t oversampling = 0.f; + +protected: + static void _bind_methods(); + + virtual void _update_base_font(); + virtual void _update_rids() const override; + + virtual void reset_state() override; + +public: + virtual Ref<Font> _get_base_font_or_default() const; + + virtual void set_antialiased(bool p_antialiased); + virtual bool is_antialiased() const; + + virtual void set_generate_mipmaps(bool p_generate_mipmaps); + virtual bool get_generate_mipmaps() const; + + virtual void set_force_autohinter(bool p_force_autohinter); + virtual bool is_force_autohinter() const; + + virtual void set_hinting(TextServer::Hinting p_hinting); + virtual TextServer::Hinting get_hinting() const; + + virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel); + virtual TextServer::SubpixelPositioning get_subpixel_positioning() const; + + virtual void set_oversampling(real_t p_oversampling); + virtual real_t get_oversampling() const; + + virtual void set_font_names(const PackedStringArray &p_names); + virtual PackedStringArray get_font_names() const; + + virtual void set_font_style(BitField<TextServer::FontStyle> p_style); + virtual BitField<TextServer::FontStyle> get_font_style() const override; + + virtual int get_spacing(TextServer::SpacingType p_spacing) const override; + + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; + + int64_t get_face_count() const override; + + SystemFont(); + ~SystemFont(); }; -#endif /* FONT_H */ +#endif // FONT_H diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index 79ac1b57c3..a9c44dc6bf 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -71,7 +71,7 @@ void Gradient::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode"); - ADD_GROUP("Raw data", ""); + ADD_GROUP("Raw Data", ""); ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors"); @@ -157,7 +157,7 @@ void Gradient::reverse() { emit_signal(CoreStringNames::get_singleton()->changed); } -void Gradient::set_points(Vector<Gradient::Point> &p_points) { +void Gradient::set_points(const Vector<Gradient::Point> &p_points) { points = p_points; is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index c2085b3a13..e4bac15e4b 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -73,7 +73,7 @@ public: void add_point(float p_offset, const Color &p_color); void remove_point(int p_index); - void set_points(Vector<Point> &p_points); + void set_points(const Vector<Point> &p_points); Vector<Point> &get_points(); void reverse(); @@ -92,10 +92,6 @@ public: void set_interpolation_mode(InterpolationMode p_interp_mode); InterpolationMode get_interpolation_mode(); - _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) { - return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0))); - } - _FORCE_INLINE_ Color get_color_at_offset(float p_offset) { if (points.is_empty()) { return Color(0, 0, 0, 1); @@ -161,10 +157,10 @@ public: const Point &pointP3 = points[p3]; float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset); - float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x); - float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x); - float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x); - float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x); + float r = Math::cubic_interpolate(pointFirst.color.r, pointSecond.color.r, pointP0.color.r, pointP3.color.r, x); + float g = Math::cubic_interpolate(pointFirst.color.g, pointSecond.color.g, pointP0.color.g, pointP3.color.g, x); + float b = Math::cubic_interpolate(pointFirst.color.b, pointSecond.color.b, pointP0.color.b, pointP3.color.b, x); + float a = Math::cubic_interpolate(pointFirst.color.a, pointSecond.color.a, pointP0.color.a, pointP3.color.a, x); return Color(r, g, b, a); } break; diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp index 121930d86f..824dc4a544 100644 --- a/scene/resources/height_map_shape_3d.cpp +++ b/scene/resources/height_map_shape_3d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "height_map_shape_3d.h" + #include "servers/physics_server_3d.h" Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const { @@ -186,8 +187,8 @@ void HeightMapShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_map_data", "data"), &HeightMapShape3D::set_map_data); ClassDB::bind_method(D_METHOD("get_map_data"), &HeightMapShape3D::get_map_data); - ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_width", "get_map_width"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "1,4096,1"), "set_map_depth", "get_map_depth"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data"); } diff --git a/scene/resources/height_map_shape_3d.h b/scene/resources/height_map_shape_3d.h index 79d1b15674..633238030b 100644 --- a/scene/resources/height_map_shape_3d.h +++ b/scene/resources/height_map_shape_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef HEIGHT_MAP_SHAPE_H -#define HEIGHT_MAP_SHAPE_H +#ifndef HEIGHT_MAP_SHAPE_3D_H +#define HEIGHT_MAP_SHAPE_3D_H #include "scene/resources/shape_3d.h" @@ -60,4 +60,4 @@ public: HeightMapShape3D(); }; -#endif /* !HEIGHT_MAP_SHAPE_H */ +#endif // HEIGHT_MAP_SHAPE_3D_H diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 28afef8638..044477e744 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -211,7 +211,7 @@ void ImmediateMesh::surface_end() { 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; + value |= 3UL << 30; } *tangent = value; diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h index e5f627ae8e..de10fdbfbe 100644 --- a/scene/resources/immediate_mesh.h +++ b/scene/resources/immediate_mesh.h @@ -115,4 +115,4 @@ public: ~ImmediateMesh(); }; -#endif // IMMEDIATEMESH_H +#endif // IMMEDIATE_MESH_H diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index a27da11f8d..293fdd6f05 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -275,6 +275,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; + Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2]; unsigned int index_count = indices.size(); unsigned int vertex_count = vertices.size(); @@ -305,7 +306,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); const Vector3 *normals_ptr = normals.ptr(); - Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices; + HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices; LocalVector<int> vertex_remap; LocalVector<int> vertex_inverse_remap; @@ -313,22 +314,26 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli LocalVector<Vector3> merged_normals; LocalVector<int> merged_normals_counts; const Vector2 *uvs_ptr = uvs.ptr(); + const Vector2 *uv2s_ptr = uv2s.ptr(); for (unsigned int j = 0; j < vertex_count; j++) { const Vector3 &v = vertices_ptr[j]; const Vector3 &n = normals_ptr[j]; - Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v); + HashMap<Vector3, LocalVector<Pair<int, int>>>::Iterator E = unique_vertices.find(v); if (E) { - const LocalVector<Pair<int, int>> &close_verts = E->get(); + const LocalVector<Pair<int, int>> &close_verts = E->value; bool found = false; for (unsigned int k = 0; k < close_verts.size(); k++) { const Pair<int, int> &idx = close_verts[k]; - // TODO check more attributes? - if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) { + bool is_uvs_close = (!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2); + bool is_uv2s_close = (!uv2s_ptr || uv2s_ptr[j].distance_squared_to(uv2s_ptr[idx.second]) < CMP_EPSILON2); + ERR_FAIL_INDEX(idx.second, normals.size()); + bool is_normals_close = normals[idx.second].dot(n) > normal_merge_threshold; + if (is_uvs_close && is_uv2s_close && is_normals_close) { vertex_remap.push_back(idx.first); merged_normals[idx.first] += normals[idx.second]; merged_normals_counts[idx.first]++; @@ -415,7 +420,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli continue; } - if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) { + if (new_index_count == 0 || (new_index_count >= (index_count * 0.75f))) { break; } @@ -517,7 +522,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli Vector3 normal = n0 * w + n1 * u + n2 * v; Vector2 orig_uv = ray_uvs[j]; - real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; + const real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; for (int k = 0; k < 3; k++) { int idx = orig_tri_id * 3 + k; real_t weight = orig_bary[k]; @@ -702,15 +707,15 @@ void ImporterMesh::create_shadow_mesh() { Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; int vertex_count = vertices.size(); { - Map<Vector3, int> unique_vertices; + HashMap<Vector3, int> unique_vertices; const Vector3 *vptr = vertices.ptr(); for (int j = 0; j < vertex_count; j++) { const Vector3 &v = vptr[j]; - Map<Vector3, int>::Element *E = unique_vertices.find(v); + HashMap<Vector3, int>::Iterator E = unique_vertices.find(v); if (E) { - vertex_remap.push_back(E->get()); + vertex_remap.push_back(E->value); } else { int vcount = unique_vertices.size(); unique_vertices[v] = vcount; @@ -894,16 +899,16 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit Vector<uint32_t> indices; indices.resize(face_count * 3); { - Map<Vector3, uint32_t> vertex_map; + HashMap<Vector3, uint32_t> vertex_map; Vector3 *vertex_w = vertices.ptrw(); uint32_t *index_w = indices.ptrw(); for (int i = 0; i < face_count; i++) { for (int j = 0; j < 3; j++) { const Vector3 &vertex = faces[i].vertex[j]; - Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex); + HashMap<Vector3, uint32_t>::Iterator found_vertex = vertex_map.find(vertex); uint32_t index; if (found_vertex) { - index = found_vertex->get(); + index = found_vertex->value; } else { index = ++vertex_count; vertex_map[vertex] = index; @@ -956,7 +961,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { return Ref<NavigationMesh>(); } - Map<Vector3, int> unique_vertices; + HashMap<Vector3, int> unique_vertices; LocalVector<int> face_indices; for (int i = 0; i < faces.size(); i++) { @@ -1019,7 +1024,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, // Keep only the scale 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()); + Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length()); Transform3D transform; transform.scale(scale); @@ -1042,6 +1047,10 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; + if (!rnormals.size()) { + continue; + } + int vertex_ofs = vertices.size() / 3; vertices.resize((vertex_ofs + vc) * 3); @@ -1082,6 +1091,9 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, } else { for (int j = 0; j < ic / 3; j++) { + ERR_FAIL_INDEX_V(rindices[j * 3 + 0], rvertices.size(), ERR_INVALID_DATA); + ERR_FAIL_INDEX_V(rindices[j * 3 + 1], rvertices.size(), ERR_INVALID_DATA); + ERR_FAIL_INDEX_V(rindices[j * 3 + 2], rvertices.size(), ERR_INVALID_DATA); Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); @@ -1181,7 +1193,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, for (unsigned int i = 0; i < surfaces_tools.size(); i++) { surfaces_tools[i]->index(); Array arrays = surfaces_tools[i]->commit_to_arrays(); - add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); + add_surface(surfaces_tools[i]->get_primitive_type(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name")); } set_lightmap_size_hint(Size2(size_x, size_y)); @@ -1234,6 +1246,7 @@ void ImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name); ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material); + ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle"), &ImporterMesh::generate_lods); ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>())); ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear); diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 8f77597a58..bf1d0301d1 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SCENE_IMPORTER_MESH_H -#define SCENE_IMPORTER_MESH_H +#ifndef IMPORTER_MESH_H +#define IMPORTER_MESH_H #include "core/io/resource.h" #include "core/templates/local_vector.h" @@ -130,4 +130,5 @@ public: Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>()); void clear(); }; -#endif // SCENE_IMPORTER_MESH_H + +#endif // IMPORTER_MESH_H diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp new file mode 100644 index 0000000000..e8b986b431 --- /dev/null +++ b/scene/resources/label_settings.cpp @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* label_settings.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "label_settings.h" + +#include "core/core_string_names.h" + +void LabelSettings::_font_changed() { + emit_changed(); +} + +void LabelSettings::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_line_spacing", "spacing"), &LabelSettings::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &LabelSettings::get_line_spacing); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &LabelSettings::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &LabelSettings::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "size"), &LabelSettings::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &LabelSettings::get_font_size); + + ClassDB::bind_method(D_METHOD("set_font_color", "color"), &LabelSettings::set_font_color); + ClassDB::bind_method(D_METHOD("get_font_color"), &LabelSettings::get_font_color); + + ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &LabelSettings::set_outline_size); + ClassDB::bind_method(D_METHOD("get_outline_size"), &LabelSettings::get_outline_size); + + ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &LabelSettings::set_outline_color); + ClassDB::bind_method(D_METHOD("get_outline_color"), &LabelSettings::get_outline_color); + + ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &LabelSettings::set_shadow_size); + ClassDB::bind_method(D_METHOD("get_shadow_size"), &LabelSettings::get_shadow_size); + + ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &LabelSettings::set_shadow_color); + ClassDB::bind_method(D_METHOD("get_shadow_color"), &LabelSettings::get_shadow_color); + + ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &LabelSettings::set_shadow_offset); + ClassDB::bind_method(D_METHOD("get_shadow_offset"), &LabelSettings::get_shadow_offset); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); + + ADD_GROUP("Font", "font"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,1024,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "font_color"), "set_font_color", "get_font_color"); + + ADD_GROUP("Outline", "outline"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_outline_size", "get_outline_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); + + ADD_GROUP("Shadow", "shadow"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); +} + +void LabelSettings::set_line_spacing(real_t p_spacing) { + if (line_spacing != p_spacing) { + line_spacing = p_spacing; + emit_changed(); + } +} + +real_t LabelSettings::get_line_spacing() const { + return line_spacing; +} + +void LabelSettings::set_font(const Ref<Font> &p_font) { + if (font != p_font) { + if (font.is_valid()) { + font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed)); + } + font = p_font; + if (font.is_valid()) { + font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), varray(), CONNECT_REFERENCE_COUNTED); + } + emit_changed(); + } +} + +Ref<Font> LabelSettings::get_font() const { + return font; +} + +void LabelSettings::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_font_size() const { + return font_size; +} + +void LabelSettings::set_font_color(const Color &p_color) { + if (font_color != p_color) { + font_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_font_color() const { + return font_color; +} + +void LabelSettings::set_outline_size(int p_size) { + if (outline_size != p_size) { + outline_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_outline_size() const { + return outline_size; +} + +void LabelSettings::set_outline_color(const Color &p_color) { + if (outline_color != p_color) { + outline_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_outline_color() const { + return outline_color; +} + +void LabelSettings::set_shadow_size(int p_size) { + if (shadow_size != p_size) { + shadow_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_shadow_size() const { + return shadow_size; +} + +void LabelSettings::set_shadow_color(const Color &p_color) { + if (shadow_color != p_color) { + shadow_color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_shadow_color() const { + return shadow_color; +} + +void LabelSettings::set_shadow_offset(const Vector2 &p_offset) { + if (shadow_offset != p_offset) { + shadow_offset = p_offset; + emit_changed(); + } +} + +Vector2 LabelSettings::get_shadow_offset() const { + return shadow_offset; +} diff --git a/scene/resources/label_settings.h b/scene/resources/label_settings.h new file mode 100644 index 0000000000..d2644a7484 --- /dev/null +++ b/scene/resources/label_settings.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* label_settings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 LABEL_SETTINGS_H +#define LABEL_SETTINGS_H + +#include "core/io/resource.h" +#include "font.h" + +/*************************************************************************/ + +class LabelSettings : public Resource { + GDCLASS(LabelSettings, Resource); + + real_t line_spacing = 0; + + Ref<Font> font; + int font_size = Font::DEFAULT_FONT_SIZE; + Color font_color = Color(0.875, 0.875, 0.875); + + int outline_size = 0; + Color outline_color = Color(1, 1, 1); + + int shadow_size = 0; + Color shadow_color = Color(1, 1, 1); + Vector2 shadow_offset = Vector2(1, 1); + + void _font_changed(); + +protected: + static void _bind_methods(); + +public: + void set_line_spacing(real_t p_spacing); + real_t get_line_spacing() const; + + void set_font(const Ref<Font> &p_font); + Ref<Font> get_font() const; + + void set_font_size(int p_size); + int get_font_size() const; + + void set_font_color(const Color &p_color); + Color get_font_color() const; + + void set_outline_size(int p_size); + int get_outline_size() const; + + void set_outline_color(const Color &p_color); + Color get_outline_color() const; + + void set_shadow_size(int p_size); + int get_shadow_size() const; + + void set_shadow_color(const Color &p_color); + Color get_shadow_color() const; + + void set_shadow_offset(const Vector2 &p_offset); + Vector2 get_shadow_offset() const; +}; + +#endif // LABEL_SETTINGS_H diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b74f44c52f..f07232a3ad 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -84,10 +84,41 @@ void Material::inspect_native_shader_code() { SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()); RID shader = get_shader_rid(); if (st && shader.is_valid()) { - st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader); + st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader); } } +RID Material::get_shader_rid() const { + RID ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret)) { + return ret; + } + return RID(); +} +Shader::Mode Material::get_shader_mode() const { + Shader::Mode ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret)) { + return ret; + } + + return Shader::MODE_MAX; +} + +bool Material::_can_do_next_pass() const { + bool ret; + if (GDVIRTUAL_CALL(_can_do_next_pass, ret)) { + return ret; + } + return false; +} +bool Material::_can_use_render_priority() const { + bool ret; + if (GDVIRTUAL_CALL(_can_use_render_priority, ret)) { + return ret; + } + return false; +} + void Material::_bind_methods() { ClassDB::bind_method(D_METHOD("set_next_pass", "next_pass"), &Material::set_next_pass); ClassDB::bind_method(D_METHOD("get_next_pass"), &Material::get_next_pass); @@ -103,6 +134,11 @@ void Material::_bind_methods() { BIND_CONSTANT(RENDER_PRIORITY_MAX); BIND_CONSTANT(RENDER_PRIORITY_MIN); + + GDVIRTUAL_BIND(_get_shader_rid) + GDVIRTUAL_BIND(_get_shader_mode) + GDVIRTUAL_BIND(_can_do_next_pass) + GDVIRTUAL_BIND(_can_use_render_priority) } Material::Material() { @@ -151,9 +187,9 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { } if (pr) { - const Map<StringName, Variant>::Element *E = param_cache.find(pr); + HashMap<StringName, Variant>::ConstIterator E = param_cache.find(pr); if (E) { - r_ret = E->get(); + r_ret = E->value; } else { r_ret = Variant(); } @@ -166,7 +202,98 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { if (!shader.is_null()) { - shader->get_param_list(p_list); + List<PropertyInfo> list; + shader->get_param_list(&list, true); + + HashMap<String, HashMap<String, List<PropertyInfo>>> groups; + { + HashMap<String, List<PropertyInfo>> none_subgroup; + none_subgroup.insert("<None>", List<PropertyInfo>()); + groups.insert("<None>", none_subgroup); + } + + String last_group = "<None>"; + String last_subgroup = "<None>"; + + bool is_none_group_undefined = true; + bool is_none_group = true; + + for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_GROUP) { + if (!E->get().name.is_empty()) { + Vector<String> vgroup = E->get().name.split("::"); + last_group = vgroup[0]; + if (vgroup.size() > 1) { + last_subgroup = vgroup[1]; + } else { + last_subgroup = "<None>"; + } + is_none_group = false; + + if (!groups.has(last_group)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = last_group; + + List<PropertyInfo> none_subgroup; + none_subgroup.push_back(info); + + HashMap<String, List<PropertyInfo>> subgroup_map; + subgroup_map.insert("<None>", none_subgroup); + + groups.insert(last_group, subgroup_map); + } + + if (!groups[last_group].has(last_subgroup)) { + PropertyInfo info; + info.usage = PROPERTY_USAGE_SUBGROUP; + info.name = last_subgroup; + + List<PropertyInfo> subgroup; + subgroup.push_back(info); + + groups[last_group].insert(last_subgroup, subgroup); + } + } else { + last_group = "<None>"; + last_subgroup = "<None>"; + is_none_group = true; + } + continue; // Pass group. + } + + if (is_none_group_undefined && is_none_group) { + is_none_group_undefined = false; + + PropertyInfo info; + info.usage = PROPERTY_USAGE_GROUP; + info.name = "Shader Param"; + groups["<None>"]["<None>"].push_back(info); + } + + PropertyInfo info = E->get(); + info.name = info.name; + groups[last_group][last_subgroup].push_back(info); + } + + // Sort groups alphabetically. + List<UniformProp> props; + for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) { + for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = group->value.begin(); subgroup; ++subgroup) { + for (List<PropertyInfo>::Element *item = subgroup->value.front(); item; item = item->next()) { + if (subgroup->key == "<None>") { + props.push_back({ group->key, item->get() }); + } else { + props.push_back({ group->key + "::" + subgroup->key, item->get() }); + } + } + } + } + props.sort_custom<UniformPropComparator>(); + + for (List<UniformProp>::Element *E = props.front(); E; E = E->next()) { + p_list->push_back(E->get().info); + } } } @@ -312,7 +439,7 @@ ShaderMaterial::~ShaderMaterial() { Mutex BaseMaterial3D::material_mutex; SelfList<BaseMaterial3D>::List *BaseMaterial3D::dirty_materials = nullptr; -Map<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData> BaseMaterial3D::shader_map; +HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map; BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr; void BaseMaterial3D::init_shaders() { @@ -330,7 +457,7 @@ void BaseMaterial3D::init_shaders() { shader_names->rim = "rim"; shader_names->rim_tint = "rim_tint"; shader_names->clearcoat = "clearcoat"; - shader_names->clearcoat_gloss = "clearcoat_gloss"; + shader_names->clearcoat_roughness = "clearcoat_roughness"; shader_names->anisotropy = "anisotropy_ratio"; shader_names->heightmap_scale = "heightmap_scale"; shader_names->subsurface_scattering_strength = "subsurface_scattering_strength"; @@ -359,6 +486,9 @@ void BaseMaterial3D::init_shaders() { shader_names->distance_fade_min = "distance_fade_min"; shader_names->distance_fade_max = "distance_fade_max"; + shader_names->msdf_pixel_range = "msdf_pixel_range"; + shader_names->msdf_outline_size = "msdf_outline_size"; + shader_names->metallic_texture_channel = "metallic_texture_channel"; shader_names->ao_texture_channel = "ao_texture_channel"; shader_names->clearcoat_texture_channel = "clearcoat_texture_channel"; @@ -396,12 +526,10 @@ void BaseMaterial3D::init_shaders() { shader_names->albedo_texture_size = "albedo_texture_size"; } -Ref<StandardMaterial3D> BaseMaterial3D::materials_for_2d[BaseMaterial3D::MAX_MATERIALS_FOR_2D]; +HashMap<uint64_t, Ref<StandardMaterial3D>> BaseMaterial3D::materials_for_2d; void BaseMaterial3D::finish_shaders() { - for (int i = 0; i < MAX_MATERIALS_FOR_2D; i++) { - materials_for_2d[i].unref(); - } + materials_for_2d.clear(); memdelete(dirty_materials); dirty_materials = nullptr; @@ -435,24 +563,33 @@ void BaseMaterial3D::_update_shader() { } String texfilter_str; + // Force linear filtering for the heightmap texture, as the heightmap effect + // looks broken with nearest-neighbor filtering (with and without Deep Parallax). + String texfilter_height_str; switch (texture_filter) { case TEXTURE_FILTER_NEAREST: texfilter_str = "filter_nearest"; + texfilter_height_str = "filter_linear"; break; case TEXTURE_FILTER_LINEAR: texfilter_str = "filter_linear"; + texfilter_height_str = "filter_linear"; break; case TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: texfilter_str = "filter_nearest_mipmap"; + texfilter_height_str = "filter_linear_mipmap"; break; case TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: texfilter_str = "filter_linear_mipmap"; + texfilter_height_str = "filter_linear_mipmap"; break; case TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: texfilter_str = "filter_nearest_mipmap_anisotropic"; + texfilter_height_str = "filter_linear_mipmap_anisotropic"; break; case TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: texfilter_str = "filter_linear_mipmap_anisotropic"; + texfilter_height_str = "filter_linear_mipmap_anisotropic"; break; case TEXTURE_FILTER_MAX: break; // Internal value, skip. @@ -460,8 +597,10 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_USE_TEXTURE_REPEAT]) { texfilter_str += ",repeat_enable"; + texfilter_height_str += ",repeat_enable"; } else { texfilter_str += ",repeat_disable"; + texfilter_height_str += ",repeat_disable"; } //must create a shader! @@ -541,12 +680,6 @@ void BaseMaterial3D::_update_shader() { case SPECULAR_SCHLICK_GGX: code += ",specular_schlick_ggx"; break; - case SPECULAR_BLINN: - code += ",specular_blinn"; - break; - case SPECULAR_PHONG: - code += ",specular_phong"; - break; case SPECULAR_TOON: code += ",specular_toon"; break; @@ -600,8 +733,8 @@ void BaseMaterial3D::_update_shader() { code += ";\n"; - code += "uniform vec4 albedo : hint_color;\n"; - code += "uniform sampler2D texture_albedo : hint_albedo," + texfilter_str + ";\n"; + code += "uniform vec4 albedo : source_color;\n"; + code += "uniform sampler2D texture_albedo : source_color," + texfilter_str + ";\n"; if (grow_enabled) { code += "uniform float grow;\n"; } @@ -614,6 +747,11 @@ void BaseMaterial3D::_update_shader() { code += "uniform float distance_fade_max;\n"; } + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += "uniform float msdf_pixel_range;\n"; + code += "uniform float msdf_outline_size;\n"; + } + // alpha scissor is only valid if there is not antialiasing edge // alpha hash is valid whenever, but not with alpha scissor if (transparency == TRANSPARENCY_ALPHA_SCISSOR) { @@ -633,7 +771,7 @@ void BaseMaterial3D::_update_shader() { //TODO ALL HINTS if (!orm) { code += "uniform float roughness : hint_range(0,1);\n"; - code += "uniform sampler2D texture_metallic : hint_white," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_metallic : hint_default_white," + texfilter_str + ";\n"; code += "uniform vec4 metallic_texture_channel;\n"; switch (roughness_texture_channel) { case TEXTURE_CHANNEL_RED: { @@ -668,8 +806,8 @@ void BaseMaterial3D::_update_shader() { } if (features[FEATURE_EMISSION]) { - code += "uniform sampler2D texture_emission : hint_black_albedo," + texfilter_str + ";\n"; - code += "uniform vec4 emission : hint_color;\n"; + code += "uniform sampler2D texture_emission : source_color, hint_default_black," + texfilter_str + ";\n"; + code += "uniform vec4 emission : source_color;\n"; code += "uniform float emission_energy;\n"; } @@ -686,48 +824,48 @@ void BaseMaterial3D::_update_shader() { if (features[FEATURE_RIM]) { code += "uniform float rim : hint_range(0,1);\n"; code += "uniform float rim_tint : hint_range(0,1);\n"; - code += "uniform sampler2D texture_rim : hint_white," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_rim : hint_default_white," + texfilter_str + ";\n"; } if (features[FEATURE_CLEARCOAT]) { code += "uniform float clearcoat : hint_range(0,1);\n"; - code += "uniform float clearcoat_gloss : hint_range(0,1);\n"; - code += "uniform sampler2D texture_clearcoat : hint_white," + texfilter_str + ";\n"; + code += "uniform float clearcoat_roughness : hint_range(0,1);\n"; + code += "uniform sampler2D texture_clearcoat : hint_default_white," + texfilter_str + ";\n"; } if (features[FEATURE_ANISOTROPY]) { code += "uniform float anisotropy_ratio : hint_range(0,256);\n"; code += "uniform sampler2D texture_flowmap : hint_anisotropy," + texfilter_str + ";\n"; } if (features[FEATURE_AMBIENT_OCCLUSION]) { - code += "uniform sampler2D texture_ambient_occlusion : hint_white, " + texfilter_str + ";\n"; + code += "uniform sampler2D texture_ambient_occlusion : hint_default_white, " + texfilter_str + ";\n"; code += "uniform vec4 ao_texture_channel;\n"; code += "uniform float ao_light_affect;\n"; } if (features[FEATURE_DETAIL]) { - code += "uniform sampler2D texture_detail_albedo : hint_albedo," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_detail_albedo : source_color," + texfilter_str + ";\n"; code += "uniform sampler2D texture_detail_normal : hint_normal," + texfilter_str + ";\n"; - code += "uniform sampler2D texture_detail_mask : hint_white," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_detail_mask : hint_default_white," + texfilter_str + ";\n"; } if (features[FEATURE_SUBSURFACE_SCATTERING]) { code += "uniform float subsurface_scattering_strength : hint_range(0,1);\n"; - code += "uniform sampler2D texture_subsurface_scattering : hint_white," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_subsurface_scattering : hint_default_white," + texfilter_str + ";\n"; } if (features[FEATURE_SUBSURFACE_TRANSMITTANCE]) { - code += "uniform vec4 transmittance_color : hint_color;\n"; + code += "uniform vec4 transmittance_color : source_color;\n"; code += "uniform float transmittance_depth;\n"; - code += "uniform sampler2D texture_subsurface_transmittance : hint_white," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_subsurface_transmittance : hint_default_white," + texfilter_str + ";\n"; code += "uniform float transmittance_boost;\n"; } if (features[FEATURE_BACKLIGHT]) { - code += "uniform vec4 backlight : hint_color;\n"; - code += "uniform sampler2D texture_backlight : hint_black," + texfilter_str + ";\n"; + code += "uniform vec4 backlight : source_color;\n"; + code += "uniform sampler2D texture_backlight : hint_default_black," + texfilter_str + ";\n"; } if (features[FEATURE_HEIGHT_MAPPING]) { - code += "uniform sampler2D texture_heightmap : hint_black," + texfilter_str + ";\n"; + code += "uniform sampler2D texture_heightmap : hint_default_black," + texfilter_height_str + ";\n"; code += "uniform float heightmap_scale;\n"; code += "uniform int heightmap_min_layers;\n"; code += "uniform int heightmap_max_layers;\n"; @@ -779,26 +917,26 @@ void BaseMaterial3D::_update_shader() { case BILLBOARD_DISABLED: { } break; case BILLBOARD_ENABLED: { - code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - 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"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } } break; case BILLBOARD_FIXED_Y: { - code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n"; + code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);\n"; if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { - 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"; + code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } } break; case BILLBOARD_PARTICLES: { //make billboard - 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"; + code += " mat4 mat_world = mat4(normalize(INV_VIEW_MATRIX[0]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[1]) * length(MODEL_MATRIX[0]),normalize(INV_VIEW_MATRIX[2]) * length(MODEL_MATRIX[2]), MODEL_MATRIX[3]);\n"; //rotate by rotation - 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"; + 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 += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n"; + code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat_world;\n"; //handle animation code += " float h_frames = float(particles_anim_h_frames);\n"; @@ -853,8 +991,8 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_UV1_USE_TRIPLANAR]) { if (flags[FLAG_UV1_USE_WORLD_TRIPLANAR]) { - 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"; + code += " uv1_power_normal=pow(abs(mat3(MODEL_MATRIX) * NORMAL),vec3(uv1_blend_sharpness));\n"; + code += " uv1_triplanar_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv1_scale + uv1_offset;\n"; } else { code += " uv1_power_normal=pow(abs(NORMAL),vec3(uv1_blend_sharpness));\n"; code += " uv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;\n"; @@ -865,8 +1003,8 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_UV2_USE_TRIPLANAR]) { if (flags[FLAG_UV2_USE_WORLD_TRIPLANAR]) { - 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"; + code += " uv2_power_normal=pow(abs(mat3(MODEL_MATRIX) * NORMAL), vec3(uv2_blend_sharpness));\n"; + code += " uv2_triplanar_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0f)).xyz * uv2_scale + uv2_offset;\n"; } else { code += " uv2_power_normal=pow(abs(NORMAL), vec3(uv2_blend_sharpness));\n"; code += " uv2_triplanar_pos = VERTEX * uv2_scale + uv2_offset;\n"; @@ -881,6 +1019,12 @@ void BaseMaterial3D::_update_shader() { code += "}\n"; code += "\n\n"; + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += "float msdf_median(float r, float g, float b, float a) {\n"; + code += " return min(max(min(r, g), min(max(r, g), b)), a);\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 += " vec4 samp=vec4(0.0);\n"; @@ -980,7 +1124,30 @@ void BaseMaterial3D::_update_shader() { } } - if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) { + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += " {\n"; + code += " albedo_tex.rgb = mix(vec3(1.0 + 0.055) * pow(albedo_tex.rgb, vec3(1.0 / 2.4)) - vec3(0.055), vec3(12.92) * albedo_tex.rgb.rgb, lessThan(albedo_tex.rgb, vec3(0.0031308)));\n"; + code += " vec2 msdf_size = vec2(msdf_pixel_range) / vec2(textureSize(texture_albedo, 0));\n"; + if (flags[FLAG_USE_POINT_SIZE]) { + code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n"; + } else { + if (flags[FLAG_UV1_USE_TRIPLANAR]) { + code += " vec2 dest_size = vec2(1.0) / fwidth(uv1_triplanar_pos);\n"; + } else { + code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n"; + } + } + code += " float px_size = max(0.5 * dot(msdf_size, dest_size), 1.0);\n"; + code += " float d = msdf_median(albedo_tex.r, albedo_tex.g, albedo_tex.b, albedo_tex.a) - 0.5;\n"; + code += " if (msdf_outline_size > 0.0) {\n"; + code += " float cr = clamp(msdf_outline_size, 0.0, msdf_pixel_range / 2.0) / msdf_pixel_range;\n"; + code += " albedo_tex.a = clamp((d + cr) * px_size, 0.0, 1.0);\n"; + code += " } else {\n"; + code += " albedo_tex.a = clamp(d * px_size + 0.5, 0.0, 1.0);\n"; + code += " }\n"; + code += " albedo_tex.rgb = vec3(1.0);\n"; + code += " }\n"; + } else if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) { 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"; } @@ -1110,7 +1277,7 @@ void BaseMaterial3D::_update_shader() { if (!RenderingServer::get_singleton()->is_low_end()) { code += " {\n"; if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { - code += " float fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; + code += " float fade_distance = abs((VIEW_MATRIX * MODEL_MATRIX[3]).z);\n"; } else { code += " float fade_distance=-VERTEX.z;\n"; @@ -1166,7 +1333,7 @@ void BaseMaterial3D::_update_shader() { code += " vec2 clearcoat_tex = texture(texture_clearcoat,base_uv).xy;\n"; } code += " CLEARCOAT = clearcoat*clearcoat_tex.x;"; - code += " CLEARCOAT_GLOSS = clearcoat_gloss*clearcoat_tex.y;\n"; + code += " CLEARCOAT_ROUGHNESS = clearcoat_roughness*clearcoat_tex.y;\n"; } if (features[FEATURE_ANISOTROPY]) { @@ -1408,13 +1575,13 @@ float BaseMaterial3D::get_clearcoat() const { return clearcoat; } -void BaseMaterial3D::set_clearcoat_gloss(float p_clearcoat_gloss) { - clearcoat_gloss = p_clearcoat_gloss; - RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_gloss, p_clearcoat_gloss); +void BaseMaterial3D::set_clearcoat_roughness(float p_clearcoat_roughness) { + clearcoat_roughness = p_clearcoat_roughness; + RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_roughness, p_clearcoat_roughness); } -float BaseMaterial3D::get_clearcoat_gloss() const { - return clearcoat_gloss; +float BaseMaterial3D::get_clearcoat_roughness() const { + return clearcoat_roughness; } void BaseMaterial3D::set_anisotropy(float p_anisotropy) { @@ -1723,8 +1890,6 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { _validate_high_end("refraction", property); _validate_high_end("subsurf_scatter", property); - _validate_high_end("anisotropy", property); - _validate_high_end("clearcoat", property); _validate_high_end("heightmap", property); if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { @@ -1747,6 +1912,14 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NO_EDITOR; } + if (property.name == "msdf_pixel_range" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (property.name == "msdf_outline_size" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) { property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -2095,35 +2268,45 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -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; +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, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, RID *r_shader_rid) { + int64_t hash = 0; if (p_shaded) { - version = 1; + hash |= 1 << 0; } if (p_transparent) { - version |= 2; + hash |= 1 << 1; } if (p_cut_alpha) { - version |= 4; + hash |= 1 << 2; } if (p_opaque_prepass) { - version |= 8; + hash |= 1 << 3; } if (p_double_sided) { - version |= 16; + hash |= 1 << 4; } if (p_billboard) { - version |= 32; + hash |= 1 << 5; } if (p_billboard_y) { - version |= 64; + hash |= 1 << 6; + } + if (p_msdf) { + hash |= 1 << 7; } + if (p_no_depth) { + hash |= 1 << 8; + } + if (p_fixed_size) { + hash |= 1 << 9; + } + hash = hash_murmur3_one_64(p_filter, hash); - if (materials_for_2d[version].is_valid()) { + if (materials_for_2d.has(hash)) { if (r_shader_rid) { - *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); } - return materials_for_2d[version]; + return materials_for_2d[hash]; } Ref<StandardMaterial3D> material; @@ -2134,18 +2317,22 @@ Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transpar material->set_cull_mode(p_double_sided ? CULL_DISABLED : CULL_BACK); material->set_flag(FLAG_SRGB_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(FLAG_ALBEDO_TEXTURE_MSDF, p_msdf); + material->set_flag(FLAG_DISABLE_DEPTH_TEST, p_no_depth); + material->set_flag(FLAG_FIXED_SIZE, p_fixed_size); + material->set_texture_filter(p_filter); if (p_billboard || p_billboard_y) { material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true); material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED); } - materials_for_2d[version] = material; + materials_for_2d[hash] = material; if (r_shader_rid) { - *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); } - return materials_for_2d[version]; + return materials_for_2d[hash]; } void BaseMaterial3D::set_on_top_of_alpha() { @@ -2173,6 +2360,24 @@ float BaseMaterial3D::get_proximity_fade_distance() const { return proximity_fade_distance; } +void BaseMaterial3D::set_msdf_pixel_range(float p_range) { + msdf_pixel_range = p_range; + RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_pixel_range, p_range); +} + +float BaseMaterial3D::get_msdf_pixel_range() const { + return msdf_pixel_range; +} + +void BaseMaterial3D::set_msdf_outline_size(float p_size) { + msdf_outline_size = p_size; + RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_outline_size, p_size); +} + +float BaseMaterial3D::get_msdf_outline_size() const { + return msdf_outline_size; +} + void BaseMaterial3D::set_distance_fade(DistanceFadeMode p_mode) { distance_fade = p_mode; _queue_shader_change(); @@ -2271,8 +2476,8 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clearcoat", "clearcoat"), &BaseMaterial3D::set_clearcoat); ClassDB::bind_method(D_METHOD("get_clearcoat"), &BaseMaterial3D::get_clearcoat); - ClassDB::bind_method(D_METHOD("set_clearcoat_gloss", "clearcoat_gloss"), &BaseMaterial3D::set_clearcoat_gloss); - ClassDB::bind_method(D_METHOD("get_clearcoat_gloss"), &BaseMaterial3D::get_clearcoat_gloss); + ClassDB::bind_method(D_METHOD("set_clearcoat_roughness", "clearcoat_roughness"), &BaseMaterial3D::set_clearcoat_roughness); + ClassDB::bind_method(D_METHOD("get_clearcoat_roughness"), &BaseMaterial3D::get_clearcoat_roughness); ClassDB::bind_method(D_METHOD("set_anisotropy", "anisotropy"), &BaseMaterial3D::set_anisotropy); ClassDB::bind_method(D_METHOD("get_anisotropy"), &BaseMaterial3D::get_anisotropy); @@ -2415,6 +2620,12 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_proximity_fade_distance", "distance"), &BaseMaterial3D::set_proximity_fade_distance); ClassDB::bind_method(D_METHOD("get_proximity_fade_distance"), &BaseMaterial3D::get_proximity_fade_distance); + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "range"), &BaseMaterial3D::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &BaseMaterial3D::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_outline_size", "size"), &BaseMaterial3D::set_msdf_outline_size); + ClassDB::bind_method(D_METHOD("get_msdf_outline_size"), &BaseMaterial3D::get_msdf_outline_size); + ClassDB::bind_method(D_METHOD("set_distance_fade", "mode"), &BaseMaterial3D::set_distance_fade); ClassDB::bind_method(D_METHOD("get_distance_fade"), &BaseMaterial3D::get_distance_fade); @@ -2438,7 +2649,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Shading", ""); 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_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Toon,Disabled"), "set_specular_mode", "get_specular_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT); ADD_GROUP("Vertex Color", "vertex_color"); @@ -2448,7 +2659,8 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Albedo", "albedo_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo_color"), "set_albedo", "get_albedo"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "albedo_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ALBEDO); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_texture_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_texture_msdf"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_MSDF); ADD_GROUP("ORM", "orm_"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orm_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ORM); @@ -2486,7 +2698,7 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Clearcoat", "clearcoat_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "clearcoat_enabled"), "set_feature", "get_feature", FEATURE_CLEARCOAT); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat", "get_clearcoat"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_gloss", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_gloss", "get_clearcoat_gloss"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "clearcoat_roughness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_clearcoat_roughness", "get_clearcoat_roughness"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "clearcoat_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_CLEARCOAT); ADD_GROUP("Anisotropy", "anisotropy_"); @@ -2545,14 +2757,14 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_NORMAL); ADD_GROUP("UV1", "uv1_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv1_scale"), "set_uv1_scale", "get_uv1_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv1_scale", PROPERTY_HINT_LINK), "set_uv1_scale", "get_uv1_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv1_offset"), "set_uv1_offset", "get_uv1_offset"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv1_triplanar"), "set_flag", "get_flag", FLAG_UV1_USE_TRIPLANAR); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv1_triplanar_sharpness", PROPERTY_HINT_EXP_EASING), "set_uv1_triplanar_blend_sharpness", "get_uv1_triplanar_blend_sharpness"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv1_world_triplanar"), "set_flag", "get_flag", FLAG_UV1_USE_WORLD_TRIPLANAR); ADD_GROUP("UV2", "uv2_"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv2_scale"), "set_uv2_scale", "get_uv2_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv2_scale", PROPERTY_HINT_LINK), "set_uv2_scale", "get_uv2_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "uv2_offset"), "set_uv2_offset", "get_uv2_offset"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_TRIPLANAR); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_triplanar_sharpness", PROPERTY_HINT_EXP_EASING), "set_uv2_triplanar_blend_sharpness", "get_uv2_triplanar_blend_sharpness"); @@ -2577,19 +2789,22 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Grow", "grow_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "grow"), "set_grow_enabled", "is_grow_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_amount", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_grow", "get_grow"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_amount", PROPERTY_HINT_RANGE, "-16,16,0.001,suffix:m"), "set_grow", "get_grow"); ADD_GROUP("Transform", ""); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_point_size"), "set_flag", "get_flag", FLAG_USE_POINT_SIZE); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1,suffix:px"), "set_point_size", "get_point_size"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_particle_trails"), "set_flag", "get_flag", FLAG_PARTICLE_TRAILS_MODE); ADD_GROUP("Proximity Fade", "proximity_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_proximity_fade_distance", "get_proximity_fade_distance"); + ADD_GROUP("MSDF", "msdf_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "1,250,1"), "set_msdf_outline_size", "get_msdf_outline_size"); ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "distance_fade_mode", PROPERTY_HINT_ENUM, "Disabled,PixelAlpha,PixelDither,ObjectDither"), "set_distance_fade", "get_distance_fade"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_max_distance", "get_distance_fade_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_max_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_max_distance", "get_distance_fade_max_distance"); BIND_ENUM_CONSTANT(TEXTURE_ALBEDO); BIND_ENUM_CONSTANT(TEXTURE_METALLIC); @@ -2685,6 +2900,7 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP); BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN); BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE); + BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_MSDF); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); @@ -2693,8 +2909,6 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(DIFFUSE_TOON); BIND_ENUM_CONSTANT(SPECULAR_SCHLICK_GGX); - BIND_ENUM_CONSTANT(SPECULAR_BLINN); - BIND_ENUM_CONSTANT(SPECULAR_PHONG); BIND_ENUM_CONSTANT(SPECULAR_TOON); BIND_ENUM_CONSTANT(SPECULAR_DISABLED); @@ -2732,7 +2946,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_rim(1.0); set_rim_tint(0.5); set_clearcoat(1); - set_clearcoat_gloss(0.5); + set_clearcoat_roughness(0.5); set_anisotropy(0); set_heightmap_scale(0.05); set_subsurface_scattering_strength(0); @@ -2776,6 +2990,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_heightmap_deep_parallax_max_layers(32); set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal + flags[FLAG_ALBEDO_TEXTURE_MSDF] = false; flags[FLAG_USE_TEXTURE_REPEAT] = true; is_initialized = true; @@ -2852,7 +3067,7 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) { "flags_no_depth_test", "no_depth_test" }, { "flags_use_point_size", "use_point_size" }, { "flags_fixed_size", "fixed_Size" }, - { "flags_albedo_tex_force_srg", "albedo_tex_force_srgb" }, + { "flags_albedo_tex_force_srgb", "albedo_texture_force_srgb" }, { "flags_do_not_receive_shadows", "disable_receive_shadows" }, { "flags_disable_ambient_light", "disable_ambient_light" }, { "params_diffuse_mode", "diffuse_mode" }, @@ -2895,4 +3110,7 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) return false; } + #endif // DISABLE_DEPRECATED + +/////////////////////// diff --git a/scene/resources/material.h b/scene/resources/material.h index 57591bee2f..8c04817c6b 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -51,11 +51,15 @@ class Material : public Resource { protected: _FORCE_INLINE_ RID _get_material() const { return material; } static void _bind_methods(); - virtual bool _can_do_next_pass() const { return false; } - virtual bool _can_use_render_priority() const { return false; } + virtual bool _can_do_next_pass() const; + virtual bool _can_use_render_priority() const; void _validate_property(PropertyInfo &property) const override; + GDVIRTUAL0RC(RID, _get_shader_rid) + GDVIRTUAL0RC(Shader::Mode, _get_shader_mode) + GDVIRTUAL0RC(bool, _can_do_next_pass) + GDVIRTUAL0RC(bool, _can_use_render_priority) public: enum { RENDER_PRIORITY_MAX = RS::MATERIAL_RENDER_PRIORITY_MAX, @@ -68,9 +72,8 @@ public: int get_render_priority() const; virtual RID get_rid() const override; - virtual RID get_shader_rid() const = 0; - - virtual Shader::Mode get_shader_mode() const = 0; + virtual RID get_shader_rid() const; + virtual Shader::Mode get_shader_mode() const; Material(); virtual ~Material(); }; @@ -79,7 +82,18 @@ class ShaderMaterial : public Material { GDCLASS(ShaderMaterial, Material); Ref<Shader> shader; - Map<StringName, Variant> param_cache; + HashMap<StringName, Variant> param_cache; + + struct UniformProp { + String str; + PropertyInfo info; + }; + + struct UniformPropComparator { + bool operator()(const UniformProp &p_a, const UniformProp &p_b) const { + return p_a.str.naturalnocasecmp_to(p_b.str) < 0; + } + }; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -239,6 +253,7 @@ public: FLAG_INVERT_HEIGHTMAP, FLAG_SUBSURFACE_MODE_SKIN, FLAG_PARTICLE_TRAILS_MODE, + FLAG_ALBEDO_TEXTURE_MSDF, FLAG_MAX }; @@ -252,8 +267,6 @@ public: enum SpecularMode { SPECULAR_SCHLICK_GGX, - SPECULAR_BLINN, - SPECULAR_PHONG, SPECULAR_TOON, SPECULAR_DISABLED, SPECULAR_MAX @@ -321,6 +334,9 @@ private: memset(this, 0, sizeof(MaterialKey)); } + static uint32_t hash(const MaterialKey &p_key) { + return hash_djb2_buffer((const uint8_t *)&p_key, sizeof(MaterialKey)); + } bool operator==(const MaterialKey &p_key) const { return memcmp(this, &p_key, sizeof(MaterialKey)) == 0; } @@ -335,7 +351,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; @@ -387,7 +403,7 @@ private: StringName rim; StringName rim_tint; StringName clearcoat; - StringName clearcoat_gloss; + StringName clearcoat_roughness; StringName anisotropy; StringName heightmap_scale; StringName subsurface_scattering_strength; @@ -411,6 +427,8 @@ private: StringName uv2_blend_sharpness; StringName grow; StringName proximity_fade_distance; + StringName msdf_pixel_range; + StringName msdf_outline_size; StringName distance_fade_min; StringName distance_fade_max; StringName ao_light_affect; @@ -445,36 +463,36 @@ private: bool orm; Color albedo; - float specular; - float metallic; - float roughness; + float specular = 0.0f; + float metallic = 0.0f; + float roughness = 0.0f; Color emission; - float emission_energy; - float normal_scale; - float rim; - float rim_tint; - float clearcoat; - float clearcoat_gloss; - float anisotropy; - float heightmap_scale; - float subsurface_scattering_strength; - float transmittance_amount; + float emission_energy = 0.0f; + float normal_scale = 0.0f; + float rim = 0.0f; + float rim_tint = 0.0f; + float clearcoat = 0.0f; + float clearcoat_roughness = 0.0f; + float anisotropy = 0.0f; + float heightmap_scale = 0.0f; + float subsurface_scattering_strength = 0.0f; + float transmittance_amount = 0.0f; Color transmittance_color; - float transmittance_depth; - float transmittance_boost; + float transmittance_depth = 0.0f; + float transmittance_boost = 0.0f; Color backlight; - float refraction; - float point_size; - float alpha_scissor_threshold; - float alpha_hash_scale; - float alpha_antialiasing_edge; + float refraction = 0.0f; + float point_size = 0.0f; + float alpha_scissor_threshold = 0.0f; + float alpha_hash_scale = 0.0f; + float alpha_antialiasing_edge = 0.0f; bool grow_enabled = false; - float ao_light_affect; - float grow; - int particles_anim_h_frames; - int particles_anim_v_frames; - bool particles_anim_loop; + float ao_light_affect = 0.0f; + float grow = 0.0f; + int particles_anim_h_frames = 0; + int particles_anim_v_frames = 0; + bool particles_anim_loop = false; Transparency transparency = TRANSPARENCY_DISABLED; ShadingMode shading_mode = SHADING_MODE_PER_PIXEL; @@ -482,26 +500,29 @@ private: Vector3 uv1_scale; Vector3 uv1_offset; - float uv1_triplanar_sharpness; + float uv1_triplanar_sharpness = 0.0f; Vector3 uv2_scale; Vector3 uv2_offset; - float uv2_triplanar_sharpness; + float uv2_triplanar_sharpness = 0.0f; DetailUV detail_uv = DETAIL_UV_1; bool deep_parallax = false; - int deep_parallax_min_layers; - int deep_parallax_max_layers; + int deep_parallax_min_layers = 0; + int deep_parallax_max_layers = 0; bool heightmap_parallax_flip_tangent = false; bool heightmap_parallax_flip_binormal = false; bool proximity_fade_enabled = false; - float proximity_fade_distance; + float proximity_fade_distance = 0.0f; + + float msdf_pixel_range = 4.f; + float msdf_outline_size = 0.f; DistanceFadeMode distance_fade = DISTANCE_FADE_DISABLED; - float distance_fade_max_distance; - float distance_fade_min_distance; + float distance_fade_max_distance = 0.0f; + float distance_fade_min_distance = 0.0f; BlendMode blend_mode = BLEND_MODE_MIX; BlendMode detail_blend_mode = BLEND_MODE_MIX; @@ -526,9 +547,7 @@ private: _FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const; - static const int MAX_MATERIALS_FOR_2D = 128; - - static Ref<StandardMaterial3D> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff + static HashMap<uint64_t, Ref<StandardMaterial3D>> materials_for_2d; //used by Sprite3D, Label3D and other stuff void _validate_high_end(const String &text, PropertyInfo &property) const; @@ -572,8 +591,8 @@ public: void set_clearcoat(float p_clearcoat); float get_clearcoat() const; - void set_clearcoat_gloss(float p_clearcoat_gloss); - float get_clearcoat_gloss() const; + void set_clearcoat_roughness(float p_clearcoat_roughness); + float get_clearcoat_roughness() const; void set_anisotropy(float p_anisotropy); float get_anisotropy() const; @@ -713,6 +732,12 @@ public: void set_proximity_fade_distance(float p_distance); float get_proximity_fade_distance() const; + void set_msdf_pixel_range(float p_range); + float get_msdf_pixel_range() const; + + void set_msdf_outline_size(float p_size); + float get_msdf_outline_size() const; + void set_distance_fade(DistanceFadeMode p_mode); DistanceFadeMode get_distance_fade() const; @@ -738,7 +763,7 @@ public: static void finish_shaders(); static void flush_changes(); - 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); + 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, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; @@ -786,6 +811,13 @@ public: BaseMaterial3D(true) {} }; +class PlaceholderMaterial : public Material { + GDCLASS(PlaceholderMaterial, Material) +public: + virtual RID get_shader_rid() const override { return RID(); } + virtual Shader::Mode get_shader_mode() const override { return Shader::MODE_CANVAS_ITEM; } +}; + ////////////////////// -#endif +#endif // MATERIAL_H diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 441e84eccc..ec9db89794 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -40,37 +40,169 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; +int Mesh::get_surface_count() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret)) { + return ret; + } + return 0; +} + +int Mesh::surface_get_array_len(int p_idx) const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret)) { + return ret; + } + return 0; +} + +int Mesh::surface_get_array_index_len(int p_idx) const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret)) { + return ret; + } + return 0; +} + +Array Mesh::surface_get_arrays(int p_surface) const { + Array ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret)) { + return ret; + } + return Array(); +} + +Array Mesh::surface_get_blend_shape_arrays(int p_surface) const { + Array ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret)) { + return ret; + } + + return Array(); +} + +Dictionary Mesh::surface_get_lods(int p_surface) const { + Dictionary ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret)) { + return ret; + } + + return Dictionary(); +} + +uint32_t Mesh::surface_get_format(int p_idx) const { + uint32_t ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret)) { + return ret; + } + + return 0; +} + +Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const { + uint32_t ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret)) { + return (Mesh::PrimitiveType)ret; + } + + return PRIMITIVE_MAX; +} + +void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) { + if (GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material)) { + return; + } +} + +Ref<Material> Mesh::surface_get_material(int p_idx) const { + Ref<Material> ret; + if (GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret)) { + return ret; + } + + return Ref<Material>(); +} + +int Mesh::get_blend_shape_count() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret)) { + return ret; + } + + return 0; +} + +StringName Mesh::get_blend_shape_name(int p_index) const { + StringName ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret)) { + return ret; + } + + return StringName(); +} + +void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) { + if (GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name)) { + return; + } +} + +AABB Mesh::get_aabb() const { + AABB ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret)) { + return ret; + } + + return AABB(); +} + Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) { return triangle_mesh; } - int facecount = 0; + int faces_size = 0; for (int i = 0; i < get_surface_count(); i++) { - if (surface_get_primitive_type(i) != PRIMITIVE_TRIANGLES) { - continue; - } - - if (surface_get_format(i) & ARRAY_FORMAT_INDEX) { - facecount += surface_get_array_index_len(i); - } else { - facecount += surface_get_array_len(i); + switch (surface_get_primitive_type(i)) { + case PRIMITIVE_TRIANGLES: { + int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i); + // Don't error if zero, it's valid (we'll just skip it later). + ERR_CONTINUE_MSG((len % 3) != 0, vformat("Ignoring surface %d, incorrect %s count: %d (for PRIMITIVE_TRIANGLES).", i, (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? "index" : "vertex", len)); + faces_size += len; + } break; + case PRIMITIVE_TRIANGLE_STRIP: { + int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i); + // Don't error if zero, it's valid (we'll just skip it later). + ERR_CONTINUE_MSG(len != 0 && len < 3, vformat("Ignoring surface %d, incorrect %s count: %d (for PRIMITIVE_TRIANGLE_STRIP).", i, (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? "index" : "vertex", len)); + faces_size += (len == 0) ? 0 : (len - 2) * 3; + } break; + default: { + } break; } } - if (facecount == 0 || (facecount % 3) != 0) { + if (faces_size == 0) { return triangle_mesh; } Vector<Vector3> faces; - faces.resize(facecount); + faces.resize(faces_size); + Vector<int32_t> surface_indices; + surface_indices.resize(faces_size / 3); Vector3 *facesw = faces.ptrw(); + int32_t *surface_indicesw = surface_indices.ptrw(); int widx = 0; for (int i = 0; i < get_surface_count(); i++) { - if (surface_get_primitive_type(i) != PRIMITIVE_TRIANGLES) { + Mesh::PrimitiveType primitive = surface_get_primitive_type(i); + if (primitive != PRIMITIVE_TRIANGLES && primitive != PRIMITIVE_TRIANGLE_STRIP) { + continue; + } + int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i); + if ((primitive == PRIMITIVE_TRIANGLES && (len == 0 || (len % 3) != 0)) || (primitive == PRIMITIVE_TRIANGLE_STRIP && len < 3)) { + // Error was already shown, just skip (including zero). continue; } @@ -81,21 +213,45 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { Vector<Vector3> vertices = a[ARRAY_VERTEX]; const Vector3 *vr = vertices.ptr(); + int32_t from_index = widx / 3; + if (surface_get_format(i) & ARRAY_FORMAT_INDEX) { int ic = surface_get_array_index_len(i); Vector<int> indices = a[ARRAY_INDEX]; const int *ir = indices.ptr(); - for (int j = 0; j < ic; j++) { - int index = ir[j]; - facesw[widx++] = vr[index]; + if (primitive == PRIMITIVE_TRIANGLES) { + for (int j = 0; j < ic; j++) { + int index = ir[j]; + facesw[widx++] = vr[index]; + } + } else { // PRIMITIVE_TRIANGLE_STRIP + for (int j = 2; j < ic; j++) { + facesw[widx++] = vr[ir[j - 2]]; + facesw[widx++] = vr[ir[j - 1]]; + facesw[widx++] = vr[ir[j]]; + } } } else { - for (int j = 0; j < vc; j++) { - facesw[widx++] = vr[j]; + if (primitive == PRIMITIVE_TRIANGLES) { + for (int j = 0; j < vc; j++) { + facesw[widx++] = vr[j]; + } + } else { // PRIMITIVE_TRIANGLE_STRIP + for (int j = 2; j < vc; j++) { + facesw[widx++] = vr[j - 2]; + facesw[widx++] = vr[j - 1]; + facesw[widx++] = vr[j]; + } } } + + int32_t to_index = widx / 3; + + for (int j = from_index; j < to_index; j++) { + surface_indicesw[j] = i; + } } triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); @@ -104,6 +260,64 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { return triangle_mesh; } +Ref<TriangleMesh> Mesh::generate_surface_triangle_mesh(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, get_surface_count(), Ref<TriangleMesh>()); + + if (surface_triangle_meshes.size() != get_surface_count()) { + surface_triangle_meshes.resize(get_surface_count()); + } + + if (surface_triangle_meshes[p_surface].is_valid()) { + return surface_triangle_meshes[p_surface]; + } + + int facecount = 0; + + if (surface_get_primitive_type(p_surface) != PRIMITIVE_TRIANGLES) { + return Ref<TriangleMesh>(); + } + + if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) { + facecount += surface_get_array_index_len(p_surface); + } else { + facecount += surface_get_array_len(p_surface); + } + + Vector<Vector3> faces; + faces.resize(facecount); + Vector3 *facesw = faces.ptrw(); + + Array a = surface_get_arrays(p_surface); + ERR_FAIL_COND_V(a.is_empty(), Ref<TriangleMesh>()); + + int vc = surface_get_array_len(p_surface); + Vector<Vector3> vertices = a[ARRAY_VERTEX]; + const Vector3 *vr = vertices.ptr(); + int widx = 0; + + if (surface_get_format(p_surface) & ARRAY_FORMAT_INDEX) { + int ic = surface_get_array_index_len(p_surface); + Vector<int> indices = a[ARRAY_INDEX]; + const int *ir = indices.ptr(); + + for (int j = 0; j < ic; j++) { + int index = ir[j]; + facesw[widx++] = vr[index]; + } + + } else { + for (int j = 0; j < vc; j++) { + facesw[widx++] = vr[j]; + } + } + + Ref<TriangleMesh> triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh->create(faces); + surface_triangle_meshes.set(p_surface, triangle_mesh); + + return triangle_mesh; +} + void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { if (debug_lines.size() > 0) { r_lines = debug_lines; @@ -164,6 +378,14 @@ Vector<Face3> Mesh::get_faces() const { return Vector<Face3>(); } +Vector<Face3> Mesh::get_surface_faces(int p_surface) const { + Ref<TriangleMesh> tm = generate_surface_triangle_mesh(p_surface); + if (tm.is_valid()) { + return tm->get_faces(); + } + return Vector<Face3>(); +} + Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { if (p_simplify) { ConvexDecompositionSettings settings; @@ -339,7 +561,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { has_indices = true; } - Map<Vector3, Vector3> normal_accum; + HashMap<Vector3, Vector3> normal_accum; //fill normals with triangle normals for (int i = 0; i < vc; i += 3) { @@ -358,13 +580,13 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { Vector3 n = Plane(t[0], t[1], t[2]).normal; for (int j = 0; j < 3; j++) { - Map<Vector3, Vector3>::Element *E = normal_accum.find(t[j]); + HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t[j]); if (!E) { normal_accum[t[j]] = n; } else { - float d = n.dot(E->get()); + float d = n.dot(E->value); if (d < 1.0) { - E->get() += n * (1.0 - d); + E->value += n * (1.0 - d); } //E->get()+=n; } @@ -383,10 +605,10 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { for (int i = 0; i < vc2; i++) { Vector3 t = r[i]; - Map<Vector3, Vector3>::Element *E = normal_accum.find(t); + HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t); ERR_CONTINUE(!E); - t += E->get() * p_margin; + t += E->value * p_margin; r[i] = t; } @@ -502,6 +724,21 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); + + GDVIRTUAL_BIND(_get_surface_count) + GDVIRTUAL_BIND(_surface_get_array_len, "index") + GDVIRTUAL_BIND(_surface_get_array_index_len, "index") + GDVIRTUAL_BIND(_surface_get_arrays, "index") + GDVIRTUAL_BIND(_surface_get_blend_shape_arrays, "index") + GDVIRTUAL_BIND(_surface_get_lods, "index") + GDVIRTUAL_BIND(_surface_get_format, "index") + GDVIRTUAL_BIND(_surface_get_primitive_type, "index") + GDVIRTUAL_BIND(_surface_set_material, "index", "material") + GDVIRTUAL_BIND(_surface_get_material, "index") + GDVIRTUAL_BIND(_get_blend_shape_count) + GDVIRTUAL_BIND(_get_blend_shape_name, "index") + GDVIRTUAL_BIND(_set_blend_shape_name, "index", "name") + GDVIRTUAL_BIND(_get_aabb) } void Mesh::clear_cache() const { @@ -1684,6 +1921,7 @@ Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_te 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."); + ERR_FAIL_COND_V_MSG(p_texel_size <= 0.0f, ERR_PARAMETER_RANGE_ERROR, "Texel size must be greater than 0."); LocalVector<float> vertices; LocalVector<float> normals; @@ -1695,7 +1933,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, flo // Keep only the scale 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()); + Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length()); Transform3D transform; transform.scale(scale); @@ -1935,7 +2173,7 @@ void ArrayMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces"); ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode"); - ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh"); } @@ -1960,3 +2198,17 @@ ArrayMesh::~ArrayMesh() { RenderingServer::get_singleton()->free(mesh); } } +/////////////// + +void PlaceholderMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aabb", "aabb"), &PlaceholderMesh::set_aabb); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_aabb", "get_aabb"); +} + +PlaceholderMesh::PlaceholderMesh() { + rid = RS::get_singleton()->mesh_create(); +} + +PlaceholderMesh::~PlaceholderMesh() { + RS::get_singleton()->free(rid); +} diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 08d834bdb9..142373ce7f 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -42,12 +42,38 @@ class Mesh : public Resource { GDCLASS(Mesh, Resource); mutable Ref<TriangleMesh> triangle_mesh; //cached + mutable Vector<Ref<TriangleMesh>> surface_triangle_meshes; //cached mutable Vector<Vector3> debug_lines; Size2i lightmap_size_hint; +public: + enum PrimitiveType { + PRIMITIVE_POINTS = RenderingServer::PRIMITIVE_POINTS, + PRIMITIVE_LINES = RenderingServer::PRIMITIVE_LINES, + PRIMITIVE_LINE_STRIP = RenderingServer::PRIMITIVE_LINE_STRIP, + PRIMITIVE_TRIANGLES = RenderingServer::PRIMITIVE_TRIANGLES, + PRIMITIVE_TRIANGLE_STRIP = RenderingServer::PRIMITIVE_TRIANGLE_STRIP, + PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX, + }; + protected: static void _bind_methods(); + GDVIRTUAL0RC(int, _get_surface_count) + GDVIRTUAL1RC(int, _surface_get_array_len, int) + GDVIRTUAL1RC(int, _surface_get_array_index_len, int) + GDVIRTUAL1RC(Array, _surface_get_arrays, int) + GDVIRTUAL1RC(Array, _surface_get_blend_shape_arrays, int) + GDVIRTUAL1RC(Dictionary, _surface_get_lods, int) + GDVIRTUAL1RC(uint32_t, _surface_get_format, int) + GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int) + GDVIRTUAL2(_surface_set_material, int, Ref<Material>) + GDVIRTUAL1RC(Ref<Material>, _surface_get_material, int) + GDVIRTUAL0RC(int, _get_blend_shape_count) + GDVIRTUAL1RC(StringName, _get_blend_shape_name, int) + GDVIRTUAL2(_set_blend_shape_name, int, StringName) + GDVIRTUAL0RC(AABB, _get_aabb) + public: enum { NO_INDEX_ARRAY = RenderingServer::NO_INDEX_ARRAY, @@ -120,31 +146,25 @@ public: }; - enum PrimitiveType { - PRIMITIVE_POINTS = RenderingServer::PRIMITIVE_POINTS, - PRIMITIVE_LINES = RenderingServer::PRIMITIVE_LINES, - PRIMITIVE_LINE_STRIP = RenderingServer::PRIMITIVE_LINE_STRIP, - PRIMITIVE_TRIANGLES = RenderingServer::PRIMITIVE_TRIANGLES, - PRIMITIVE_TRIANGLE_STRIP = RenderingServer::PRIMITIVE_TRIANGLE_STRIP, - PRIMITIVE_MAX = RenderingServer::PRIMITIVE_MAX, - }; - - virtual int get_surface_count() const = 0; - virtual int surface_get_array_len(int p_idx) const = 0; - virtual int surface_get_array_index_len(int p_idx) const = 0; - virtual Array surface_get_arrays(int p_surface) const = 0; - virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; - virtual Dictionary surface_get_lods(int p_surface) const = 0; - virtual uint32_t surface_get_format(int p_idx) const = 0; - virtual PrimitiveType surface_get_primitive_type(int p_idx) const = 0; - virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) = 0; - virtual Ref<Material> surface_get_material(int p_idx) const = 0; - virtual int get_blend_shape_count() const = 0; - virtual StringName get_blend_shape_name(int p_index) const = 0; - virtual void set_blend_shape_name(int p_index, const StringName &p_name) = 0; + virtual int get_surface_count() const; + virtual int surface_get_array_len(int p_idx) const; + virtual int surface_get_array_index_len(int p_idx) const; + virtual Array surface_get_arrays(int p_surface) const; + virtual Array surface_get_blend_shape_arrays(int p_surface) const; + virtual Dictionary surface_get_lods(int p_surface) const; + virtual uint32_t surface_get_format(int p_idx) const; + virtual PrimitiveType surface_get_primitive_type(int p_idx) const; + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material); + virtual Ref<Material> surface_get_material(int p_idx) const; + virtual int get_blend_shape_count() const; + virtual StringName get_blend_shape_name(int p_index) const; + virtual void set_blend_shape_name(int p_index, const StringName &p_name); + virtual AABB get_aabb() const; Vector<Face3> get_faces() const; + Vector<Face3> get_surface_faces(int p_surface) const; Ref<TriangleMesh> generate_triangle_mesh() const; + Ref<TriangleMesh> generate_surface_triangle_mesh(int p_surface) const; void generate_debug_mesh_lines(Vector<Vector3> &r_lines); void generate_debug_mesh_indices(Vector<Vector3> &r_points); @@ -153,8 +173,6 @@ public: Ref<Mesh> create_outline(float p_margin) const; - virtual AABB get_aabb() const = 0; - void set_lightmap_size_hint(const Size2i &p_size); Size2i get_lightmap_size_hint() const; void clear_cache() const; @@ -313,4 +331,38 @@ VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat); VARIANT_ENUM_CAST(Mesh::PrimitiveType); VARIANT_ENUM_CAST(Mesh::BlendShapeMode); -#endif +class PlaceholderMesh : public Mesh { + GDCLASS(PlaceholderMesh, Mesh); + + RID rid; + AABB aabb; + +protected: + static void _bind_methods(); + +public: + virtual int get_surface_count() const override { return 0; } + virtual int surface_get_array_len(int p_idx) const override { return 0; } + virtual int surface_get_array_index_len(int p_idx) const override { return 0; } + virtual Array surface_get_arrays(int p_surface) const override { return Array(); } + virtual Array surface_get_blend_shape_arrays(int p_surface) const override { return Array(); } + virtual Dictionary surface_get_lods(int p_surface) const override { return Dictionary(); } + virtual uint32_t surface_get_format(int p_idx) const override { return 0; } + virtual PrimitiveType surface_get_primitive_type(int p_idx) const override { return PRIMITIVE_TRIANGLES; } + 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 { return Ref<Material>(); } + virtual int get_blend_shape_count() const override { return 0; } + virtual StringName get_blend_shape_name(int p_index) const override { return StringName(); } + virtual void set_blend_shape_name(int p_index, const StringName &p_name) override {} + virtual RID get_rid() const override { return rid; } + virtual AABB get_aabb() const override { return aabb; } + void set_aabb(const AABB &p_aabb) { aabb = p_aabb; } + + virtual int get_builtin_bind_pose_count() const override { return 0; } + virtual Transform3D get_builtin_bind_pose(int p_index) const override { return Transform3D(); } + + PlaceholderMesh(); + ~PlaceholderMesh(); +}; + +#endif // MESH_H diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 594f723a1d..33d63adc71 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -150,7 +150,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf vertices.write[i] = v; } - Map<Point2i, int> edge_indices; + HashMap<Point2i, int> edge_indices; for (int i = 0; i < icount; i += 3) { Vertex *v[3] = { &vertices.write[r[i + 0]], &vertices.write[r[i + 1]], &vertices.write[r[i + 2]] }; diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 5168bf83eb..2d3f9d9afc 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -100,14 +100,14 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { for (const KeyValue<int, Item> &E : item_map) { - 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::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::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)); + String name = vformat("%s/%d/", PNAME("item"), E.key); + p_list->push_back(PropertyInfo(Variant::STRING, name + PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::ARRAY, name + PNAME("shapes"))); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("navmesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + PNAME("navmesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT)); } } diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index e0f2ab2114..4105bd6960 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -32,7 +32,7 @@ #define MESH_LIBRARY_H #include "core/io/resource.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "mesh.h" #include "scene/3d/navigation_region_3d.h" #include "shape_3d.h" @@ -56,7 +56,7 @@ public: Ref<NavigationMesh> navmesh; }; - Map<int, Item> item_map; + RBMap<int, Item> item_map; void _set_item_shapes(int p_item, const Array &p_shapes); Array _get_item_shapes(int p_item) const; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index c30e748f66..e5fc61ade5 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -101,9 +101,9 @@ void MultiMesh::_set_transform_2d_array(const Vector<Vector2> &p_array) { for (int i = 0; i < len / 3; i++) { Transform2D t; - t.elements[0] = r[i * 3 + 0]; - t.elements[1] = r[i * 3 + 1]; - t.elements[2] = r[i * 3 + 2]; + t.columns[0] = r[i * 3 + 0]; + t.columns[1] = r[i * 3 + 1]; + t.columns[2] = r[i * 3 + 2]; set_instance_transform_2d(i, t); } @@ -125,9 +125,9 @@ Vector<Vector2> MultiMesh::_get_transform_2d_array() const { for (int i = 0; i < instance_count; i++) { Transform2D t = get_instance_transform_2d(i); - w[i * 3 + 0] = t.elements[0]; - w[i * 3 + 1] = t.elements[1]; - w[i * 3 + 2] = t.elements[2]; + w[i * 3 + 0] = t.columns[0]; + w[i * 3 + 1] = t.columns[1]; + w[i * 3 + 2] = t.columns[2]; } return xforms; diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index 30ada5365f..0f8cc76173 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -113,4 +113,4 @@ public: VARIANT_ENUM_CAST(MultiMesh::TransformFormat); -#endif // MULTI_MESH_H +#endif // MULTIMESH_H diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 47a87bdea5..a808ead66b 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -38,6 +38,7 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + WARN_PRINT("A mesh surface was skipped when creating a NavigationMesh due to wrong primitive type in the source mesh. Mesh surface must be made out of triangles."); continue; } Array arr = p_mesh->surface_get_arrays(i); @@ -46,6 +47,7 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX]; Vector<int> iarr = arr[Mesh::ARRAY_INDEX]; if (varr.size() == 0 || iarr.size() == 0) { + WARN_PRINT("A mesh surface was skipped when creating a NavigationMesh due to an empty vertex or index array."); continue; } @@ -229,7 +231,7 @@ float NavigationMesh::get_verts_per_poly() const { } void NavigationMesh::set_detail_sample_distance(float p_value) { - ERR_FAIL_COND(p_value < 0); + ERR_FAIL_COND(p_value < 0.1); detail_sample_distance = p_value; } @@ -270,6 +272,24 @@ bool NavigationMesh::get_filter_walkable_low_height_spans() const { return filter_walkable_low_height_spans; } +void NavigationMesh::set_filter_baking_aabb(const AABB &p_aabb) { + filter_baking_aabb = p_aabb; + notify_property_list_changed(); +} + +AABB NavigationMesh::get_filter_baking_aabb() const { + return filter_baking_aabb; +} + +void NavigationMesh::set_filter_baking_aabb_offset(const Vector3 &p_aabb_offset) { + filter_baking_aabb_offset = p_aabb_offset; + notify_property_list_changed(); +} + +Vector3 NavigationMesh::get_filter_baking_aabb_offset() const { + return filter_baking_aabb_offset; +} + void NavigationMesh::set_vertices(const Vector<Vector3> &p_vertices) { vertices = p_vertices; notify_property_list_changed(); @@ -338,7 +358,7 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { } } - Map<_EdgeKey, bool> edge_map; + HashMap<_EdgeKey, bool, _EdgeKey> edge_map; Vector<Vector3> tmeshfaces; tmeshfaces.resize(faces.size() * 3); @@ -356,10 +376,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { SWAP(ek.from, ek.to); } - Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); + HashMap<_EdgeKey, bool, _EdgeKey>::Iterator F = edge_map.find(ek); if (F) { - F->get() = false; + F->value = false; } else { edge_map[ek] = true; @@ -388,6 +408,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (!lines.size()) { + return debug_mesh; + } + Array arr; arr.resize(Mesh::ARRAY_MAX); arr[Mesh::ARRAY_VERTEX] = varr; @@ -463,6 +487,10 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_filter_walkable_low_height_spans", "filter_walkable_low_height_spans"), &NavigationMesh::set_filter_walkable_low_height_spans); ClassDB::bind_method(D_METHOD("get_filter_walkable_low_height_spans"), &NavigationMesh::get_filter_walkable_low_height_spans); + ClassDB::bind_method(D_METHOD("set_filter_baking_aabb", "baking_aabb"), &NavigationMesh::set_filter_baking_aabb); + ClassDB::bind_method(D_METHOD("get_filter_baking_aabb"), &NavigationMesh::get_filter_baking_aabb); + ClassDB::bind_method(D_METHOD("set_filter_baking_aabb_offset", "baking_aabb_offset"), &NavigationMesh::set_filter_baking_aabb_offset); + ClassDB::bind_method(D_METHOD("get_filter_baking_aabb_offset"), &NavigationMesh::get_filter_baking_aabb_offset); ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationMesh::set_vertices); ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationMesh::get_vertices); @@ -480,29 +508,38 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/source_geometry_mode", PROPERTY_HINT_ENUM, "Navmesh Children, Group With Children, Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry/source_group_name"), "set_source_group_name", "get_source_group_name"); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent/height", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_height", "get_agent_height"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent/radius", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_radius", "get_agent_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent/max_climb", PROPERTY_HINT_RANGE, "0.1,5.0,0.01,or_greater"), "set_agent_max_climb", "get_agent_max_climb"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent/max_slope", PROPERTY_HINT_RANGE, "0.0,90.0,0.1"), "set_agent_max_slope", "get_agent_max_slope"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "region/min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_min_size", "get_region_min_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "region/merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_merge_size", "get_region_merge_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater"), "set_edge_max_length", "get_edge_max_length"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater"), "set_edge_max_error", "get_edge_max_error"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_max_error", "get_detail_sample_max_error"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans"); + ADD_GROUP("Sampling", "sample_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); + ADD_GROUP("Geometry", "geometry_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "NavMesh Children, Group With Children, Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry_source_group_name"), "set_source_group_name", "get_source_group_name"); + ADD_GROUP("Cells", "cell_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_height", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_height", "get_cell_height"); + ADD_GROUP("Agents", "agent_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:m"), "set_agent_height", "get_agent_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_radius", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:m"), "set_agent_radius", "get_agent_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_max_climb", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:m"), "set_agent_max_climb", "get_agent_max_climb"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_max_slope", PROPERTY_HINT_RANGE, "0.02,90.0,0.01,degrees"), "set_agent_max_slope", "get_agent_max_slope"); + ADD_GROUP("Regions", "region_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "region_min_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_min_size", "get_region_min_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "region_merge_size", PROPERTY_HINT_RANGE, "0.0,150.0,0.01,or_greater"), "set_region_merge_size", "get_region_merge_size"); + ADD_GROUP("Edges", "edge_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater,suffix:m"), "set_edge_max_length", "get_edge_max_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater,suffix:m"), "set_edge_max_error", "get_edge_max_error"); + ADD_GROUP("Polygons", "polygon_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon_verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); + ADD_GROUP("Details", "detail_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_distance", PROPERTY_HINT_RANGE, "0.1,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_distance", "get_detail_sample_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_max_error", "get_detail_sample_max_error"); + ADD_GROUP("Filters", "filter_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans"); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "filter_baking_aabb"), "set_filter_baking_aabb", "get_filter_baking_aabb"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "filter_baking_aabb_offset"), "set_filter_baking_aabb_offset", "get_filter_baking_aabb_offset"); BIND_ENUM_CONSTANT(SAMPLE_PARTITION_WATERSHED); BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MONOTONE); @@ -536,4 +573,41 @@ void NavigationMesh::_validate_property(PropertyInfo &property) const { } } +#ifndef DISABLE_DEPRECATED +bool NavigationMesh::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + if (name.find("/") != -1) { + // Compatibility with pre-3.5 "category/path" property names. + name = name.replace("/", "_"); + if (name == "sample_partition_type_sample_partition_type") { + set("sample_partition_type", p_value); + } else if (name == "filter_filter_walkable_low_height_spans") { + set("filter_walkable_low_height_spans", p_value); + } else { + set(name, p_value); + } + + return true; + } + return false; +} + +bool NavigationMesh::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + if (name.find("/") != -1) { + // Compatibility with pre-3.5 "category/path" property names. + name = name.replace("/", "_"); + if (name == "sample_partition_type_sample_partition_type") { + r_ret = get("sample_partition_type"); + } else if (name == "filter_filter_walkable_low_height_spans") { + r_ret = get("filter_walkable_low_height_spans"); + } else { + r_ret = get(name); + } + return true; + } + return false; +} +#endif // DISABLE_DEPRECATED + NavigationMesh::NavigationMesh() {} diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index e43e8627e4..40b275c792 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -49,13 +49,24 @@ class NavigationMesh : public Resource { Vector3 from; Vector3 to; - bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } + static uint32_t hash(const _EdgeKey &p_key) { + return HashMapHasherDefault::hash(p_key.from) ^ HashMapHasherDefault::hash(p_key.to); + } + + bool operator==(const _EdgeKey &p_with) const { + return HashMapComparatorDefault<Vector3>::compare(from, p_with.from) && HashMapComparatorDefault<Vector3>::compare(to, p_with.to); + } }; protected: static void _bind_methods(); virtual void _validate_property(PropertyInfo &property) const override; +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; +#endif // DISABLE_DEPRECATED + void _set_polygons(const Array &p_array); Array _get_polygons() const; @@ -82,13 +93,13 @@ public: }; protected: - float cell_size = 0.3f; - float cell_height = 0.2f; - float agent_height = 2.0f; - float agent_radius = 1.0f; - float agent_max_climb = 0.9f; + float cell_size = 0.25f; + float cell_height = 0.25f; + float agent_height = 1.5f; + float agent_radius = 0.5f; + float agent_max_climb = 0.25f; float agent_max_slope = 45.0f; - float region_min_size = 8.0f; + float region_min_size = 2.0f; float region_merge_size = 20.0f; float edge_max_length = 12.0f; float edge_max_error = 1.3f; @@ -106,6 +117,8 @@ protected: bool filter_low_hanging_obstacles = false; bool filter_ledge_spans = false; bool filter_walkable_low_height_spans = false; + AABB filter_baking_aabb; + Vector3 filter_baking_aabb_offset; public: // Recast settings @@ -175,6 +188,12 @@ public: void set_filter_walkable_low_height_spans(bool p_value); bool get_filter_walkable_low_height_spans() const; + void set_filter_baking_aabb(const AABB &p_aabb); + AABB get_filter_baking_aabb() const; + + void set_filter_baking_aabb_offset(const Vector3 &p_aabb_offset); + Vector3 get_filter_baking_aabb_offset() const; + void create_from_mesh(const Ref<Mesh> &p_mesh); void set_vertices(const Vector<Vector3> &p_vertices); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 9d388d465d..2c58aa83a9 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -33,29 +33,28 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/core_string_names.h" +#include "core/io/missing_resource.h" #include "core/io/resource_loader.h" -#include "editor/editor_inspector.h" +#include "core/templates/local_vector.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/property_utils.h" #define PACKED_SCENE_VERSION 2 - +#define META_POINTER_PROPERTY_BASE "metadata/_editor_prop_ptr_" bool SceneState::can_instantiate() const { return nodes.size() > 0; } static Array _sanitize_node_pinned_properties(Node *p_node) { - if (!p_node->has_meta("_edit_pinned_properties_")) { - return Array(); - } - Array pinned = p_node->get_meta("_edit_pinned_properties_"); + Array pinned = p_node->get_meta("_edit_pinned_properties_", Array()); if (pinned.is_empty()) { return Array(); } - Set<StringName> storable_properties; + HashSet<StringName> storable_properties; p_node->get_storable_properties(storable_properties); int i = 0; do { @@ -108,12 +107,15 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty(); - Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene; + HashMap<Ref<Resource>, Ref<Resource>> resources_local_to_scene; + + LocalVector<DeferredNodePathProperties> deferred_node_paths; for (int i = 0; i < nc; i++) { const NodeData &n = nd[i]; Node *parent = nullptr; + String old_parent_path; if (i > 0) { ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])); @@ -121,6 +123,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); + nparent = ret_nodes[0]; } #endif parent = nparent; @@ -131,6 +135,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } Node *node = nullptr; + MissingNode *missing_node = nullptr; if (i == 0 && base_scene_idx >= 0) { //scene inheritance on root node @@ -185,24 +190,33 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { memdelete(obj); obj = nullptr; } - WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); - if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { - if (Object::cast_to<Control>(ret_nodes[n.parent])) { - obj = memnew(Control); - } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { - obj = memnew(Node2D); + + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_node = memnew(MissingNode); + missing_node->set_original_class(snames[n.type]); + missing_node->set_recording_properties(true); + node = missing_node; + obj = missing_node; + } else { + WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); + if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { + if (Object::cast_to<Control>(ret_nodes[n.parent])) { + obj = memnew(Control); + } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { + obj = memnew(Node2D); #ifndef _3D_DISABLED - } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { - obj = memnew(Node3D); + } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { + obj = memnew(Node3D); #endif // _3D_DISABLED + } } - } - if (!obj) { - obj = memnew(Node); - } + if (!obj) { + obj = memnew(Node); + } - node = Object::cast_to<Node>(obj); + node = Object::cast_to<Node>(obj); + } } } @@ -215,11 +229,32 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (nprop_count) { const NodeData::Property *nprops = &n.properties[0]; + Dictionary missing_resource_properties; + for (int j = 0; j < nprop_count; j++) { bool valid; - ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); + ERR_FAIL_INDEX_V(nprops[j].value, prop_count, nullptr); + if (nprops[j].name & FLAG_PATH_PROPERTY_IS_NODE) { + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); + ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); + if (Engine::get_singleton()->is_editor_hint()) { + // If editor, just set the metadata and be it + node->set(META_POINTER_PROPERTY_BASE + String(snames[name_idx]), props[nprops[j].value]); + } else { + // Do an actual deferred sed of the property path. + DeferredNodePathProperties dnp; + dnp.path = props[nprops[j].value]; + dnp.base = node; + dnp.property = snames[name_idx]; + deferred_node_paths.push_back(dnp); + } + continue; + } + + ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); + if (snames[nprops[j].name] == CoreStringNames::get_singleton()->_script) { //work around to avoid old script variables from disappearing, should be the proper fix to: //https://github.com/godotengine/godot/issues/2958 @@ -244,10 +279,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.find(res); + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); if (E) { - value = E->get(); + value = E->value; } else { Node *base = i == 0 ? node : ret_nodes[0]; @@ -271,9 +306,24 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } - node->set(snames[nprops[j].name], value, &valid); + + bool set_valid = true; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled() && value.get_type() == Variant::OBJECT) { + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[snames[nprops[j].name]] = mr; + set_valid = false; + } + } + + if (set_valid) { + node->set(snames[nprops[j].name], value, &valid); + } } } + if (!missing_resource_properties.is_empty()) { + node->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } } //name @@ -307,10 +357,17 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (!old_parent_path.is_empty()) { + node->_set_name_nocheck(old_parent_path + "@" + node->get_name()); + } + if (n.owner >= 0) { NODE_FROM_ID(owner, n.owner); if (owner) { node->_set_owner_nocheck(owner); + if (node->data.unique_name_in_owner) { + node->_acquire_unique_name_in_owner(); + } } } @@ -322,6 +379,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (missing_node) { + missing_node->set_recording_properties(false); + } + ret_nodes[i] = node; if (node && gen_node_path_cache && ret_nodes[0]) { @@ -330,6 +391,12 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + for (uint32_t i = 0; i < deferred_node_paths.size(); i++) { + const DeferredNodePathProperties &dnp = deferred_node_paths[i]; + Node *other = dnp.base->get_node_or_null(dnp.path); + dnp.base->set(dnp.property, other); + } + for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { E.value->setup_local_to_scene(); } @@ -363,8 +430,11 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - const Variant *args = binds.ptr(); - callable = callable.bind(&args, binds.size()); + const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * binds.size()); + for (int j = 0; j < binds.size(); j++) { + argptrs[j] = &binds[j]; + } + callable = callable.bind(argptrs, binds.size()); } cfrom->connect(snames[c.signal], callable, varray(), CONNECT_PERSIST | c.flags); @@ -388,7 +458,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { return ret_nodes[0]; } -static int _nm_get_string(const String &p_string, Map<StringName, int> &name_map) { +static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) { if (name_map.has(p_string)) { return name_map[p_string]; } @@ -408,7 +478,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia return idx; } -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) { +Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { // this function handles all the work related to properly packing scenes, be it // instantiated or inherited. // given the complexity of this process, an attempt will be made to properly @@ -480,33 +550,46 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map p_node->get_property_list(&plist); Array pinned_props = _sanitize_node_pinned_properties(p_node); + Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary()); for (const PropertyInfo &E : plist) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant forced_value; + if (E.name == META_PROPERTY_MISSING_RESOURCES) { + continue; // Ignore this property when packing. + } + if (E.name.begins_with(META_POINTER_PROPERTY_BASE)) { + continue; // do not save. + } - // If instance or inheriting, not saving if property requested so, or it's meta - if (states_stack.size()) { + // If instance or inheriting, not saving if property requested so. + if (!states_stack.is_empty()) { if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { continue; } - // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list - if (E.name == "__meta__") { - if (pinned_props.size()) { - Dictionary meta_override; - meta_override["_edit_pinned_properties_"] = pinned_props; - forced_value = meta_override; - } - } } StringName name = E.name; - Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value; + Variant value = p_node->get(name); + bool use_deferred_node_path_bit = false; - if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) { + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + value = p_node->get(META_POINTER_PROPERTY_BASE + E.name); + if (value.get_type() != Variant::NODE_PATH) { + continue; //was never set, ignore. + } + use_deferred_node_path_bit = true; + } else if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) { + // Was this missing resource overridden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[E.name]; + } + } + + if (!pinned_props.has(name)) { bool is_valid_default = false; Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true); if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) { @@ -517,6 +600,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map NodeData::Property prop; prop.name = _nm_get_string(name, name_map); prop.value = _vm_get_variant(value, variant_map); + if (use_deferred_node_path_bit) { + prop.name |= FLAG_PATH_PROPERTY_IS_NODE; + } nd.properties.push_back(prop); } @@ -561,11 +647,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.owner = -1; } + MissingNode *missing_node = Object::cast_to<MissingNode>(p_node); + // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type - nd.type = _nm_get_string(p_node->get_class(), name_map); + if (missing_node != nullptr) { + // It's a missing node (type non existent on load). + nd.type = _nm_get_string(missing_node->get_original_class(), name_map); + } else { + nd.type = _nm_get_string(p_node->get_class(), name_map); + } } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated @@ -620,7 +713,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } -Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { +Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { if (p_node != p_owner && p_node->get_owner() && p_node->get_owner() != p_owner && !p_owner->is_editable_instance(p_node->get_owner())) { return OK; } @@ -827,10 +920,10 @@ Error SceneState::pack(Node *p_scene) { Node *scene = p_scene; - Map<StringName, int> name_map; + HashMap<StringName, int> name_map; HashMap<Variant, int, VariantHasher, VariantComparator> variant_map; - Map<Node *, int> node_map; - Map<Node *, int> nodepath_map; + HashMap<Node *, int> node_map; + HashMap<Node *, int> nodepath_map; // If using scene inheritance, pack the scene it inherits from. if (scene->get_scene_inherited_state().is_valid()) { @@ -861,10 +954,10 @@ Error SceneState::pack(Node *p_scene) { } variants.resize(variant_map.size()); - const Variant *K = nullptr; - while ((K = variant_map.next(K))) { - int idx = variant_map[*K]; - variants.write[idx] = *K; + + for (const KeyValue<Variant, int> &E : variant_map) { + int idx = E.value; + variants.write[idx] = E.key; } node_paths.resize(nodepath_map.size()); @@ -966,7 +1059,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property, const NodeData::Property *p = nodes[p_node].properties.ptr(); for (int i = 0; i < pc; i++) { - if (p_property == namep[p[i].name]) { + if (p_property == namep[p[i].name & FLAG_PROP_NAME_MASK]) { found = true; return variants[p[i].value]; } @@ -1357,7 +1450,19 @@ int SceneState::get_node_property_count(int p_idx) const { StringName SceneState::get_node_property_name(int p_idx, int p_prop) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName()); ERR_FAIL_INDEX_V(p_prop, nodes[p_idx].properties.size(), StringName()); - return names[nodes[p_idx].properties[p_prop].name]; + return names[nodes[p_idx].properties[p_prop].name & FLAG_PROP_NAME_MASK]; +} + +Vector<String> SceneState::get_node_deferred_nodepath_properties(int p_idx) const { + Vector<String> ret; + ERR_FAIL_INDEX_V(p_idx, nodes.size(), ret); + for (int i = 0; i < nodes[p_idx].properties.size(); i++) { + uint32_t idx = nodes[p_idx].properties[i].name; + if (idx & FLAG_PATH_PROPERTY_IS_NODE) { + ret.push_back(names[idx & FLAG_PROP_NAME_MASK]); + } + } + return ret; } Variant SceneState::get_node_property_value(int p_idx, int p_prop) const { @@ -1503,13 +1608,16 @@ int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int return nodes.size() - 1; } -void SceneState::add_node_property(int p_node, int p_name, int p_value) { +void SceneState::add_node_property(int p_node, int p_name, int p_value, bool p_deferred_node_path) { ERR_FAIL_INDEX(p_node, nodes.size()); ERR_FAIL_INDEX(p_name, names.size()); ERR_FAIL_INDEX(p_value, variants.size()); NodeData::Property prop; prop.name = p_name; + if (p_deferred_node_path) { + prop.name |= FLAG_PATH_PROPERTY_IS_NODE; + } prop.value = p_value; nodes.write[p_node].properties.push_back(prop); } @@ -1547,6 +1655,10 @@ void SceneState::add_editable_instance(const NodePath &p_path) { editable_instances.push_back(p_path); } +String SceneState::get_meta_pointer_property(const String &p_property) { + return META_POINTER_PROPERTY_BASE + p_property; +} + Vector<String> SceneState::_get_node_groups(int p_idx) const { Vector<StringName> groups = get_node_groups(p_idx); Vector<String> ret; @@ -1653,7 +1765,7 @@ void PackedScene::recreate_state() { #endif } -Ref<SceneState> PackedScene::get_state() { +Ref<SceneState> PackedScene::get_state() const { return state; } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 81b38840d9..8e1a1d29b6 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -42,7 +42,7 @@ class SceneState : public RefCounted { Vector<NodePath> node_paths; Vector<NodePath> editable_instances; mutable HashMap<NodePath, int> node_path_cache; - mutable Map<int, int> base_scene_node_remap; + mutable HashMap<int, int> base_scene_node_remap; int base_scene_idx = -1; @@ -69,6 +69,12 @@ class SceneState : public RefCounted { Vector<int> groups; }; + struct DeferredNodePathProperties { + Node *base = nullptr; + StringName property; + NodePath path; + }; + Vector<NodeData> nodes; struct ConnectionData { @@ -83,8 +89,8 @@ class SceneState : public RefCounted { Vector<ConnectionData> connections; - Error _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); - Error _parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map); + Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map); + Error _parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map); String path; @@ -104,6 +110,8 @@ public: FLAG_ID_IS_PATH = (1 << 30), TYPE_INSTANCED = 0x7FFFFFFF, FLAG_INSTANCE_IS_PLACEHOLDER = (1 << 30), + FLAG_PATH_PROPERTY_IS_NODE = (1 << 30), + FLAG_PROP_NAME_MASK = FLAG_PATH_PROPERTY_IS_NODE - 1, FLAG_MASK = (1 << 24) - 1, }; @@ -157,6 +165,7 @@ public: int get_node_property_count(int p_idx) const; StringName get_node_property_name(int p_idx, int p_prop) const; Variant get_node_property_value(int p_idx, int p_prop) const; + Vector<String> get_node_deferred_nodepath_properties(int p_idx) const; int get_connection_count() const; NodePath get_connection_source(int p_idx) const; @@ -177,7 +186,7 @@ public: int add_value(const Variant &p_value); int add_node_path(const NodePath &p_path); int add_node(int p_parent, int p_owner, int p_type, int p_name, int p_instance, int p_index); - void add_node_property(int p_node, int p_name, int p_value); + void add_node_property(int p_node, int p_name, int p_value, bool p_deferred_node_path = false); void add_node_group(int p_node, int p_group); void set_base_scene(int p_idx); void add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds); @@ -186,6 +195,9 @@ public: virtual void set_last_modified_time(uint64_t p_time) { last_modified_time = p_time; } uint64_t get_last_modified_time() const { return last_modified_time; } + // Used when saving pointers (saves a path property instead). + static String get_meta_pointer_property(const String &p_property); + SceneState(); }; @@ -225,14 +237,16 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false) override; #ifdef TOOLS_ENABLED - virtual void set_last_modified_time(uint64_t p_time) override { state->set_last_modified_time(p_time); } + virtual void set_last_modified_time(uint64_t p_time) override { + state->set_last_modified_time(p_time); + } #endif - Ref<SceneState> get_state(); + Ref<SceneState> get_state() const; PackedScene(); }; VARIANT_ENUM_CAST(PackedScene::GenEditState) -#endif // SCENE_PRELOADER_H +#endif // PACKED_SCENE_H diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 1ef2b3496f..7a49b9b515 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -34,7 +34,7 @@ Mutex ParticlesMaterial::material_mutex; SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr; -Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map; +HashMap<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData, ParticlesMaterial::MaterialKey> ParticlesMaterial::shader_map; ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = nullptr; void ParticlesMaterial::init_shaders() { @@ -190,18 +190,21 @@ void ParticlesMaterial::_update_shader() { case EMISSION_SHAPE_SPHERE: { code += "uniform float emission_sphere_radius;\n"; } break; + case EMISSION_SHAPE_SPHERE_SURFACE: { + code += "uniform float emission_sphere_radius;\n"; + } break; case EMISSION_SHAPE_BOX: { code += "uniform vec3 emission_box_extents;\n"; } break; case EMISSION_SHAPE_DIRECTED_POINTS: { - code += "uniform sampler2D emission_texture_normal : hint_black;\n"; + code += "uniform sampler2D emission_texture_normal : hint_default_black;\n"; [[fallthrough]]; } case EMISSION_SHAPE_POINTS: { - code += "uniform sampler2D emission_texture_points : hint_black;\n"; + code += "uniform sampler2D emission_texture_points : hint_default_black;\n"; code += "uniform int emission_texture_point_count;\n"; if (emission_color_texture.is_valid()) { - code += "uniform sampler2D emission_texture_color : hint_white;\n"; + code += "uniform sampler2D emission_texture_color : hint_default_white;\n"; } } break; case EMISSION_SHAPE_RING: { @@ -225,53 +228,53 @@ void ParticlesMaterial::_update_shader() { code += "uniform bool sub_emitter_keep_velocity;\n"; } - code += "uniform vec4 color_value : hint_color;\n"; + code += "uniform vec4 color_value : source_color;\n"; code += "uniform vec3 gravity;\n"; if (color_ramp.is_valid()) { - code += "uniform sampler2D color_ramp;\n"; + code += "uniform sampler2D color_ramp : repeat_disable;\n"; } if (color_initial_ramp.is_valid()) { - code += "uniform sampler2D color_initial_ramp;\n"; + code += "uniform sampler2D color_initial_ramp : repeat_disable;\n"; } if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += "uniform sampler2D linear_velocity_texture;\n"; + code += "uniform sampler2D linear_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - code += "uniform sampler2D orbit_velocity_texture;\n"; + code += "uniform sampler2D orbit_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - code += "uniform sampler2D angular_velocity_texture;\n"; + code += "uniform sampler2D angular_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - code += "uniform sampler2D linear_accel_texture;\n"; + code += "uniform sampler2D linear_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - code += "uniform sampler2D radial_accel_texture;\n"; + code += "uniform sampler2D radial_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - code += "uniform sampler2D tangent_accel_texture;\n"; + code += "uniform sampler2D tangent_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_DAMPING].is_valid()) { - code += "uniform sampler2D damping_texture;\n"; + code += "uniform sampler2D damping_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += "uniform sampler2D angle_texture;\n"; + code += "uniform sampler2D angle_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_SCALE].is_valid()) { - code += "uniform sampler2D scale_texture;\n"; + code += "uniform sampler2D scale_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) { - code += "uniform sampler2D hue_variation_texture;\n"; + code += "uniform sampler2D hue_variation_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) { - code += "uniform sampler2D anim_speed_texture;\n"; + code += "uniform sampler2D anim_speed_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += "uniform sampler2D anim_offset_texture;\n"; + code += "uniform sampler2D anim_offset_texture : repeat_disable;\n"; } if (collision_enabled) { @@ -398,6 +401,13 @@ void ParticlesMaterial::_update_shader() { case EMISSION_SHAPE_SPHERE: { code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; + code += " float p = rand_from_seed(alt_seed);\n"; + code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; + code += " TRANSFORM[3].xyz = mix(vec3(0.0, 0.0, 0.0), vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s), p);\n"; + } break; + case EMISSION_SHAPE_SPHERE_SURFACE: { + code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; + code += " float t = rand_from_seed(alt_seed) * 2.0 * pi;\n"; code += " float radius = emission_sphere_radius * sqrt(1.0 - s * s);\n"; code += " TRANSFORM[3].xyz = vec3(radius * cos(t), radius * sin(t), emission_sphere_radius * s);\n"; } break; @@ -478,6 +488,12 @@ void ParticlesMaterial::_update_shader() { code += " float degree_to_rad = pi / 180.0;\n"; code += "\n"; + if (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { + code += " int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; + code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n"; + code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n"; + } + code += " CUSTOM.y += DELTA / LIFETIME;\n"; code += " float tv = CUSTOM.y / CUSTOM.w;\n"; if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { @@ -712,11 +728,9 @@ void ParticlesMaterial::_update_shader() { code += " if (DELTA >= interval_rem) emit_count = 1;\n"; } break; case SUB_EMITTER_AT_COLLISION: { - //not implemented yet code += " if (COLLIDED) emit_count = 1;\n"; } break; case SUB_EMITTER_AT_END: { - //not implemented yet code += " float unit_delta = DELTA/LIFETIME;\n"; code += " float end_time = CUSTOM.w * 0.95;\n"; // if we do at the end we might miss it, as it can just get deactivated by emitter code += " if (CUSTOM.y < end_time && (CUSTOM.y + unit_delta) >= end_time) emit_count = sub_emitter_amount_at_end;\n"; @@ -1165,7 +1179,7 @@ RID ParticlesMaterial::get_shader_rid() const { } void ParticlesMaterial::_validate_property(PropertyInfo &property) const { - if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { + if (property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) { property.usage = PROPERTY_USAGE_NONE; } @@ -1388,7 +1402,7 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness"); ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_point_texture", "get_emission_point_texture"); @@ -1463,7 +1477,7 @@ void ParticlesMaterial::_bind_methods() { ADD_GROUP("Sub Emitter", "sub_emitter_"); 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::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:Hz"), "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"); @@ -1496,6 +1510,7 @@ void ParticlesMaterial::_bind_methods() { BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE_SURFACE); BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index fd00c58468..af45593f38 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -73,6 +73,7 @@ public: enum EmissionShape { EMISSION_SHAPE_POINT, EMISSION_SHAPE_SPHERE, + EMISSION_SHAPE_SPHERE_SURFACE, EMISSION_SHAPE_BOX, EMISSION_SHAPE_POINTS, EMISSION_SHAPE_DIRECTED_POINTS, @@ -108,6 +109,14 @@ private: uint32_t key = 0; + static uint32_t hash(const MaterialKey &p_key) { + return hash_murmur3_one_32(p_key.key); + } + + bool operator==(const MaterialKey &p_key) const { + return key == p_key.key; + } + bool operator<(const MaterialKey &p_key) const { return key < p_key.key; } @@ -118,7 +127,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; @@ -229,8 +238,8 @@ private: bool is_initialized = false; Vector3 direction; - float spread; - float flatness; + float spread = 0.0f; + float flatness = 0.0f; float params_min[PARAM_MAX]; float params_max[PARAM_MAX]; @@ -243,34 +252,34 @@ private: bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape; - float emission_sphere_radius; + float emission_sphere_radius = 0.0f; Vector3 emission_box_extents; Ref<Texture2D> emission_point_texture; Ref<Texture2D> emission_normal_texture; Ref<Texture2D> emission_color_texture; Vector3 emission_ring_axis; - real_t emission_ring_height; - real_t emission_ring_radius; - real_t emission_ring_inner_radius; + real_t emission_ring_height = 0.0f; + real_t emission_ring_radius = 0.0f; + real_t emission_ring_inner_radius = 0.0f; int emission_point_count = 1; - bool anim_loop; + bool anim_loop = false; Vector3 gravity; - double lifetime_randomness; + double lifetime_randomness = 0.0; SubEmitterMode sub_emitter_mode; - double sub_emitter_frequency; - int sub_emitter_amount_at_end; - bool sub_emitter_keep_velocity; + double sub_emitter_frequency = 0.0; + int sub_emitter_amount_at_end = 0; + bool sub_emitter_keep_velocity = false; //do not save emission points here - bool attractor_interaction_enabled; - bool collision_enabled; - bool collision_scale; - float collision_friction; - float collision_bounce; + bool attractor_interaction_enabled = false; + bool collision_enabled = false; + bool collision_scale = false; + float collision_friction = 0.0f; + float collision_bounce = 0.0f; protected: static void _bind_methods(); diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 882afdb43d..29135e30c9 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -34,8 +34,8 @@ bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { int crosses = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 a = points[e.points[0]].pos; Vector2 b = points[e.points[1]].pos; @@ -105,8 +105,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> bool valid = true; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == i || e.points[1] == i || e.points[0] == j || e.points[1] == j) { continue; } @@ -137,11 +137,11 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector Edge ignore_to_edge(-1, -1); if (!_is_point_inside(from)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -151,7 +151,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float d = from.distance_squared_to(closest); if (d < closest_dist) { - ignore_from_edge = E->get(); + ignore_from_edge = E; closest_dist = d; closest_point = closest; } @@ -161,11 +161,11 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector }; if (!_is_point_inside(to)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -175,7 +175,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float d = to.distance_squared_to(closest); if (d < closest_dist) { - ignore_to_edge = E->get(); + ignore_to_edge = E; closest_dist = d; closest_point = closest; } @@ -188,8 +188,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector { bool can_see_eachother = true; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == ignore_from_edge.points[0] && e.points[1] == ignore_from_edge.points[1]) { continue; } @@ -240,8 +240,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector valid_b = false; } - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == i || e.points[1] == i) { continue; @@ -289,14 +289,14 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } //solve graph - Set<int> open_list; + HashSet<int> open_list; points.write[aidx].distance = 0; points.write[aidx].prev = aidx; - for (Set<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) { - open_list.insert(E->get()); - points.write[E->get()].distance = from.distance_to(points[E->get()].pos); - points.write[E->get()].prev = aidx; + for (const int &E : points[aidx].connections) { + open_list.insert(E); + points.write[E].distance = from.distance_to(points[E].pos); + points.write[E].prev = aidx; } bool found_route = false; @@ -312,14 +312,14 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float least_cost = 1e30; //this could be faster (cache previous results) - for (Set<int>::Element *E = open_list.front(); E; E = E->next()) { - const Point &p = points[E->get()]; + for (const int &E : open_list) { + const Point &p = points[E]; float cost = p.distance; cost += p.pos.distance_to(to); cost += p.penalty; if (cost < least_cost) { - least_cost_point = E->get(); + least_cost_point = E; least_cost = cost; } } @@ -327,8 +327,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector const Point &np = points[least_cost_point]; //open the neighbours for search - for (Set<int>::Element *E = np.connections.front(); E; E = E->next()) { - Point &p = points.write[E->get()]; + for (const int &E : np.connections) { + Point &p = points.write[E]; float distance = np.pos.distance_to(p.pos) + np.distance; if (p.prev != -1) { @@ -343,9 +343,9 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector p.prev = least_cost_point; p.distance = distance; - open_list.insert(E->get()); + open_list.insert(E); - if (E->get() == bidx) { + if (E == bidx) { //oh my reached end! stop algorithm found_route = true; break; @@ -459,8 +459,8 @@ Dictionary PolygonPathFinder::_get_data() const { { int *cw = c.ptrw(); int idx = 0; - for (Set<int>::Element *E = points[i].connections.front(); E; E = E->next()) { - cw[idx++] = E->get(); + for (const int &E : points[i].connections) { + cw[idx++] = E; } } connections[i] = c; @@ -469,9 +469,9 @@ Dictionary PolygonPathFinder::_get_data() const { { int *iw = ind.ptrw(); int idx = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - iw[idx++] = E->get().points[0]; - iw[idx++] = E->get().points[1]; + for (const Edge &E : edges) { + iw[idx++] = E.points[0]; + iw[idx++] = E.points[1]; } } @@ -489,11 +489,11 @@ bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const { } Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -508,7 +508,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { } } - ERR_FAIL_COND_V(closest_dist == 1e20, Vector2()); + ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2()); return closest_point; } @@ -516,9 +516,9 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2 &p_from, const Vector2 &p_to) const { Vector<Vector2> inters; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - Vector2 a = points[E->get().points[0]].pos; - Vector2 b = points[E->get().points[1]].pos; + for (const Edge &E : edges) { + Vector2 a = points[E.points[0]].pos; + Vector2 b = points[E.points[1]].pos; Vector2 res; if (Geometry2D::segment_intersects_segment(a, b, p_from, p_to, &res)) { diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h index db96192917..0e22b53dcb 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -38,21 +38,23 @@ class PolygonPathFinder : public Resource { struct Point { Vector2 pos; - Set<int> connections; + HashSet<int> connections; float distance = 0.0; float penalty = 0.0; int prev = 0; }; - struct Edge { - int points[2] = {}; + union Edge { + struct { + int32_t points[2]; + }; + uint64_t key = 0; - _FORCE_INLINE_ bool operator<(const Edge &p_edge) const { - if (points[0] == p_edge.points[0]) { - return points[1] < p_edge.points[1]; - } else { - return points[0] < p_edge.points[0]; - } + _FORCE_INLINE_ bool operator==(const Edge &p_edge) const { + return key == p_edge.key; + } + _FORCE_INLINE_ static uint32_t hash(const Edge &p_edge) { + return hash_one_uint64(p_edge.key); } Edge(int a = 0, int b = 0) { @@ -68,7 +70,7 @@ class PolygonPathFinder : public Resource { Rect2 bounds; Vector<Point> points; - Set<Edge> edges; + HashSet<Edge, Edge> edges; bool _is_point_inside(const Vector2 &p_point) const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 38acd0af0a..68441afb1c 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -29,18 +29,29 @@ /*************************************************************************/ #include "primitive_meshes.h" + +#include "core/core_string_names.h" +#include "scene/resources/theme.h" #include "servers/rendering_server.h" +#include "thirdparty/misc/clipper.hpp" +#include "thirdparty/misc/polypartition.h" /** PrimitiveMesh */ void PrimitiveMesh::_update() const { Array arr; - arr.resize(RS::ARRAY_MAX); - _create_mesh_array(arr); + if (GDVIRTUAL_CALL(_create_mesh_array, arr)) { + ERR_FAIL_COND_MSG(arr.size() != RS::ARRAY_MAX, "_create_mesh_array must return an array of Mesh.ARRAY_MAX elements."); + } else { + arr.resize(RS::ARRAY_MAX); + _create_mesh_array(arr); + } Vector<Vector3> points = arr[RS::ARRAY_VERTEX]; + ERR_FAIL_COND_MSG(points.size() == 0, "_create_mesh_array must return at least a vertex array."); + aabb = AABB(); int pc = points.size(); @@ -208,8 +219,10 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); 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::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); + + GDVIRTUAL_BIND(_create_mesh_array); } void PrimitiveMesh::set_material(const Ref<Material> &p_material) { @@ -262,6 +275,10 @@ PrimitiveMesh::~PrimitiveMesh() { */ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, radius, height, radial_segments, rings); +} + +void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, w; float onethird = 1.0 / 3.0; @@ -302,7 +319,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0)); normals.push_back(p.normalized()); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * onethird)); point++; @@ -341,7 +358,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius, y, -z * radius); points.push_back(p); normals.push_back(Vector3(x, 0.0, -z)); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, onethird + (v * onethird))); point++; @@ -381,7 +398,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0)); normals.push_back(p.normalized()); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird))); point++; @@ -418,16 +435,19 @@ void CapsuleMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CapsuleMesh::set_rings); ClassDB::bind_method(D_METHOD("get_rings"), &CapsuleMesh::get_rings); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "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"); + + ADD_LINKED_PROPERTY("radius", "height"); + ADD_LINKED_PROPERTY("height", "radius"); } void CapsuleMesh::set_radius(const float p_radius) { radius = p_radius; if (radius > height * 0.5) { - radius = height * 0.5; + height = radius * 2.0; } _request_update(); } @@ -439,7 +459,7 @@ float CapsuleMesh::get_radius() const { void CapsuleMesh::set_height(const float p_height) { height = p_height; if (radius > height * 0.5) { - height = radius * 2; + radius = height * 0.5; } _request_update(); } @@ -473,6 +493,10 @@ CapsuleMesh::CapsuleMesh() {} */ void BoxMesh::_create_mesh_array(Array &p_arr) const { + BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d); +} + +void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; @@ -675,7 +699,7 @@ void BoxMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_subdivide_depth", "divisions"), &BoxMesh::set_subdivide_depth); ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &BoxMesh::get_subdivide_depth); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "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_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); @@ -724,6 +748,10 @@ BoxMesh::BoxMesh() {} */ void CylinderMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom); +} + +void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, radius; @@ -781,7 +809,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const { }; // add top - if (top_radius > 0.0) { + if (cap_top && top_radius > 0.0) { y = height * 0.5; thisrow = point; @@ -817,7 +845,7 @@ void CylinderMesh::_create_mesh_array(Array &p_arr) const { }; // add bottom - if (bottom_radius > 0.0) { + if (cap_bottom && bottom_radius > 0.0) { y = height * -0.5; thisrow = point; @@ -872,11 +900,19 @@ 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,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"); + ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &CylinderMesh::set_cap_top); + ClassDB::bind_method(D_METHOD("is_cap_top"), &CylinderMesh::is_cap_top); + + ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &CylinderMesh::set_cap_bottom); + ClassDB::bind_method(D_METHOD("is_cap_bottom"), &CylinderMesh::is_cap_bottom); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "top_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_top_radius", "get_top_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bottom_radius", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_bottom_radius", "get_bottom_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "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"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom"); } void CylinderMesh::set_top_radius(const float p_radius) { @@ -924,6 +960,24 @@ int CylinderMesh::get_rings() const { return rings; } +void CylinderMesh::set_cap_top(bool p_cap_top) { + cap_top = p_cap_top; + _request_update(); +} + +bool CylinderMesh::is_cap_top() const { + return cap_top; +} + +void CylinderMesh::set_cap_bottom(bool p_cap_bottom) { + cap_bottom = p_cap_bottom; + _request_update(); +} + +bool CylinderMesh::is_cap_bottom() const { + return cap_bottom; +} + CylinderMesh::CylinderMesh() {} /** @@ -1002,10 +1056,10 @@ void PlaneMesh::_bind_methods() { 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::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "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"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset"); } void PlaneMesh::set_size(const Size2 &p_size) { @@ -1273,7 +1327,7 @@ void PrismMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "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_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); @@ -1386,8 +1440,8 @@ void QuadMesh::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_center_offset", "get_center_offset"); } uint32_t QuadMesh::surface_get_format(int p_idx) const { @@ -1423,6 +1477,10 @@ Vector3 QuadMesh::get_center_offset() const { */ void SphereMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere); +} + +void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) { int i, j, prevrow, thisrow, point; float x, y, z; @@ -1509,8 +1567,8 @@ void SphereMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_is_hemisphere", "is_hemisphere"), &SphereMesh::set_is_hemisphere); ClassDB::bind_method(D_METHOD("get_is_hemisphere"), &SphereMesh::get_is_hemisphere); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "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"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_hemisphere"), "set_is_hemisphere", "get_is_hemisphere"); @@ -1888,12 +1946,12 @@ void TubeTrailMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater,suffix:m"), "set_section_length", "get_section_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings"); @@ -2115,9 +2173,9 @@ void RibbonTrailMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape); ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater,suffix:m"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater,suffix:m"), "set_section_length", "get_section_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); @@ -2127,3 +2185,769 @@ void RibbonTrailMesh::_bind_methods() { RibbonTrailMesh::RibbonTrailMesh() { } + +/*************************************************************************/ +/* TextMesh */ +/*************************************************************************/ + +void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_gl) const { + if (cache.has(p_key)) { + return; + } + + GlyphMeshData &gl_data = cache[p_key]; + + Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index); + Vector2 origin = Vector2(p_gl.x_off, p_gl.y_off) * pixel_size; + + PackedVector3Array points = d["points"]; + PackedInt32Array contours = d["contours"]; + bool orientation = d["orientation"]; + + if (points.size() < 3 || contours.size() < 1) { + return; // No full contours, only glyph control points (or nothing), ignore. + } + + // Approximate Bezier curves as polygons. + // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info. + for (int i = 0; i < contours.size(); i++) { + int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1); + int32_t end = contours[i]; + Vector<ContourPoint> polygon; + + for (int32_t j = start; j <= end; j++) { + if (points[j].z == TextServer::CONTOUR_CURVE_TAG_ON) { + // Point on the curve. + Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size + origin; + polygon.push_back(ContourPoint(p, true)); + } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + // Conic Bezier arc. + int32_t next = (j == end) ? start : (j + 1); + int32_t prev = (j == start) ? end : (j - 1); + Vector2 p0; + Vector2 p1 = Vector2(points[j].x, points[j].y); + Vector2 p2; + + // For successive conic OFF points add a virtual ON point in the middle. + if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0; + } else if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_ON) { + p0 = Vector2(points[prev].x, points[prev].y); + } else { + ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j)); + } + if (points[next].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0; + } else if (points[next].z == TextServer::CONTOUR_CURVE_TAG_ON) { + p2 = Vector2(points[next].x, points[next].y); + } else { + ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j)); + } + + real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5); + real_t t = step; + while (t < 1.0) { + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t t2 = t * t; + + Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1); + Vector2 p = point * pixel_size + origin; + polygon.push_back(ContourPoint(p, false)); + t += step; + } + } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) { + // Cubic Bezier arc. + int32_t cur = j; + int32_t next1 = (j == end) ? start : (j + 1); + int32_t next2 = (next1 == end) ? start : (next1 + 1); + int32_t prev = (j == start) ? end : (j - 1); + + // There must be exactly two OFF points and two ON points for each cubic arc. + if (points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON) { + cur = (cur == 0) ? end : cur - 1; + next1 = (next1 == 0) ? end : next1 - 1; + next2 = (next2 == 0) ? end : next2 - 1; + prev = (prev == 0) ? end : prev - 1; + } else { + j++; + } + ERR_FAIL_COND_MSG(points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev)); + ERR_FAIL_COND_MSG(points[cur].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, cur)); + ERR_FAIL_COND_MSG(points[next1].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, next1)); + ERR_FAIL_COND_MSG(points[next2].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2)); + + Vector2 p0 = Vector2(points[prev].x, points[prev].y); + Vector2 p1 = Vector2(points[cur].x, points[cur].y); + Vector2 p2 = Vector2(points[next1].x, points[next1].y); + Vector2 p3 = Vector2(points[next2].x, points[next2].y); + + real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5); + real_t t = step; + while (t < 1.0) { + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector2 point = p0 * omt3 + p1 * omt2 * t * 3.0 + p2 * omt * t2 * 3.0 + p3 * t3; + Vector2 p = point * pixel_size + origin; + polygon.push_back(ContourPoint(p, false)); + t += step; + } + } else { + ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j)); + } + } + + if (polygon.size() < 3) { + continue; // Skip glyph control points. + } + + if (!orientation) { + polygon.reverse(); + } + + gl_data.contours.push_back(polygon); + } + + // Calculate bounds. + List<TPPLPoly> in_poly; + for (int i = 0; i < gl_data.contours.size(); i++) { + TPPLPoly inp; + inp.Init(gl_data.contours[i].size()); + real_t length = 0.0; + for (int j = 0; j < gl_data.contours[i].size(); j++) { + int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1); + + gl_data.min_p.x = MIN(gl_data.min_p.x, gl_data.contours[i][j].point.x); + gl_data.min_p.y = MIN(gl_data.min_p.y, gl_data.contours[i][j].point.y); + gl_data.max_p.x = MAX(gl_data.max_p.x, gl_data.contours[i][j].point.x); + gl_data.max_p.y = MAX(gl_data.max_p.y, gl_data.contours[i][j].point.y); + length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length(); + + inp.GetPoint(j) = gl_data.contours[i][j].point; + } + TPPLOrientation poly_orient = inp.GetOrientation(); + if (poly_orient == TPPL_ORIENTATION_CW) { + inp.SetHole(true); + } + in_poly.push_back(inp); + gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW)); + } + + TPPLPartition tpart; + + //Decompose and triangulate. + List<TPPLPoly> out_poly; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { + ERR_FAIL_MSG("Convex decomposing failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh."); + } + List<TPPLPoly> out_tris; + for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + if (tpart.Triangulate_OPT(&(I->get()), &out_tris) == 0) { + ERR_FAIL_MSG("Triangulation failed. Make sure the font doesn't contain self-intersecting lines, as these are not supported in TextMesh."); + } + } + + for (List<TPPLPoly>::Element *I = out_tris.front(); I; I = I->next()) { + TPPLPoly &tp = I->get(); + ERR_FAIL_COND(tp.GetNumPoints() != 3); // Triangles only. + + for (int i = 0; i < 3; i++) { + gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y)); + } + } +} + +void TextMesh::_create_mesh_array(Array &p_arr) const { + Ref<Font> font = _get_font_or_default(); + ERR_FAIL_COND(font.is_null()); + + if (dirty_cache) { + cache.clear(); + dirty_cache = false; + } + + // Update text buffer. + if (dirty_text) { + TS->shaped_text_clear(text_rid); + TS->shaped_text_set_direction(text_rid, text_direction); + + String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } + + Array stt; + if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { + GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt); + } else { + stt = TS->parse_structured_text(st_parser, st_args, text); + } + TS->shaped_text_set_bidi_override(text_rid, stt); + + dirty_text = false; + dirty_font = false; + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } + } else if (dirty_font) { + int spans = TS->shaped_get_span_count(text_rid); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } + + dirty_font = false; + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } + } + + Vector2 offset; + const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid); + int gl_size = TS->shaped_text_get_glyph_count(text_rid); + float line_width = TS->shaped_text_get_width(text_rid) * pixel_size; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + offset.x = 0.0; + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -line_width / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -line_width; + } break; + } + + bool has_depth = !Math::is_zero_approx(depth); + + // Generate glyph data, precalculate size of the arrays and mesh bounds for UV. + int64_t p_size = 0; + int64_t i_size = 0; + + Vector2 min_p = Vector2(INFINITY, INFINITY); + Vector2 max_p = Vector2(-INFINITY, -INFINITY); + + Vector2 offset_pre = offset; + for (int i = 0; i < gl_size; i++) { + if (glyphs[i].index == 0) { + offset.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; + continue; + } + if (glyphs[i].font_rid != RID()) { + GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); + _generate_glyph_mesh_data(key, glyphs[i]); + GlyphMeshData &gl_data = cache[key]; + + p_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); + i_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); + + if (has_depth) { + for (int j = 0; j < gl_data.contours.size(); j++) { + p_size += glyphs[i].repeat * gl_data.contours[j].size() * 4; + i_size += glyphs[i].repeat * gl_data.contours[j].size() * 6; + } + } + + for (int j = 0; j < glyphs[i].repeat; j++) { + min_p.x = MIN(gl_data.min_p.x + offset_pre.x, min_p.x); + min_p.y = MIN(gl_data.min_p.y + offset_pre.y, min_p.y); + max_p.x = MAX(gl_data.max_p.x + offset_pre.x, max_p.x); + max_p.y = MAX(gl_data.max_p.y + offset_pre.y, max_p.y); + + offset_pre.x += glyphs[i].advance * pixel_size; + } + } else { + p_size += glyphs[i].repeat * 4; + i_size += glyphs[i].repeat * 6; + + offset_pre.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; + } + } + + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<float> tangents; + Vector<Vector2> uvs; + Vector<int32_t> indices; + + vertices.resize(p_size); + normals.resize(p_size); + uvs.resize(p_size); + tangents.resize(p_size * 4); + indices.resize(i_size); + + Vector3 *vertices_ptr = vertices.ptrw(); + Vector3 *normals_ptr = normals.ptrw(); + float *tangents_ptr = tangents.ptrw(); + Vector2 *uvs_ptr = uvs.ptrw(); + int32_t *indices_ptr = indices.ptrw(); + + // Generate mesh. + int32_t p_idx = 0; + int32_t i_idx = 0; + for (int i = 0; i < gl_size; i++) { + if (glyphs[i].index == 0) { + offset.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; + continue; + } + if (glyphs[i].font_rid != RID()) { + GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); + _generate_glyph_mesh_data(key, glyphs[i]); + const GlyphMeshData &gl_data = cache[key]; + + int64_t ts = gl_data.triangles.size(); + const Vector2 *ts_ptr = gl_data.triangles.ptr(); + + for (int j = 0; j < glyphs[i].repeat; j++) { + for (int k = 0; k < ts; k += 3) { + // Add front face. + for (int l = 0; l < 3; l++) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0); + vertices_ptr[p_idx] = point; + normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); + } else { + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); + } + tangents_ptr[p_idx * 4 + 0] = 1.0; + tangents_ptr[p_idx * 4 + 1] = 0.0; + tangents_ptr[p_idx * 4 + 2] = 0.0; + tangents_ptr[p_idx * 4 + 3] = 1.0; + indices_ptr[i_idx++] = p_idx; + p_idx++; + } + if (has_depth) { + // Add back face. + for (int l = 2; l >= 0; l--) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0); + vertices_ptr[p_idx] = point; + normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0); + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.4), real_t(0.8))); + tangents_ptr[p_idx * 4 + 0] = -1.0; + tangents_ptr[p_idx * 4 + 1] = 0.0; + tangents_ptr[p_idx * 4 + 2] = 0.0; + tangents_ptr[p_idx * 4 + 3] = 1.0; + indices_ptr[i_idx++] = p_idx; + p_idx++; + } + } + } + // Add sides. + if (has_depth) { + for (int k = 0; k < gl_data.contours.size(); k++) { + int64_t ps = gl_data.contours[k].size(); + const ContourPoint *ps_ptr = gl_data.contours[k].ptr(); + const ContourInfo &ps_info = gl_data.contours_info[k]; + real_t length = 0.0; + for (int l = 0; l < ps; l++) { + int prev = (l == 0) ? (ps - 1) : (l - 1); + int next = (l + 1 == ps) ? 0 : (l + 1); + Vector2 d1; + Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized(); + if (ps_ptr[l].sharp) { + d1 = d2; + } else { + d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized(); + } + real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length(); + + Vector3 quad_faces[4] = { + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0), + }; + for (int m = 0; m < 4; m++) { + const Vector2 &d = ((m % 2) == 0) ? d1 : d2; + real_t u_pos = ((m % 2) == 0) ? length : length + seg_len; + vertices_ptr[p_idx + m] = quad_faces[m]; + normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0); + if (m < 2) { + uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9); + } else { + uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0); + } + tangents_ptr[(p_idx + m) * 4 + 0] = d.x; + tangents_ptr[(p_idx + m) * 4 + 1] = -d.y; + tangents_ptr[(p_idx + m) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + m) * 4 + 3] = 1.0; + } + + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; + + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 3; + indices_ptr[i_idx++] = p_idx + 2; + + length += seg_len; + p_idx += 4; + } + } + } + offset.x += glyphs[i].advance * pixel_size; + } + } else { + // Add fallback quad for missing glyphs. + for (int j = 0; j < glyphs[i].repeat; j++) { + Size2 sz = TS->get_hex_code_box_size(glyphs[i].font_size, glyphs[i].index) * pixel_size; + Vector3 quad_faces[4] = { + Vector3(offset.x, offset.y, 0.0), + Vector3(offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, offset.y, 0.0), + }; + for (int k = 0; k < 4; k++) { + vertices_ptr[p_idx + k] = quad_faces[k]; + normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); + } else { + uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); + } + tangents_ptr[(p_idx + k) * 4 + 0] = 1.0; + tangents_ptr[(p_idx + k) * 4 + 1] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 3] = 1.0; + } + + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; + + indices_ptr[i_idx++] = p_idx + 0; + indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx + 3; + p_idx += 4; + + offset.x += glyphs[i].advance * pixel_size; + } + } + } + + if (p_size == 0) { + // If empty, add single triangle to suppress errors. + vertices.push_back(Vector3()); + normals.push_back(Vector3()); + uvs.push_back(Vector2()); + tangents.push_back(1.0); + tangents.push_back(0.0); + tangents.push_back(0.0); + tangents.push_back(1.0); + indices.push_back(0); + indices.push_back(0); + indices.push_back(0); + } + + p_arr[RS::ARRAY_VERTEX] = vertices; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void TextMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment); + + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text); + ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width); + + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size); + ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size); + + ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step); + ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step); + + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction); + + ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options); + + ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase); + ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase); + + ClassDB::bind_method(D_METHOD("_font_changed"), &TextMesh::_font_changed); + ClassDB::bind_method(D_METHOD("_request_update"), &TextMesh::_request_update); + + ADD_GROUP("Text", ""); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + + ADD_GROUP("Mesh", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1,suffix:px"), "set_curve_step", "get_curve_step"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:m"), "set_width", "get_width"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); +} + +void TextMesh::_notification(int p_what) { + switch (p_what) { + case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = tr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + dirty_text = true; + _request_update(); + } break; + } +} + +TextMesh::TextMesh() { + primitive_type = PRIMITIVE_TRIANGLES; + text_rid = TS->create_shaped_text(); +} + +TextMesh::~TextMesh() { + TS->free_rid(text_rid); +} + +void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (horizontal_alignment != p_alignment) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + dirty_text = true; + } + horizontal_alignment = p_alignment; + _request_update(); + } +} + +HorizontalAlignment TextMesh::get_horizontal_alignment() const { + return horizontal_alignment; +} + +void TextMesh::set_text(const String &p_string) { + if (text != p_string) { + text = p_string; + xl_text = tr(text); + dirty_text = true; + _request_update(); + } +} + +String TextMesh::get_text() const { + return text; +} + +void TextMesh::_font_changed() { + dirty_font = true; + dirty_cache = true; + call_deferred(SNAME("_request_update")); +} + +void TextMesh::set_font(const Ref<Font> &p_font) { + if (font_override != p_font) { + if (font_override.is_valid()) { + font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + font_override = p_font; + dirty_font = true; + dirty_cache = true; + if (font_override.is_valid()) { + font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + _request_update(); + } +} + +Ref<Font> TextMesh::get_font() const { + return font_override; +} + +Ref<Font> TextMesh::_get_font_or_default() const { + if (font_override.is_valid()) { + return font_override; + } + + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + } + } + } + + // If they don't exist, use any type to return the default/empty value. + return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); +} + +void TextMesh::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = CLAMP(p_size, 1, 127); + dirty_font = true; + dirty_cache = true; + _request_update(); + } +} + +int TextMesh::get_font_size() const { + return font_size; +} + +void TextMesh::set_depth(real_t p_depth) { + if (depth != p_depth) { + depth = MAX(p_depth, 0.0); + _request_update(); + } +} + +real_t TextMesh::get_depth() const { + return depth; +} + +void TextMesh::set_width(real_t p_width) { + if (width != p_width) { + width = p_width; + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + dirty_text = true; + } + _request_update(); + } +} + +real_t TextMesh::get_width() const { + return width; +} + +void TextMesh::set_pixel_size(real_t p_amount) { + if (pixel_size != p_amount) { + pixel_size = CLAMP(p_amount, 0.0001, 128.0); + dirty_cache = true; + _request_update(); + } +} + +real_t TextMesh::get_pixel_size() const { + return pixel_size; +} + +void TextMesh::set_curve_step(real_t p_step) { + if (curve_step != p_step) { + curve_step = CLAMP(p_step, 0.1, 10.0); + dirty_cache = true; + _request_update(); + } +} + +real_t TextMesh::get_curve_step() const { + return curve_step; +} + +void TextMesh::set_text_direction(TextServer::Direction p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + dirty_text = true; + _request_update(); + } +} + +TextServer::Direction TextMesh::get_text_direction() const { + return text_direction; +} + +void TextMesh::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + dirty_text = true; + _request_update(); + } +} + +String TextMesh::get_language() const { + return language; +} + +void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + dirty_text = true; + _request_update(); + } +} + +TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const { + return st_parser; +} + +void TextMesh::set_structured_text_bidi_override_options(Array p_args) { + if (st_args != p_args) { + st_args = p_args; + dirty_text = true; + _request_update(); + } +} + +Array TextMesh::get_structured_text_bidi_override_options() const { + return st_args; +} + +void TextMesh::set_uppercase(bool p_uppercase) { + if (uppercase != p_uppercase) { + uppercase = p_uppercase; + dirty_text = true; + _request_update(); + } +} + +bool TextMesh::is_uppercase() const { + return uppercase; +} diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 3fc5fd4a16..ec124e379f 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -31,7 +31,9 @@ #ifndef PRIMITIVE_MESHES_H #define PRIMITIVE_MESHES_H +#include "scene/resources/font.h" #include "scene/resources/mesh.h" +#include "servers/text_server.h" ///@TODO probably should change a few integers to unsigned integers... @@ -64,8 +66,9 @@ protected: static void _bind_methods(); - virtual void _create_mesh_array(Array &p_arr) const = 0; + virtual void _create_mesh_array(Array &p_arr) const {} void _request_update(); + GDVIRTUAL0RC(Array, _create_mesh_array) public: virtual int get_surface_count() const override; @@ -116,6 +119,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8); + void set_radius(const float p_radius); float get_radius() const; @@ -148,6 +153,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0); + void set_size(const Vector3 &p_size); Vector3 get_size() const; @@ -176,12 +183,16 @@ private: float height = 2.0; int radial_segments = 64; int rings = 4; + bool cap_top = true; + bool cap_bottom = true; protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true); + void set_top_radius(const float p_radius); float get_top_radius() const; @@ -197,6 +208,12 @@ public: void set_rings(const int p_rings); int get_rings() const; + void set_cap_top(bool p_cap_top); + bool is_cap_top() const; + + void set_cap_bottom(bool p_cap_bottom); + bool is_cap_bottom() const; + CylinderMesh(); }; @@ -240,7 +257,7 @@ class PrismMesh : public PrimitiveMesh { private: float left_to_right = 0.5; - Vector3 size = Vector3(2.0, 2.0, 2.0); + Vector3 size = Vector3(1.0, 1.0, 1.0); int subdivide_w = 0; int subdivide_h = 0; int subdivide_d = 0; @@ -302,8 +319,8 @@ class SphereMesh : public PrimitiveMesh { GDCLASS(SphereMesh, PrimitiveMesh); private: - float radius = 1.0; - float height = 2.0; + float radius = 0.5; + float height = 1.0; int radial_segments = 64; int rings = 32; bool is_hemisphere = false; @@ -313,6 +330,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false); + void set_radius(const float p_radius); float get_radius() const; @@ -349,7 +368,7 @@ class TubeTrailMesh : public PrimitiveMesh { GDCLASS(TubeTrailMesh, PrimitiveMesh); private: - float radius = 1.0; + float radius = 0.5; int radial_steps = 8; int sections = 5; float section_length = 0.2; @@ -438,5 +457,143 @@ public: RibbonTrailMesh(); }; +/** + Text... +*/ + +class TextMesh : public PrimitiveMesh { + GDCLASS(TextMesh, PrimitiveMesh); + +private: + struct ContourPoint { + Vector2 point; + bool sharp = false; + + ContourPoint(){}; + ContourPoint(const Vector2 &p_pt, bool p_sharp) { + point = p_pt; + sharp = p_sharp; + }; + }; + + struct ContourInfo { + real_t length = 0.0; + bool ccw = true; + ContourInfo(){}; + ContourInfo(real_t p_len, bool p_ccw) { + length = p_len; + ccw = p_ccw; + } + }; + + struct GlyphMeshKey { + uint64_t font_id; + uint32_t gl_id; + + bool operator==(const GlyphMeshKey &p_b) const { + return (font_id == p_b.font_id) && (gl_id == p_b.gl_id); + } + + GlyphMeshKey(uint64_t p_font_id, uint32_t p_gl_id) { + font_id = p_font_id; + gl_id = p_gl_id; + } + }; + + struct GlyphMeshKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const GlyphMeshKey &p_a) { + return hash_murmur3_buffer(&p_a, sizeof(GlyphMeshKey)); + } + }; + + struct GlyphMeshData { + Vector<Vector2> triangles; + Vector<Vector<ContourPoint>> contours; + Vector<ContourInfo> contours_info; + Vector2 min_p = Vector2(INFINITY, INFINITY); + Vector2 max_p = Vector2(-INFINITY, -INFINITY); + }; + mutable HashMap<GlyphMeshKey, GlyphMeshData, GlyphMeshKeyHasher> cache; + + RID text_rid; + String text; + String xl_text; + + int font_size = 16; + Ref<Font> font_override; + float width = 500.0; + + HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; + bool uppercase = false; + String language; + TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; + Array st_args; + + real_t depth = 0.05; + real_t pixel_size = 0.01; + real_t curve_step = 0.5; + + mutable bool dirty_text = true; + mutable bool dirty_font = true; + mutable bool dirty_cache = true; + + void _generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_glyph) const; + void _font_changed(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + + TextMesh(); + ~TextMesh(); + + void set_horizontal_alignment(HorizontalAlignment p_alignment); + HorizontalAlignment get_horizontal_alignment() const; + + void set_text(const String &p_string); + String get_text() const; + + void set_font(const Ref<Font> &p_font); + Ref<Font> get_font() const; + Ref<Font> _get_font_or_default() const; + + void set_font_size(int p_size); + int get_font_size() const; + + void set_text_direction(TextServer::Direction p_text_direction); + TextServer::Direction get_text_direction() const; + + void set_language(const String &p_language); + String get_language() const; + + void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser); + TextServer::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + + void set_uppercase(bool p_uppercase); + bool is_uppercase() const; + + void set_width(real_t p_width); + real_t get_width() const; + + void set_depth(real_t p_depth); + real_t get_depth() const; + + void set_curve_step(real_t p_step); + real_t get_curve_step() const; + + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; +}; + VARIANT_ENUM_CAST(RibbonTrailMesh::Shape) -#endif + +#endif // PRIMITIVE_MESHES_H diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 27659f724e..a64b262cb4 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -58,6 +58,7 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const #endif // DISABLE_DEPRECATED void RectangleShape2D::set_size(const Vector2 &p_size) { + ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative."); size = p_size; _update_shape(); } @@ -100,7 +101,7 @@ void RectangleShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_size", "size"), &RectangleShape2D::set_size); ClassDB::bind_method(D_METHOD("get_size"), &RectangleShape2D::get_size); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size"); } RectangleShape2D::RectangleShape2D() : diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index c03faa2c2d..100e8ea7c6 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/io/missing_resource.h" #include "core/io/resource_format_binary.h" #include "core/version.h" @@ -39,6 +40,8 @@ // Version 3: new string ID for ext/subresources, breaks forward compat. #define FORMAT_VERSION 3 +#define BINARY_FORMAT_VERSION 4 + #include "core/io/dir_access.h" #include "core/version.h" @@ -62,18 +65,18 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia return ERR_PARSE_ERROR; } - String unique_id = token.value; + if (p_data->no_placeholders) { + r_res.unref(); + } else { + String unique_id = token.value; - if (!p_data->resource_map.has(unique_id)) { - Ref<DummyResource> dr; - dr.instantiate(); - dr->set_scene_unique_id(unique_id); - p_data->resource_map[unique_id] = dr; - uint32_t im_size = p_data->resource_index_map.size(); - p_data->resource_index_map.insert(dr, im_size); - } + if (!p_data->resource_map.has(unique_id)) { + r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file"; + return ERR_PARSE_ERROR; + } - r_res = p_data->resource_map[unique_id]; + r_res = p_data->resource_map[unique_id]; + } VariantParser::get_token(p_stream, token, line, r_err_str); if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { @@ -92,11 +95,15 @@ Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, Varia return ERR_PARSE_ERROR; } - String id = token.value; + if (p_data->no_placeholders) { + r_res.unref(); + } else { + String id = token.value; - ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); + ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR); - r_res = p_data->rev_external_resources[id]; + r_res = p_data->rev_external_resources[id]; + } VariantParser::get_token(p_stream, token, line, r_err_str); if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) { @@ -150,10 +157,10 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R if (ext_resources[id].cache.is_valid()) { r_res = ext_resources[id].cache; } else if (use_sub_threads) { - RES res = ResourceLoader::load_threaded_get(path); + Ref<Resource> res = ResourceLoader::load_threaded_get(path); if (res.is_null()) { if (ResourceLoader::get_abort_on_missing_resources()) { - error = ERR_FILE_CORRUPT; + error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced nonexistent resource at: " + path; _printerr(); return error; @@ -165,13 +172,13 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R r_res = res; } } else { - error = ERR_FILE_CORRUPT; + error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced non-loaded resource at: " + path; _printerr(); return error; } } else { - r_res = RES(); + r_res = Ref<Resource>(); } VariantParser::get_token(p_stream, token, line, r_err_str); @@ -213,6 +220,15 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated } + HashSet<StringName> path_properties; + + if (next_tag.fields.has("node_paths")) { + Vector<String> paths = next_tag.fields["node_paths"]; + for (int i = 0; i < paths.size(); i++) { + path_properties.insert(paths[i]); + } + } + if (next_tag.fields.has("instance")) { instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]); @@ -265,7 +281,9 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser); if (error) { - if (error != ERR_FILE_EOF) { + if (error == ERR_FILE_MISSING_DEPENDENCIES) { + // Resource loading error, just skip it. + } else if (error != ERR_FILE_EOF) { _printerr(); return Ref<PackedScene>(); } else { @@ -275,9 +293,10 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars } if (!assign.is_empty()) { - int nameidx = packed_scene->get_state()->add_name(assign); + StringName assign_name = assign; + int nameidx = packed_scene->get_state()->add_name(assign_name); int valueidx = packed_scene->get_state()->add_value(value); - packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx); + packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx, path_properties.has(assign_name)); //it's assignment } else if (!next_tag.name.is_empty()) { break; @@ -458,7 +477,7 @@ Error ResourceLoaderText::load() { } } else { - RES res = ResourceLoader::load(path, type); + Ref<Resource> res = ResourceLoader::load(path, type); if (res.is_null()) { if (ResourceLoader::get_abort_on_missing_resources()) { @@ -525,28 +544,37 @@ Error ResourceLoaderText::load() { if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(path)) { //reuse existing - Resource *r = ResourceCache::get(path); - if (r && r->get_class() == type) { - res = Ref<Resource>(r); + Ref<Resource> cache = ResourceCache::get_ref(path); + if (cache.is_valid() && cache->get_class() == type) { + res = cache; res->reset_state(); do_assign = true; } } + MissingResource *missing_resource = nullptr; + if (res.is_null()) { //not reuse - if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist + Ref<Resource> cache = ResourceCache::get_ref(path); + if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && cache.is_valid()) { //only if it doesn't exist //cached, do not assign - Resource *r = ResourceCache::get(path); - res = Ref<Resource>(r); + res = cache; } else { //create Object *obj = ClassDB::instantiate(type); if (!obj) { - error_text += "Can't create sub resource of type: " + type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -570,6 +598,8 @@ Error ResourceLoaderText::load() { res->set_scene_unique_id(id); } + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -583,7 +613,23 @@ Error ResourceLoaderText::load() { if (!assign.is_empty()) { if (do_assign) { - res->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + res->set(assign, value); + } } //it's assignment } else if (!next_tag.name.is_empty()) { @@ -597,6 +643,14 @@ Error ResourceLoaderText::load() { } } + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + res->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + if (progress && resources_total > 0) { *progress = resource_current / float(resources_total); } @@ -614,21 +668,28 @@ Error ResourceLoaderText::load() { return error; } - if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && ResourceCache::has(local_path)) { - Resource *r = ResourceCache::get(local_path); - if (r->get_class() == res_type) { - r->reset_state(); - resource = Ref<Resource>(r); - } + Ref<Resource> cache = ResourceCache::get_ref(local_path); + if (cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE && cache.is_valid() && cache->get_class() == res_type) { + cache->reset_state(); + resource = cache; } + MissingResource *missing_resource = nullptr; + if (!resource.is_valid()) { Object *obj = ClassDB::instantiate(res_type); if (!obj) { - error_text += "Can't create sub resource of type: " + res_type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(res_type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + res_type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -644,6 +705,8 @@ Error ResourceLoaderText::load() { resource_current++; + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -666,7 +729,23 @@ Error ResourceLoaderText::load() { } if (!assign.is_empty()) { - resource->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + resource->set(assign, value); + } //it's assignment } else if (!next_tag.name.is_empty()) { error = ERR_FILE_CORRUPT; @@ -674,14 +753,24 @@ Error ResourceLoaderText::load() { _printerr(); return error; } else { - error = OK; - if (progress && resources_total > 0) { - *progress = resource_current / float(resources_total); - } - - return error; + break; } } + + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + resource->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + + error = OK; + if (progress && resources_total > 0) { + *progress = resource_current / float(resources_total); + } + + return error; } //for scene files @@ -736,11 +825,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) { ResourceLoaderText::ResourceLoaderText() {} -ResourceLoaderText::~ResourceLoaderText() { - memdelete(f); -} - -void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { +void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); ignore_resource_parsing = true; ERR_FAIL_COND(error != OK); @@ -796,13 +881,13 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen } } -Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map) { +Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map) { open(p_f, true); ERR_FAIL_COND_V(error != OK, error); ignore_resource_parsing = true; //FileAccess - FileAccess *fw = nullptr; + Ref<FileAccess> fw; String base_path = local_path.get_base_dir(); @@ -812,23 +897,20 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); if (err != OK) { - if (fw) { - memdelete(fw); - } error = ERR_FILE_CORRUPT; ERR_FAIL_V(error); } if (next_tag.name != "ext_resource") { //nothing was done - if (!fw) { + if (fw.is_null()) { return OK; } break; } else { - if (!fw) { + if (fw.is_null()) { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); if (is_scene) { fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); @@ -838,7 +920,6 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p } if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) { - memdelete(fw); error = ERR_FILE_CORRUPT; ERR_FAIL_V(error); } @@ -896,25 +977,17 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p fw->store_8(c); c = f->get_8(); } - f->close(); bool all_ok = fw->get_error() == OK; - memdelete(fw); - if (!all_ok) { return ERR_CANT_CREATE; } - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(p_path); - da->rename(p_path + ".depren", p_path); - memdelete(da); - return OK; } -void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { +void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { error = OK; lines = 1; @@ -991,23 +1064,23 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { rp.userdata = this; } -static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) { +static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len = false) { CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32((utf8.length() + 1) | 0x80000000); + p_f->store_32((utf8.length() + 1) | 0x80000000); } else { - f->store_32(utf8.length() + 1); + p_f->store_32(utf8.length() + 1); } - f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); + p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } -Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) { +Error ResourceLoaderText::save_as_binary(const String &p_path) { if (error) { return error; } - FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE); - if (!wf) { + Ref<FileAccess> wf = FileAccess::open(p_path, FileAccess::WRITE); + if (wf.is_null()) { return ERR_CANT_OPEN; } @@ -1019,10 +1092,10 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) wf->store_32(0); //64 bits file, false for now wf->store_32(VERSION_MAJOR); wf->store_32(VERSION_MINOR); - static const int save_format_version = 3; //use format version 3 for saving + static const int save_format_version = BINARY_FORMAT_VERSION; wf->store_32(save_format_version); - bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type); + bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type); wf->store_64(0); //offset to import metadata, this is no longer used wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); @@ -1077,8 +1150,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) uid = ResourceUID::get_singleton()->text_to_id(uidt); } - bs_save_unicode_string(wf.f, type); - bs_save_unicode_string(wf.f, path); + bs_save_unicode_string(wf, type); + bs_save_unicode_string(wf, path); wf->store_64(uid); int lindex = dummy_read.external_resources.size(); @@ -1107,54 +1180,217 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) wf->store_32(0); //zero sub resources, still parsing them String temp_file = p_path + ".temp"; - FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE); - if (!wf2) { - return ERR_CANT_OPEN; - } - Vector<uint64_t> local_offsets; Vector<uint64_t> local_pointers_pos; + { + Ref<FileAccess> wf2 = FileAccess::open(temp_file, FileAccess::WRITE); + if (wf2.is_null()) { + return ERR_CANT_OPEN; + } - while (next_tag.name == "sub_resource" || next_tag.name == "resource") { - String type; - int id = -1; - bool main_res; + while (next_tag.name == "sub_resource" || next_tag.name == "resource") { + String type; + String id; + bool main_res; - if (next_tag.name == "sub_resource") { - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; + if (next_tag.name == "sub_resource") { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } + + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'id' in external resource tag"; + _printerr(); + return error; + } + + type = next_tag.fields["type"]; + id = next_tag.fields["id"]; + main_res = false; + + if (!dummy_read.resource_map.has(id)) { + Ref<DummyResource> dr; + dr.instantiate(); + dr->set_scene_unique_id(id); + dummy_read.resource_map[id] = dr; + uint32_t im_size = dummy_read.resource_index_map.size(); + dummy_read.resource_index_map.insert(dr, im_size); + } + + } else { + type = res_type; + String uid_text = ResourceUID::get_singleton()->id_to_text(res_uid); + id = type + "_" + uid_text.replace("uid://", "").replace("<invalid>", "0"); + main_res = true; + } + + local_offsets.push_back(wf2->get_position()); + + bs_save_unicode_string(wf, "local://" + id); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset + + bs_save_unicode_string(wf2, type); + uint64_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); + + int prop_count = 0; + + while (true) { + String assign; + Variant value; + + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + + if (error) { + if (main_res && error == ERR_FILE_EOF) { + next_tag.name = ""; //exit + break; + } + + _printerr(); + return error; + } + + if (!assign.is_empty()) { + HashMap<StringName, int> empty_string_map; //unused + bs_save_unicode_string(wf2, assign, true); + ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); + prop_count++; + + } else if (!next_tag.name.is_empty()) { + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } + } + + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); + } + + if (next_tag.name == "node") { + // This is a node, must save one more! + + if (!is_scene) { + error_text += "found the 'node' tag on a resource file!"; _printerr(); + error = ERR_FILE_CORRUPT; return error; } - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); + Ref<PackedScene> packed_scene = _parse_node_tag(rp); + + if (!packed_scene.is_valid()) { return error; } - type = next_tag.fields["type"]; - id = next_tag.fields["id"]; - main_res = false; - } else { - type = res_type; - id = 0; //used for last anyway - main_res = true; + error = OK; + //get it here + List<PropertyInfo> props; + packed_scene->get_property_list(&props); + + String id = "PackedScene_" + ResourceUID::get_singleton()->id_to_text(res_uid).replace("uid://", "").replace("<invalid>", "0"); + bs_save_unicode_string(wf, "local://" + id); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset + + local_offsets.push_back(wf2->get_position()); + bs_save_unicode_string(wf2, "PackedScene"); + uint64_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); + + int prop_count = 0; + + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + String name = E.name; + Variant value = packed_scene->get(name); + + HashMap<StringName, int> empty_string_map; //unused + bs_save_unicode_string(wf2, name, true); + ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); + prop_count++; + } + + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); } + } - local_offsets.push_back(wf2->get_position()); + uint64_t offset_from = wf->get_position(); + wf->seek(sub_res_count_pos); //plus one because the saved one + wf->store_32(local_offsets.size()); + + for (int i = 0; i < local_offsets.size(); i++) { + wf->seek(local_pointers_pos[i]); + wf->store_64(local_offsets[i] + offset_from); + } + + wf->seek_end(); + + Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file); + wf->store_buffer(data.ptr(), data.size()); + { + Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir()); + dar->remove(temp_file); + } + + wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end + + return OK; +} + +Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) { + if (error) { + return error; + } + + ignore_resource_parsing = true; + + DummyReadData dummy_read; + dummy_read.no_placeholders = true; + VariantParser::ResourceParser rp; + rp.ext_func = _parse_ext_resource_dummys; + rp.sub_func = _parse_sub_resource_dummys; + rp.userdata = &dummy_read; + + while (next_tag.name == "ext_resource") { + error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); + + if (error) { + _printerr(); + return error; + } + } - bs_save_unicode_string(wf, "local://" + itos(id)); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset + while (next_tag.name == "sub_resource" || next_tag.name == "resource") { + if (next_tag.name == "sub_resource") { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } - bs_save_unicode_string(wf2, type); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); + r_classes->insert(next_tag.fields["type"]); - int prop_count = 0; + } else { + r_classes->insert(next_tag.fields["res_type"]); + } while (true) { String assign; @@ -1163,9 +1399,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); if (error) { - if (main_res && error == ERR_FILE_EOF) { - next_tag.name = ""; //exit - break; + if (error == ERR_FILE_EOF) { + return OK; } _printerr(); @@ -1173,11 +1408,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) } if (!assign.is_empty()) { - Map<StringName, int> empty_string_map; //unused - bs_save_unicode_string(wf2, assign, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); - prop_count++; - + continue; } else if (!next_tag.name.is_empty()) { error = OK; break; @@ -1188,14 +1419,10 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) return error; } } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); } - if (next_tag.name == "node") { - //this is a node, must save one more! + while (next_tag.name == "node") { + // This is a node, must save one more! if (!is_scene) { error_text += "found the 'node' tag on a resource file!"; @@ -1204,75 +1431,50 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) return error; } - Ref<PackedScene> packed_scene = _parse_node_tag(rp); - - if (!packed_scene.is_valid()) { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); return error; } - error = OK; - //get it here - List<PropertyInfo> props; - packed_scene->get_property_list(&props); + r_classes->insert(next_tag.fields["type"]); - bs_save_unicode_string(wf, "local://0"); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset + while (true) { + String assign; + Variant value; - local_offsets.push_back(wf2->get_position()); - bs_save_unicode_string(wf2, "PackedScene"); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); - int prop_count = 0; + if (error) { + if (error == ERR_FILE_MISSING_DEPENDENCIES) { + // Resource loading error, just skip it. + } else if (error != ERR_FILE_EOF) { + _printerr(); + return error; + } else { + return OK; + } + } - for (const PropertyInfo &E : props) { - if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + if (!assign.is_empty()) { continue; + } else if (!next_tag.name.is_empty()) { + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; } - - String name = E.name; - Variant value = packed_scene->get(name); - - Map<StringName, int> empty_string_map; //unused - bs_save_unicode_string(wf2, name, true); - ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); - prop_count++; } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - - wf2->close(); - - uint64_t offset_from = wf->get_position(); - wf->seek(sub_res_count_pos); //plus one because the saved one - wf->store_32(local_offsets.size()); - - for (int i = 0; i < local_offsets.size(); i++) { - wf->seek(local_pointers_pos[i]); - wf->store_64(local_offsets[i] + offset_from); } - wf->seek_end(); - - Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file); - wf->store_buffer(data.ptr(), data.size()); - { - DirAccessRef dar = DirAccess::open(temp_file.get_base_dir()); - dar->remove(temp_file); - } - - wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end - - wf->close(); - return OK; } -String ResourceLoaderText::recognize(FileAccess *p_f) { +String ResourceLoaderText::recognize(Ref<FileAccess> p_f) { error = OK; lines = 1; @@ -1316,7 +1518,7 @@ String ResourceLoaderText::recognize(FileAccess *p_f) { return tag.fields["type"]; } -ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) { +ResourceUID::ID ResourceLoaderText::get_uid(Ref<FileAccess> p_f) { error = OK; lines = 1; @@ -1344,16 +1546,16 @@ ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) { ///////////////////// -RES ResourceFormatLoaderText::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<Resource> ResourceFormatLoaderText::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) { if (r_error) { *r_error = ERR_CANT_OPEN; } Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'."); ResourceLoaderText loader; String path = !p_original_path.is_empty() ? p_original_path : p_path; @@ -1370,7 +1572,7 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina if (err == OK) { return loader.get_resource(); } else { - return RES(); + return Ref<Resource>(); } } @@ -1380,9 +1582,12 @@ void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String & return; } - if (p_type == "PackedScene") { + if (ClassDB::is_parent_class("PackedScene", p_type)) { p_extensions->push_back("tscn"); - } else { + } + + // Don't allow .tres for PackedScenes. + if (p_type != "PackedScene") { p_extensions->push_back("tres"); } } @@ -1396,6 +1601,26 @@ bool ResourceFormatLoaderText::handles_type(const String &p_type) const { return true; } +void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) { + String ext = p_path.get_extension().to_lower(); + if (ext == "tscn") { + r_classes->insert("PackedScene"); + } + + // ...for anything else must test... + + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return; // Could not read. + } + + ResourceLoaderText loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + loader.open(f); + loader.get_classes_used(r_classes); +} + String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { String ext = p_path.get_extension().to_lower(); if (ext == "tscn") { @@ -1406,8 +1631,8 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { // ...for anything else must test... - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ""; //could not read } @@ -1425,8 +1650,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) return ResourceUID::INVALID_ID; } - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ResourceUID::INVALID_ID; //could not read } @@ -1437,8 +1662,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) } void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { ERR_FAIL(); } @@ -1448,23 +1673,34 @@ void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<Strin loader.get_dependencies(f, p_dependencies, p_add_types); } -Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - ERR_FAIL_V(ERR_CANT_OPEN); +Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) { + Error err = OK; + { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + ERR_FAIL_V(ERR_CANT_OPEN); + } + + ResourceLoaderText loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + err = loader.rename_dependencies(f, p_path, p_map); } - ResourceLoaderText loader; - loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); - loader.res_path = loader.local_path; - return loader.rename_dependencies(f, p_path, p_map); + if (err == OK) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(p_path); + da->rename(p_path + ".depren", p_path); + } + + return err; } ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = nullptr; Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { Error err; - FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'."); @@ -1473,7 +1709,7 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, loader.local_path = ProjectSettings::get_singleton()->localize_path(path); loader.res_path = loader.local_path; loader.open(f); - return loader.save_as_binary(f, p_dst_path); + return loader.save_as_binary(p_dst_path); } /*****************************************************************************************************/ @@ -1487,24 +1723,24 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, /*****************************************************************************************************/ /*****************************************************************************************************/ -String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_resource) { - ResourceFormatSaverTextInstance *rsi = (ResourceFormatSaverTextInstance *)ud; +String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Resource> &p_resource) { + ResourceFormatSaverTextInstance *rsi = static_cast<ResourceFormatSaverTextInstance *>(ud); return rsi->_write_resource(p_resource); } -String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { +String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) { if (external_resources.has(res)) { - return "ExtResource( \"" + external_resources[res] + "\" )"; + return "ExtResource(\"" + external_resources[res] + "\")"; } else { if (internal_resources.has(res)) { - return "SubResource( \"" + internal_resources[res] + "\" )"; + return "SubResource(\"" + internal_resources[res] + "\")"; } else if (!res->is_built_in()) { if (res->get_path() == local_path) { //circular reference attempt return "null"; } //external resource String path = relative_paths ? local_path.path_to_file(res->get_path()) : res->get_path(); - return "Resource( \"" + path + "\" )"; + return "Resource(\"" + path + "\")"; } else { ERR_FAIL_V_MSG("null", "Resource was not pre cached for the resource section, bug?"); //internal resource @@ -1515,7 +1751,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, bool p_main) { switch (p_variant.get_type()) { case Variant::OBJECT: { - RES res = p_variant; + Ref<Resource> res = p_variant; if (res.is_null() || external_resources.has(res)) { return; @@ -1552,7 +1788,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, Variant v = res->get(I->get().name); if (pi.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { - RES sres = v; + Ref<Resource> sres = v; if (sres.is_valid()) { NonPersistentKey npk; npk.base = res; @@ -1596,15 +1832,24 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, } } -Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +static String _resource_get_class(Ref<Resource> p_resource) { + Ref<MissingResource> missing_resource = p_resource; + if (missing_resource.is_valid()) { + return missing_resource->get_original_class(); + } else { + return p_resource->get_class(); + } +} + +Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { if (p_path.ends_with(".tscn")) { packed_scene = p_resource; } Error err; - f = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, ERR_CANT_OPEN, "Cannot save file '" + p_path + "'."); - FileAccessRef _fref(f); + Ref<FileAccess> _fref(f); local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1637,7 +1882,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r { String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; if (packed_scene.is_null()) { - title += "type=\"" + p_resource->get_class() + "\" "; + title += "type=\"" + _resource_get_class(p_resource) + "\" "; } int load_steps = saved_resources.size() + external_resources.size(); @@ -1658,8 +1903,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #ifdef TOOLS_ENABLED // Keep order from cached ids. - Set<String> cached_ids_found; - for (KeyValue<RES, String> &E : external_resources) { + HashSet<String> cached_ids_found; + for (KeyValue<Ref<Resource>, String> &E : external_resources) { String cached_id = E.key->get_id_for_path(local_path); if (cached_id.is_empty() || cached_ids_found.has(cached_id)) { int sep_pos = E.value.find("_"); @@ -1675,7 +1920,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } } // Create IDs for non cached resources. - for (KeyValue<RES, String> &E : external_resources) { + for (KeyValue<Ref<Resource>, String> &E : external_resources) { if (cached_ids_found.has(E.value)) { // Already cached, go on. continue; } @@ -1697,14 +1942,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #else // Make sure to start from one, as it makes format more readable. int counter = 1; - for (KeyValue<RES, String> &E : external_resources) { + for (KeyValue<Ref<Resource>, String> &E : external_resources) { E.value = itos(counter++); } #endif Vector<ResourceSort> sorted_er; - for (const KeyValue<RES, String> &E : external_resources) { + for (const KeyValue<Ref<Resource>, String> &E : external_resources) { ResourceSort rs; rs.resource = E.key; rs.id = E.value; @@ -1730,10 +1975,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_line(String()); // Separate. } - Set<String> used_unique_ids; + HashSet<String> used_unique_ids; - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES res = E->get(); + for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) { + Ref<Resource> res = E->get(); if (E->next() && res->is_built_in()) { if (!res->get_scene_unique_id().is_empty()) { if (used_unique_ids.has(res->get_scene_unique_id())) { @@ -1745,8 +1990,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } } - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES res = E->get(); + for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) { + Ref<Resource> res = E->get(); ERR_CONTINUE(!resource_set.has(res)); bool main = (E->next() == nullptr); @@ -1761,7 +2006,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r if (res->get_scene_unique_id().is_empty()) { String new_id; while (true) { - new_id = res->get_class() + "_" + Resource::generate_scene_unique_id(); + new_id = _resource_get_class(res) + "_" + Resource::generate_scene_unique_id(); if (!used_unique_ids.has(new_id)) { break; @@ -1773,7 +2018,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } String id = res->get_scene_unique_id(); - line += "type=\"" + res->get_class() + "\" id=\"" + id; + line += "type=\"" + _resource_get_class(res) + "\" id=\"" + id; f->store_line(line + "\"]"); if (takeover_paths) { res->set_path(p_path + "::" + id, true); @@ -1785,12 +2030,17 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #endif } + Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary()); + List<PropertyInfo> property_list; res->get_property_list(&property_list); for (List<PropertyInfo>::Element *PE = property_list.front(); PE; PE = PE->next()) { if (skip_editor && PE->get().name.begins_with("__editor")) { continue; } + if (PE->get().name == META_PROPERTY_MISSING_RESOURCES) { + continue; + } if (PE->get().usage & PROPERTY_USAGE_STORAGE) { String name = PE->get().name; @@ -1805,6 +2055,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } else { value = res->get(name); } + + if (PE->get().type == Variant::OBJECT && missing_resource_properties.has(PE->get().name)) { + // Was this missing resource overridden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[PE->get().name]; + } + } + Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { @@ -1838,6 +2097,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r Ref<PackedScene> instance = state->get_node_instance(i); String instance_placeholder = state->get_node_instance_placeholder(i); Vector<StringName> groups = state->get_node_groups(i); + Vector<String> deferred_node_paths = state->get_node_deferred_nodepath_properties(i); String header = "[node"; header += " name=\"" + String(name).c_escape() + "\""; @@ -1854,6 +2114,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r header += " index=\"" + itos(index) + "\""; } + if (deferred_node_paths.size()) { + header += " node_paths=" + Variant(deferred_node_paths).get_construct_string(); + } + if (groups.size()) { // Write all groups on the same line as they're part of a section header. // This improves readability while not impacting VCS friendliness too much, @@ -1941,16 +2205,13 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { - f->close(); return ERR_CANT_CREATE; } - f->close(); - return OK; } -Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverText::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) { return ERR_FILE_UNRECOGNIZED; } @@ -1959,11 +2220,11 @@ Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, return saver.save(p_path, p_resource, p_flags); } -bool ResourceFormatSaverText::recognize(const RES &p_resource) const { +bool ResourceFormatSaverText::recognize(const Ref<Resource> &p_resource) const { return true; // All resources recognized! } -void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverText::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (Ref<PackedScene>(p_resource).is_valid()) { p_extensions->push_back("tscn"); // Text scene. } else { diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 9585b9040f..69bb40502f 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -43,12 +43,12 @@ class ResourceLoaderText { String res_path; String error_text; - FileAccess *f = nullptr; + Ref<FileAccess> f; VariantParser::StreamFile stream; struct ExtResource { - RES cache; + Ref<Resource> cache; String path; String type; }; @@ -58,8 +58,8 @@ class ResourceLoaderText { bool ignore_resource_parsing = false; - Map<String, ExtResource> ext_resources; - Map<String, RES> int_resources; + HashMap<String, ExtResource> ext_resources; + HashMap<String, Ref<Resource>> int_resources; int resources_total = 0; int resource_current = 0; @@ -76,7 +76,7 @@ class ResourceLoaderText { ResourceUID::ID res_uid = ResourceUID::INVALID_ID; - Map<String, String> remaps; + HashMap<String, String> remaps; static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); } static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); } @@ -90,14 +90,15 @@ class ResourceLoaderText { }; struct DummyReadData { - Map<RES, int> external_resources; - Map<String, RES> rev_external_resources; - Map<RES, int> resource_index_map; - Map<String, RES> resource_map; + bool no_placeholders = false; + HashMap<Ref<Resource>, int> external_resources; + HashMap<String, Ref<Resource>> rev_external_resources; + HashMap<Ref<Resource>, int> resource_index_map; + HashMap<String, Ref<Resource>> resource_map; }; - static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } - static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } + static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); } + static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); } static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); @@ -108,7 +109,7 @@ class ResourceLoaderText { Error error = OK; - RES resource; + Ref<Resource> resource; Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser); @@ -120,28 +121,30 @@ public: int get_stage_count() const; void set_translation_remapped(bool p_remapped); - void open(FileAccess *p_f, bool p_skip_first_tag = false); - String recognize(FileAccess *p_f); - ResourceUID::ID get_uid(FileAccess *p_f); - void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); - Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map); + void open(Ref<FileAccess> p_f, bool p_skip_first_tag = false); + String recognize(Ref<FileAccess> p_f); + ResourceUID::ID get_uid(Ref<FileAccess> p_f); + void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types); + Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map); + Error get_classes_used(HashSet<StringName> *r_classes); - Error save_as_binary(FileAccess *p_f, const String &p_path); + Error save_as_binary(const String &p_path); ResourceLoaderText(); - ~ResourceLoaderText(); }; class ResourceFormatLoaderText : public ResourceFormatLoader { public: static ResourceFormatLoaderText *singleton; - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; + virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes); + virtual String get_resource_type(const String &p_path) const; virtual ResourceUID::ID get_resource_uid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); - virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); + virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); @@ -157,23 +160,22 @@ class ResourceFormatSaverTextInstance { bool relative_paths = false; bool bundle_resources = false; bool skip_editor = false; - FileAccess *f = nullptr; struct NonPersistentKey { //for resource properties generated on the fly - RES base; + Ref<Resource> base; StringName property; bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; } }; - Map<NonPersistentKey, RES> non_persistent_map; + RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map; - Set<RES> resource_set; - List<RES> saved_resources; - Map<RES, String> external_resources; - Map<RES, String> internal_resources; + HashSet<Ref<Resource>> resource_set; + List<Ref<Resource>> saved_resources; + HashMap<Ref<Resource>, String> external_resources; + HashMap<Ref<Resource>, String> internal_resources; struct ResourceSort { - RES resource; + Ref<Resource> resource; String id; bool operator<(const ResourceSort &p_right) const { return id.naturalnocasecmp_to(p_right.id) < 0; @@ -182,19 +184,19 @@ class ResourceFormatSaverTextInstance { void _find_resources(const Variant &p_variant, bool p_main = false); - static String _write_resources(void *ud, const RES &p_resource); - String _write_resource(const RES &res); + static String _write_resources(void *ud, const Ref<Resource> &p_resource); + String _write_resource(const Ref<Resource> &res); public: - Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); }; class ResourceFormatSaverText : public ResourceFormatSaver { public: static ResourceFormatSaverText *singleton; - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual bool recognize(const RES &p_resource) const; - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual bool recognize(const Ref<Resource> &p_resource) const; + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; ResourceFormatSaverText(); }; diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp index 2acc0f1922..6789f9f7d5 100644 --- a/scene/resources/scene_replication_config.cpp +++ b/scene/resources/scene_replication_config.cpp @@ -52,11 +52,19 @@ bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_val ReplicationProperty &prop = properties[idx]; if (what == "sync") { prop.sync = p_value; - sync_props.push_back(prop.name); + if (prop.sync) { + sync_props.push_back(prop.name); + } else { + sync_props.erase(prop.name); + } return true; } else if (what == "spawn") { prop.spawn = p_value; - spawn_props.push_back(prop.name); + if (prop.spawn) { + spawn_props.push_back(prop.name); + } else { + spawn_props.erase(prop.name); + } return true; } } @@ -124,6 +132,15 @@ void SceneReplicationConfig::remove_property(const NodePath &p_path) { properties.erase(p_path); } +bool SceneReplicationConfig::has_property(const NodePath &p_path) const { + for (int i = 0; i < properties.size(); i++) { + if (properties[i].name == p_path) { + return true; + } + } + return false; +} + int SceneReplicationConfig::property_get_index(const NodePath &p_path) const { for (int i = 0; i < properties.size(); i++) { if (properties[i].name == p_path) { @@ -178,6 +195,7 @@ void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_en void SceneReplicationConfig::_bind_methods() { ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties); ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("has_property", "path"), &SceneReplicationConfig::has_property); ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property); ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index); ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn); diff --git a/scene/resources/scene_replication_config.h b/scene/resources/scene_replication_config.h index b791be9414..ab3658d2a7 100644 --- a/scene/resources/scene_replication_config.h +++ b/scene/resources/scene_replication_config.h @@ -73,6 +73,7 @@ public: void add_property(const NodePath &p_path, int p_index = -1); void remove_property(const NodePath &p_path); + bool has_property(const NodePath &p_path) const; int property_get_index(const NodePath &p_path) const; bool property_get_spawn(const NodePath &p_path); diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index cea8ca1b29..d53c777492 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -88,8 +88,8 @@ void SegmentShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_b", "b"), &SegmentShape2D::set_b); ClassDB::bind_method(D_METHOD("get_b"), &SegmentShape2D::get_b); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "a"), "set_a", "get_a"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "b"), "set_b", "get_b"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "a", PROPERTY_HINT_NONE, "suffix:px"), "set_a", "get_a"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "b", PROPERTY_HINT_NONE, "suffix:px"), "set_b", "get_b"); } SegmentShape2D::SegmentShape2D() : diff --git a/scene/resources/separation_ray_shape_2d.cpp b/scene/resources/separation_ray_shape_2d.cpp index 0406c91b70..fea41061fd 100644 --- a/scene/resources/separation_ray_shape_2d.cpp +++ b/scene/resources/separation_ray_shape_2d.cpp @@ -57,7 +57,7 @@ void SeparationRayShape2D::draw(const RID &p_to_rid, const Color &p_color) { Transform2D xf; xf.rotate(target_position.angle()); - xf.translate(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); + xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); Vector<Vector2> pts = { xf.xform(Vector2(arrow_size, 0)), @@ -89,7 +89,7 @@ void SeparationRayShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape2D::set_slide_on_slope); ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape2D::get_slide_on_slope); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:px"), "set_length", "get_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope"); } diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp index 5aa7616589..7306d5b985 100644 --- a/scene/resources/separation_ray_shape_3d.cpp +++ b/scene/resources/separation_ray_shape_3d.cpp @@ -80,7 +80,7 @@ void SeparationRayShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_slope", "active"), &SeparationRayShape3D::set_slide_on_slope); ClassDB::bind_method(D_METHOD("get_slide_on_slope"), &SeparationRayShape3D::get_slide_on_slope); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_length", "get_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_slope"), "set_slide_on_slope", "get_slide_on_slope"); } diff --git a/scene/resources/separation_ray_shape_3d.h b/scene/resources/separation_ray_shape_3d.h index 0e750a48e6..ae08ff7887 100644 --- a/scene/resources/separation_ray_shape_3d.h +++ b/scene/resources/separation_ray_shape_3d.h @@ -28,8 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SEPARATION_RAY_SHAPE_H -#define SEPARATION_RAY_SHAPE_H +#ifndef SEPARATION_RAY_SHAPE_3D_H +#define SEPARATION_RAY_SHAPE_3D_H + #include "scene/resources/shape_3d.h" class SeparationRayShape3D : public Shape3D { @@ -53,4 +54,5 @@ public: SeparationRayShape3D(); }; -#endif // SEPARATION_RAY_SHAPE_H + +#endif // SEPARATION_RAY_SHAPE_3D_H diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index ce7fcb199d..74031e02d7 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -33,6 +33,7 @@ #include "core/io/file_access.h" #include "scene/scene_string_names.h" #include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" #include "servers/rendering_server.h" #include "texture.h" @@ -40,7 +41,23 @@ Shader::Mode Shader::get_mode() const { return mode; } +void Shader::_dependency_changed() { + RenderingServer::get_singleton()->shader_set_code(shader, RenderingServer::get_singleton()->shader_get_code(shader)); + params_cache_dirty = true; + + emit_changed(); +} + +void Shader::set_path(const String &p_path, bool p_take_over) { + Resource::set_path(p_path, p_take_over); + RS::get_singleton()->shader_set_path_hint(shader, p_path); +} + void Shader::set_code(const String &p_code) { + for (Ref<ShaderInclude> E : include_dependencies) { + E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + } + String type = ShaderLanguage::get_shader_type(p_code); if (type == "canvas_item") { @@ -55,7 +72,27 @@ void Shader::set_code(const String &p_code) { mode = MODE_SPATIAL; } - RenderingServer::get_singleton()->shader_set_code(shader, p_code); + code = p_code; + String pp_code = p_code; + + HashSet<Ref<ShaderInclude>> new_include_dependencies; + + { + // Preprocessor must run here and not in the server because: + // 1) Need to keep track of include dependencies at resource level + // 2) Server does not do interaction with Resource filetypes, this is a scene level feature. + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_include_dependencies); + } + + // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) + include_dependencies = new_include_dependencies; + + for (Ref<ShaderInclude> E : include_dependencies) { + E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + } + + RenderingServer::get_singleton()->shader_set_code(shader, pp_code); params_cache_dirty = true; emit_changed(); @@ -63,10 +100,10 @@ void Shader::set_code(const String &p_code) { String Shader::get_code() const { _update_shader(); - return RenderingServer::get_singleton()->shader_get_code(shader); + return code; } -void Shader::get_param_list(List<PropertyInfo> *p_params) const { +void Shader::get_param_list(List<PropertyInfo> *p_params, bool p_get_groups) const { _update_shader(); List<PropertyInfo> local; @@ -75,12 +112,16 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { params_cache_dirty = false; for (PropertyInfo &pi : local) { - if (default_textures.has(pi.name)) { //do not show default textures + bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP; + if (!p_get_groups && is_group) { continue; } - String original_name = pi.name; - pi.name = "shader_param/" + pi.name; - params_cache[pi.name] = original_name; + if (!is_group) { + if (default_textures.has(pi.name)) { //do not show default textures + continue; + } + params_cache[pi.name] = pi.name; + } if (p_params) { //small little hack if (pi.type == Variant::RID) { @@ -100,7 +141,7 @@ RID Shader::get_rid() const { void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) { if (p_texture.is_valid()) { if (!default_textures.has(p_param)) { - default_textures[p_param] = Map<int, Ref<Texture2D>>(); + default_textures[p_param] = HashMap<int, Ref<Texture2D>>(); } default_textures[p_param][p_index] = p_texture; RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index); @@ -126,7 +167,7 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int } void Shader::get_default_texture_param_list(List<StringName> *r_textures) const { - for (const KeyValue<StringName, Map<int, Ref<Texture2D>>> &E : default_textures) { + for (const KeyValue<StringName, HashMap<int, Ref<Texture2D>>> &E : default_textures) { r_textures->push_back(E.key); } } @@ -172,7 +213,7 @@ Shader::~Shader() { //////////// -RES ResourceFormatLoaderShader::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<Resource> ResourceFormatLoaderShader::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) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; } @@ -210,29 +251,26 @@ String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const return ""; } -Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverShader::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { Ref<Shader> shader = p_resource; ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER); String source = shader->get_code(); Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, err, "Cannot save shader '" + p_path + "'."); file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); return ERR_CANT_CREATE; } - file->close(); - memdelete(file); return OK; } -void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverShader::get_recognized_extensions(const Ref<Resource> &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("gdshader"); @@ -240,6 +278,6 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, } } -bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { +bool ResourceFormatSaverShader::recognize(const Ref<Resource> &p_resource) const { return p_resource->get_class_name() == "Shader"; //only shader, not inherited } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index d05ec06819..7aa14651a5 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -35,6 +35,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "scene/resources/texture.h" +#include "shader_include.h" class Shader : public Resource { GDCLASS(Shader, Resource); @@ -53,14 +54,17 @@ public: private: RID shader; Mode mode = MODE_SPATIAL; + HashSet<Ref<ShaderInclude>> include_dependencies; + String code; // hack the name of performance // shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make // conversion fast and save memory. mutable bool params_cache_dirty = true; - mutable Map<StringName, StringName> params_cache; //map a shader param to a material param.. - Map<StringName, Map<int, Ref<Texture2D>>> default_textures; + mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param.. + HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures; + void _dependency_changed(); virtual void _update_shader() const; //used for visual shader protected: static void _bind_methods(); @@ -69,10 +73,12 @@ public: //void set_mode(Mode p_mode); virtual Mode get_mode() const; + virtual void set_path(const String &p_path, bool p_take_over = false) override; + void set_code(const String &p_code); String get_code() const; - void get_param_list(List<PropertyInfo> *p_params) const; + void get_param_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const; bool has_param(const StringName &p_param) const; void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0); @@ -86,9 +92,9 @@ public: get_param_list(nullptr); } - const Map<StringName, StringName>::Element *E = params_cache.find(p_param); + const HashMap<StringName, StringName>::Iterator E = params_cache.find(p_param); if (E) { - return E->get(); + return E->value; } return StringName(); } @@ -103,7 +109,7 @@ VARIANT_ENUM_CAST(Shader::Mode); class ResourceFormatLoaderShader : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -111,9 +117,9 @@ public: class ResourceFormatSaverShader : public ResourceFormatSaver { public: - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const RES &p_resource) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; }; #endif // SHADER_H diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp new file mode 100644 index 0000000000..b819128af3 --- /dev/null +++ b/scene/resources/shader_include.cpp @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* shader_include.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "shader_include.h" +#include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" + +void ShaderInclude::_dependency_changed() { + emit_changed(); +} + +void ShaderInclude::set_code(const String &p_code) { + HashSet<Ref<ShaderInclude>> new_dependencies; + code = p_code; + + for (Ref<ShaderInclude> E : dependencies) { + E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + } + + { + String pp_code; + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_dependencies); + } + + // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) + dependencies = new_dependencies; + + for (Ref<ShaderInclude> E : dependencies) { + E->connect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); + } + + emit_changed(); +} + +String ShaderInclude::get_code() const { + return code; +} + +void ShaderInclude::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_code", "code"), &ShaderInclude::set_code); + ClassDB::bind_method(D_METHOD("get_code"), &ShaderInclude::get_code); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code"); +} + +// ResourceFormatLoaderShaderInclude + +Ref<Resource> ResourceFormatLoaderShaderInclude::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) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } + + Ref<ShaderInclude> shader_inc; + shader_inc.instantiate(); + + Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); + + String str; + str.parse_utf8((const char *)buffer.ptr(), buffer.size()); + + shader_inc->set_code(str); + + if (r_error) { + *r_error = OK; + } + + return shader_inc; +} + +void ResourceFormatLoaderShaderInclude::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("gdshaderinc"); +} + +bool ResourceFormatLoaderShaderInclude::handles_type(const String &p_type) const { + return (p_type == "ShaderInclude"); +} + +String ResourceFormatLoaderShaderInclude::get_resource_type(const String &p_path) const { + String extension = p_path.get_extension().to_lower(); + if (extension == "gdshaderinc") { + return "ShaderInclude"; + } + return ""; +} + +// ResourceFormatSaverShaderInclude + +Error ResourceFormatSaverShaderInclude::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { + Ref<ShaderInclude> shader_inc = p_resource; + ERR_FAIL_COND_V(shader_inc.is_null(), ERR_INVALID_PARAMETER); + + String source = shader_inc->get_code(); + + Error error; + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &error); + + ERR_FAIL_COND_V_MSG(error, error, "Cannot save shader include '" + p_path + "'."); + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } + + return OK; +} + +void ResourceFormatSaverShaderInclude::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { + const ShaderInclude *shader_inc = Object::cast_to<ShaderInclude>(*p_resource); + if (shader_inc != nullptr) { + p_extensions->push_back("gdshaderinc"); + } +} + +bool ResourceFormatSaverShaderInclude::recognize(const Ref<Resource> &p_resource) const { + return p_resource->get_class_name() == "ShaderInclude"; //only shader, not inherited +} diff --git a/scene/resources/shader_include.h b/scene/resources/shader_include.h new file mode 100644 index 0000000000..6f0deeef4e --- /dev/null +++ b/scene/resources/shader_include.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* shader_include.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 SHADER_INCLUDE_H +#define SHADER_INCLUDE_H + +#include "core/io/resource.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/templates/hash_set.h" + +class ShaderInclude : public Resource { + GDCLASS(ShaderInclude, Resource); + OBJ_SAVE_TYPE(ShaderInclude); + +private: + String code; + HashSet<Ref<ShaderInclude>> dependencies; + void _dependency_changed(); + +protected: + static void _bind_methods(); + +public: + void set_code(const String &p_text); + String get_code() const; +}; + +class ResourceFormatLoaderShaderInclude : public ResourceFormatLoader { +public: + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + +class ResourceFormatSaverShaderInclude : public ResourceFormatSaver { +public: + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; +}; + +#endif // SHADER_INCLUDE_H diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index ffb2b27644..4423c1d7bb 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -117,7 +117,7 @@ void Shape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_debug_mesh"), &Shape3D::get_debug_mesh); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001,suffix:m"), "set_margin", "get_margin"); } Shape3D::Shape3D() { diff --git a/scene/resources/shape_3d.h b/scene/resources/shape_3d.h index 77e79a269d..58fe723d89 100644 --- a/scene/resources/shape_3d.h +++ b/scene/resources/shape_3d.h @@ -73,4 +73,4 @@ public: ~Shape3D(); }; -#endif // SHAPE_H +#endif // SHAPE_3D_H diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index d49f9e7f51..4c49119df0 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2D_H -#define SKELETONMODIFICATION2D_H +#ifndef SKELETON_MODIFICATION_2D_H +#define SKELETON_MODIFICATION_2D_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_stack_2d.h" @@ -49,7 +49,7 @@ class SkeletonModification2D : public Resource { protected: static void _bind_methods(); - SkeletonModificationStack2D *stack; + SkeletonModificationStack2D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; @@ -86,4 +86,4 @@ public: SkeletonModification2D(); }; -#endif // SKELETONMODIFICATION2D_H +#endif // SKELETON_MODIFICATION_2D_H diff --git a/scene/resources/skeleton_modification_2d_ccdik.h b/scene/resources/skeleton_modification_2d_ccdik.h index 31485fa31f..e49dfca5b5 100644 --- a/scene/resources/skeleton_modification_2d_ccdik.h +++ b/scene/resources/skeleton_modification_2d_ccdik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DCCDIK_H -#define SKELETONMODIFICATION2DCCDIK_H +#ifndef SKELETON_MODIFICATION_2D_CCDIK_H +#define SKELETON_MODIFICATION_2D_CCDIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -113,4 +113,4 @@ public: ~SkeletonModification2DCCDIK(); }; -#endif // SKELETONMODIFICATION2DCCDIK_H +#endif // SKELETON_MODIFICATION_2D_CCDIK_H diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index d5077084ef..4a875d039f 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DFABRIK_H -#define SKELETONMODIFICATION2DFABRIK_H +#ifndef SKELETON_MODIFICATION_2D_FABRIK_H +#define SKELETON_MODIFICATION_2D_FABRIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -105,4 +105,4 @@ public: ~SkeletonModification2DFABRIK(); }; -#endif // SKELETONMODIFICATION2DFABRIK_H +#endif // SKELETON_MODIFICATION_2D_FABRIK_H diff --git a/scene/resources/skeleton_modification_2d_jiggle.h b/scene/resources/skeleton_modification_2d_jiggle.h index a1abca6564..b9080eafa9 100644 --- a/scene/resources/skeleton_modification_2d_jiggle.h +++ b/scene/resources/skeleton_modification_2d_jiggle.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DJIGGLE_H -#define SKELETONMODIFICATION2DJIGGLE_H +#ifndef SKELETON_MODIFICATION_2D_JIGGLE_H +#define SKELETON_MODIFICATION_2D_JIGGLE_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -136,4 +136,4 @@ public: ~SkeletonModification2DJiggle(); }; -#endif // SKELETONMODIFICATION2DJIGGLE_H +#endif // SKELETON_MODIFICATION_2D_JIGGLE_H diff --git a/scene/resources/skeleton_modification_2d_lookat.h b/scene/resources/skeleton_modification_2d_lookat.h index ff91b92e7d..e4d7afa605 100644 --- a/scene/resources/skeleton_modification_2d_lookat.h +++ b/scene/resources/skeleton_modification_2d_lookat.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DLOOKAT_H -#define SKELETONMODIFICATION2DLOOKAT_H +#ifndef SKELETON_MODIFICATION_2D_LOOKAT_H +#define SKELETON_MODIFICATION_2D_LOOKAT_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -97,4 +97,4 @@ public: ~SkeletonModification2DLookAt(); }; -#endif // SKELETONMODIFICATION2DLOOKAT_H +#endif // SKELETON_MODIFICATION_2D_LOOKAT_H diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h index d53102fa5e..55482b2cca 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DPHYSICALBONES_H -#define SKELETONMODIFICATION2DPHYSICALBONES_H +#ifndef SKELETON_MODIFICATION_2D_PHYSICALBONES_H +#define SKELETON_MODIFICATION_2D_PHYSICALBONES_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -52,7 +52,7 @@ private: bool _simulation_state_dirty = false; TypedArray<StringName> _simulation_state_dirty_names; - bool _simulation_state_dirty_process; + bool _simulation_state_dirty_process = false; void _update_simulation_state(); protected: @@ -79,4 +79,4 @@ public: ~SkeletonModification2DPhysicalBones(); }; -#endif // SKELETONMODIFICATION2DPHYSICALBONES_H +#endif // SKELETON_MODIFICATION_2D_PHYSICALBONES_H diff --git a/scene/resources/skeleton_modification_2d_stackholder.h b/scene/resources/skeleton_modification_2d_stackholder.h index 99d9f6f381..4da9fcc1f6 100644 --- a/scene/resources/skeleton_modification_2d_stackholder.h +++ b/scene/resources/skeleton_modification_2d_stackholder.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DSTACKHOLDER_H -#define SKELETONMODIFICATION2DSTACKHOLDER_H +#ifndef SKELETON_MODIFICATION_2D_STACKHOLDER_H +#define SKELETON_MODIFICATION_2D_STACKHOLDER_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -61,4 +61,4 @@ public: ~SkeletonModification2DStackHolder(); }; -#endif // SKELETONMODIFICATION2DSTACKHOLDER_H +#endif // SKELETON_MODIFICATION_2D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index b08fd82381..d3c62e441f 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -464,8 +464,8 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() { 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::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0,100000000,0.01,suffix:m"), "set_target_minimum_distance", "get_target_minimum_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0,100000000,0.01,suffix:m"), "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("", ""); } diff --git a/scene/resources/skeleton_modification_2d_twoboneik.h b/scene/resources/skeleton_modification_2d_twoboneik.h index fc14d35f70..e1dbb6cfda 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.h +++ b/scene/resources/skeleton_modification_2d_twoboneik.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION2DTWOBONEIK_H -#define SKELETONMODIFICATION2DTWOBONEIK_H +#ifndef SKELETON_MODIFICATION_2D_TWOBONEIK_H +#define SKELETON_MODIFICATION_2D_TWOBONEIK_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -104,4 +104,4 @@ public: ~SkeletonModification2DTwoBoneIK(); }; -#endif // SKELETONMODIFICATION2DTWOBONEIK_H +#endif // SKELETON_MODIFICATION_2D_TWOBONEIK_H diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h index a81c0c38bd..6daf5efcd9 100644 --- a/scene/resources/skeleton_modification_3d.h +++ b/scene/resources/skeleton_modification_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATION3D_H -#define SKELETONMODIFICATION3D_H +#ifndef SKELETON_MODIFICATION_3D_H +#define SKELETON_MODIFICATION_3D_H #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_stack_3d.h" @@ -44,7 +44,7 @@ class SkeletonModification3D : public Resource { protected: static void _bind_methods(); - SkeletonModificationStack3D *stack; + SkeletonModificationStack3D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; @@ -76,4 +76,4 @@ public: SkeletonModification3D(); }; -#endif // SKELETONMODIFICATION3D_H +#endif // SKELETON_MODIFICATION_3D_H diff --git a/scene/resources/skeleton_modification_3d_ccdik.h b/scene/resources/skeleton_modification_3d_ccdik.h index 873ab8aa5a..7098794038 100644 --- a/scene/resources/skeleton_modification_3d_ccdik.h +++ b/scene/resources/skeleton_modification_3d_ccdik.h @@ -32,8 +32,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DCCDIK_H -#define SKELETONMODIFICATION3DCCDIK_H +#ifndef SKELETON_MODIFICATION_3D_CCDIK_H +#define SKELETON_MODIFICATION_3D_CCDIK_H class SkeletonModification3DCCDIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DCCDIK, SkeletonModification3D); @@ -111,4 +111,4 @@ public: ~SkeletonModification3DCCDIK(); }; -#endif //SKELETONMODIFICATION3DCCDIK_H +#endif // SKELETON_MODIFICATION_3D_CCDIK_H diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index cc4d3a5e20..3d66bb6d99 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -32,8 +32,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DFABRIK_H -#define SKELETONMODIFICATION3DFABRIK_H +#ifndef SKELETON_MODIFICATION_3D_FABRIK_H +#define SKELETON_MODIFICATION_3D_FABRIK_H class SkeletonModification3DFABRIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DFABRIK, SkeletonModification3D); @@ -121,4 +121,4 @@ public: ~SkeletonModification3DFABRIK(); }; -#endif //SKELETONMODIFICATION3DFABRIK_H +#endif // SKELETON_MODIFICATION_3D_FABRIK_H diff --git a/scene/resources/skeleton_modification_3d_jiggle.h b/scene/resources/skeleton_modification_3d_jiggle.h index 7ec5ed4f11..f41ffcd58d 100644 --- a/scene/resources/skeleton_modification_3d_jiggle.h +++ b/scene/resources/skeleton_modification_3d_jiggle.h @@ -32,8 +32,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DJIGGLE_H -#define SKELETONMODIFICATION3DJIGGLE_H +#ifndef SKELETON_MODIFICATION_3D_JIGGLE_H +#define SKELETON_MODIFICATION_3D_JIGGLE_H class SkeletonModification3DJiggle : public SkeletonModification3D { GDCLASS(SkeletonModification3DJiggle, SkeletonModification3D); @@ -135,4 +135,4 @@ public: ~SkeletonModification3DJiggle(); }; -#endif //SKELETONMODIFICATION3DJIGGLE_H +#endif // SKELETON_MODIFICATION_3D_JIGGLE_H diff --git a/scene/resources/skeleton_modification_3d_lookat.h b/scene/resources/skeleton_modification_3d_lookat.h index 9f5120a0fd..4e5714b5dc 100644 --- a/scene/resources/skeleton_modification_3d_lookat.h +++ b/scene/resources/skeleton_modification_3d_lookat.h @@ -31,8 +31,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DLOOKAT_H -#define SKELETONMODIFICATION3DLOOKAT_H +#ifndef SKELETON_MODIFICATION_3D_LOOKAT_H +#define SKELETON_MODIFICATION_3D_LOOKAT_H class SkeletonModification3DLookAt : public SkeletonModification3D { GDCLASS(SkeletonModification3DLookAt, SkeletonModification3D); @@ -86,4 +86,4 @@ public: ~SkeletonModification3DLookAt(); }; -#endif //SKELETONMODIFICATION3DLOOKAT_H +#endif // SKELETON_MODIFICATION_3D_LOOKAT_H diff --git a/scene/resources/skeleton_modification_3d_stackholder.h b/scene/resources/skeleton_modification_3d_stackholder.h index 5780d7d43f..ae22099158 100644 --- a/scene/resources/skeleton_modification_3d_stackholder.h +++ b/scene/resources/skeleton_modification_3d_stackholder.h @@ -31,8 +31,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DSTACKHOLDER_H -#define SKELETONMODIFICATION3DSTACKHOLDER_H +#ifndef SKELETON_MODIFICATION_3D_STACKHOLDER_H +#define SKELETON_MODIFICATION_3D_STACKHOLDER_H class SkeletonModification3DStackHolder : public SkeletonModification3D { GDCLASS(SkeletonModification3DStackHolder, SkeletonModification3D); @@ -56,4 +56,4 @@ public: ~SkeletonModification3DStackHolder(); }; -#endif //SKELETONMODIFICATION3DSTACKHOLDER_H +#endif // SKELETON_MODIFICATION_3D_STACKHOLDER_H diff --git a/scene/resources/skeleton_modification_3d_twoboneik.h b/scene/resources/skeleton_modification_3d_twoboneik.h index a4ddc6cee7..57e8237511 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.h +++ b/scene/resources/skeleton_modification_3d_twoboneik.h @@ -31,8 +31,8 @@ #include "scene/3d/skeleton_3d.h" #include "scene/resources/skeleton_modification_3d.h" -#ifndef SKELETONMODIFICATION3DTWOBONEIK_H -#define SKELETONMODIFICATION3DTWOBONEIK_H +#ifndef SKELETON_MODIFICATION_3D_TWOBONEIK_H +#define SKELETON_MODIFICATION_3D_TWOBONEIK_H class SkeletonModification3DTwoBoneIK : public SkeletonModification3D { GDCLASS(SkeletonModification3DTwoBoneIK, SkeletonModification3D); @@ -115,4 +115,4 @@ public: ~SkeletonModification3DTwoBoneIK(); }; -#endif //SKELETONMODIFICATION3DTWOBONEIK_H +#endif // SKELETON_MODIFICATION_3D_TWOBONEIK_H diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index b944c244b6..38ec19828f 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -263,7 +263,7 @@ void SkeletonModificationStack2D::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "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 index 7b5f8e0322..ceffd71368 100644 --- a/scene/resources/skeleton_modification_stack_2d.h +++ b/scene/resources/skeleton_modification_stack_2d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATIONSTACK2D_H -#define SKELETONMODIFICATIONSTACK2D_H +#ifndef SKELETON_MODIFICATION_STACK_2D_H +#define SKELETON_MODIFICATION_STACK_2D_H #include "scene/2d/skeleton_2d.h" #include "scene/resources/skeleton_modification_2d.h" @@ -96,4 +96,4 @@ public: SkeletonModificationStack2D(); }; -#endif // SKELETONMODIFICATION2D_H +#endif // SKELETON_MODIFICATION_STACK_2D_H diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index 7ccba1228c..44fbfc934e 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -217,7 +217,7 @@ void SkeletonModificationStack3D::_bind_methods() { 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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); } SkeletonModificationStack3D::SkeletonModificationStack3D() { diff --git a/scene/resources/skeleton_modification_stack_3d.h b/scene/resources/skeleton_modification_stack_3d.h index 2c17fba2c5..4ff1bd1e33 100644 --- a/scene/resources/skeleton_modification_stack_3d.h +++ b/scene/resources/skeleton_modification_stack_3d.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SKELETONMODIFICATIONSTACK3D_H -#define SKELETONMODIFICATIONSTACK3D_H +#ifndef SKELETON_MODIFICATION_STACK_3D_H +#define SKELETON_MODIFICATION_STACK_3D_H #include "core/templates/local_vector.h" #include "scene/3d/skeleton_3d.h" @@ -88,4 +88,4 @@ public: SkeletonModificationStack3D(); }; -#endif // SKELETONMODIFICATIONSTACK3D_H +#endif // SKELETON_MODIFICATION_STACK_3D_H diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp new file mode 100644 index 0000000000..bfb4bb6e2b --- /dev/null +++ b/scene/resources/skeleton_profile.cpp @@ -0,0 +1,835 @@ +/*************************************************************************/ +/* skeleton_profile.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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_profile.h" + +bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { + ERR_FAIL_COND_V(is_read_only, false); + String path = p_path; + + if (path.begins_with("groups/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, groups.size(), false); + + if (what == "group_name") { + set_group_name(which, p_value); + } else if (what == "texture") { + set_texture(which, p_value); + } else { + return false; + } + } + + if (path.begins_with("bones/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, bones.size(), false); + + if (what == "bone_name") { + set_bone_name(which, p_value); + } else if (what == "bone_parent") { + set_bone_parent(which, p_value); + } else if (what == "tail_direction") { + set_tail_direction(which, static_cast<TailDirection>((int)p_value)); + } else if (what == "bone_tail") { + set_bone_tail(which, p_value); + } else if (what == "reference_pose") { + set_reference_pose(which, p_value); + } else if (what == "handle_offset") { + set_handle_offset(which, p_value); + } else if (what == "group") { + set_group(which, p_value); + } else if (what == "require") { + set_require(which, p_value); + } else { + return false; + } + } + return true; +} + +bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const { + String path = p_path; + + if (path.begins_with("groups/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, groups.size(), false); + + if (what == "group_name") { + r_ret = get_group_name(which); + } else if (what == "texture") { + r_ret = get_texture(which); + } else { + return false; + } + } + + if (path.begins_with("bones/")) { + int which = path.get_slicec('/', 1).to_int(); + String what = path.get_slicec('/', 2); + ERR_FAIL_INDEX_V(which, bones.size(), false); + + if (what == "bone_name") { + r_ret = get_bone_name(which); + } else if (what == "bone_parent") { + r_ret = get_bone_parent(which); + } else if (what == "tail_direction") { + r_ret = get_tail_direction(which); + } else if (what == "bone_tail") { + r_ret = get_bone_tail(which); + } else if (what == "reference_pose") { + r_ret = get_reference_pose(which); + } else if (what == "handle_offset") { + r_ret = get_handle_offset(which); + } else if (what == "group") { + r_ret = get_group(which); + } else if (what == "require") { + r_ret = is_require(which); + } else { + return false; + } + } + return true; +} + +void SkeletonProfile::_validate_property(PropertyInfo &property) const { + if (is_read_only) { + if (property.name == ("group_size") || property.name == ("bone_size") || property.name == ("root_bone") || property.name == ("scale_base_bone")) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + return; + } + } + + if (property.name == ("root_bone") || property.name == ("scale_base_bone")) { + String hint = ""; + for (int i = 0; i < bones.size(); i++) { + hint += i == 0 ? String(bones[i].bone_name) : "," + String(bones[i].bone_name); + } + property.hint_string = hint; + } + + PackedStringArray split = property.name.split("/"); + if (split.size() == 3 && split[0] == "bones") { + if (split[2] == "bone_tail" && get_tail_direction(split[1].to_int()) != TAIL_DIRECTION_SPECIFIC_CHILD) { + property.usage = PROPERTY_USAGE_NONE; + } + } +} + +void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const { + if (is_read_only) { + return; + } + String group_names = ""; + for (int i = 0; i < groups.size(); i++) { + String path = "groups/" + itos(i) + "/"; + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group_name")); + p_list->push_back(PropertyInfo(Variant::OBJECT, path + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D")); + if (i > 0) { + group_names = group_names + ","; + } + group_names = group_names + groups[i].group_name; + } + for (int i = 0; i < bones.size(); i++) { + String path = "bones/" + itos(i) + "/"; + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_name")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_parent")); + p_list->push_back(PropertyInfo(Variant::INT, path + "tail_direction", PROPERTY_HINT_ENUM, "AverageChildren,SpecificChild,End")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_tail")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, path + "reference_pose")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, path + "handle_offset")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group", PROPERTY_HINT_ENUM, group_names)); + p_list->push_back(PropertyInfo(Variant::BOOL, path + "require")); + } + + for (PropertyInfo &E : *p_list) { + _validate_property(E); + } +} + +StringName SkeletonProfile::get_root_bone() { + return root_bone; +} + +void SkeletonProfile::set_root_bone(StringName p_bone_name) { + if (is_read_only) { + return; + } + root_bone = p_bone_name; +} + +StringName SkeletonProfile::get_scale_base_bone() { + return scale_base_bone; +} + +void SkeletonProfile::set_scale_base_bone(StringName p_bone_name) { + if (is_read_only) { + return; + } + scale_base_bone = p_bone_name; +} + +int SkeletonProfile::get_group_size() { + return groups.size(); +} + +void SkeletonProfile::set_group_size(int p_size) { + if (is_read_only) { + return; + } + ERR_FAIL_COND(p_size < 0); + groups.resize(p_size); + emit_signal("profile_updated"); + notify_property_list_changed(); +} + +StringName SkeletonProfile::get_group_name(int p_group_idx) const { + ERR_FAIL_INDEX_V(p_group_idx, groups.size(), StringName()); + return groups[p_group_idx].group_name; +} + +void SkeletonProfile::set_group_name(int p_group_idx, const StringName p_group_name) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_group_idx, groups.size()); + groups.write[p_group_idx].group_name = p_group_name; + emit_signal("profile_updated"); +} + +Ref<Texture2D> SkeletonProfile::get_texture(int p_group_idx) const { + ERR_FAIL_INDEX_V(p_group_idx, groups.size(), Ref<Texture2D>()); + return groups[p_group_idx].texture; +} + +void SkeletonProfile::set_texture(int p_group_idx, const Ref<Texture2D> &p_texture) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_group_idx, groups.size()); + groups.write[p_group_idx].texture = p_texture; + emit_signal("profile_updated"); +} + +int SkeletonProfile::get_bone_size() { + return bones.size(); +} + +void SkeletonProfile::set_bone_size(int p_size) { + if (is_read_only) { + return; + } + ERR_FAIL_COND(p_size < 0); + bones.resize(p_size); + emit_signal("profile_updated"); + notify_property_list_changed(); +} + +int SkeletonProfile::find_bone(StringName p_bone_name) const { + if (p_bone_name == StringName()) { + return -1; + } + for (int i = 0; i < bones.size(); i++) { + if (bones[i].bone_name == p_bone_name) { + return i; + } + } + return -1; +} + +StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].bone_name; +} + +void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName p_bone_name) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].bone_name = p_bone_name; + emit_signal("profile_updated"); +} + +StringName SkeletonProfile::get_bone_parent(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].bone_parent; +} + +void SkeletonProfile::set_bone_parent(int p_bone_idx, const StringName p_bone_parent) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].bone_parent = p_bone_parent; + emit_signal("profile_updated"); +} + +SkeletonProfile::TailDirection SkeletonProfile::get_tail_direction(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), TAIL_DIRECTION_AVERAGE_CHILDREN); + return bones[p_bone_idx].tail_direction; +} + +void SkeletonProfile::set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].tail_direction = p_tail_direction; + emit_signal("profile_updated"); + notify_property_list_changed(); +} + +StringName SkeletonProfile::get_bone_tail(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].bone_tail; +} + +void SkeletonProfile::set_bone_tail(int p_bone_idx, const StringName p_bone_tail) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].bone_tail = p_bone_tail; + emit_signal("profile_updated"); +} + +Transform3D SkeletonProfile::get_reference_pose(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Transform3D()); + return bones[p_bone_idx].reference_pose; +} + +void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].reference_pose = p_reference_pose; + emit_signal("profile_updated"); +} + +Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Vector2()); + return bones[p_bone_idx].handle_offset; +} + +void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].handle_offset = p_handle_offset; + emit_signal("profile_updated"); +} + +StringName SkeletonProfile::get_group(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); + return bones[p_bone_idx].group; +} + +void SkeletonProfile::set_group(int p_bone_idx, const StringName p_group) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].group = p_group; + emit_signal("profile_updated"); +} + +bool SkeletonProfile::is_require(int p_bone_idx) const { + ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), false); + return bones[p_bone_idx].require; +} + +void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) { + if (is_read_only) { + return; + } + ERR_FAIL_INDEX(p_bone_idx, bones.size()); + bones.write[p_bone_idx].require = p_require; + emit_signal("profile_updated"); +} + +bool SkeletonProfile::has_bone(StringName p_bone_name) { + bool is_found = false; + for (int i = 0; i < bones.size(); i++) { + if (bones[i].bone_name == p_bone_name) { + is_found = true; + break; + } + } + return is_found; +} + +void SkeletonProfile::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_root_bone", "bone_name"), &SkeletonProfile::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonProfile::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_scale_base_bone", "bone_name"), &SkeletonProfile::set_scale_base_bone); + ClassDB::bind_method(D_METHOD("get_scale_base_bone"), &SkeletonProfile::get_scale_base_bone); + + ClassDB::bind_method(D_METHOD("set_group_size", "size"), &SkeletonProfile::set_group_size); + ClassDB::bind_method(D_METHOD("get_group_size"), &SkeletonProfile::get_group_size); + + ClassDB::bind_method(D_METHOD("get_group_name", "group_idx"), &SkeletonProfile::get_group_name); + ClassDB::bind_method(D_METHOD("set_group_name", "group_idx", "group_name"), &SkeletonProfile::set_group_name); + + ClassDB::bind_method(D_METHOD("get_texture", "group_idx"), &SkeletonProfile::get_texture); + ClassDB::bind_method(D_METHOD("set_texture", "group_idx", "texture"), &SkeletonProfile::set_texture); + + ClassDB::bind_method(D_METHOD("set_bone_size", "size"), &SkeletonProfile::set_bone_size); + ClassDB::bind_method(D_METHOD("get_bone_size"), &SkeletonProfile::get_bone_size); + + ClassDB::bind_method(D_METHOD("find_bone", "bone_name"), &SkeletonProfile::find_bone); + + ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &SkeletonProfile::get_bone_name); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "bone_name"), &SkeletonProfile::set_bone_name); + + ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &SkeletonProfile::get_bone_parent); + ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "bone_parent"), &SkeletonProfile::set_bone_parent); + + ClassDB::bind_method(D_METHOD("get_tail_direction", "bone_idx"), &SkeletonProfile::get_tail_direction); + ClassDB::bind_method(D_METHOD("set_tail_direction", "bone_idx", "tail_direction"), &SkeletonProfile::set_tail_direction); + + ClassDB::bind_method(D_METHOD("get_bone_tail", "bone_idx"), &SkeletonProfile::get_bone_tail); + ClassDB::bind_method(D_METHOD("set_bone_tail", "bone_idx", "bone_tail"), &SkeletonProfile::set_bone_tail); + + ClassDB::bind_method(D_METHOD("get_reference_pose", "bone_idx"), &SkeletonProfile::get_reference_pose); + ClassDB::bind_method(D_METHOD("set_reference_pose", "bone_idx", "bone_name"), &SkeletonProfile::set_reference_pose); + + ClassDB::bind_method(D_METHOD("get_handle_offset", "bone_idx"), &SkeletonProfile::get_handle_offset); + ClassDB::bind_method(D_METHOD("set_handle_offset", "bone_idx", "handle_offset"), &SkeletonProfile::set_handle_offset); + + ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group); + ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group); + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "scale_base_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_scale_base_bone", "get_scale_base_bone"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,groups/"), "set_group_size", "get_group_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bones/"), "set_bone_size", "get_bone_size"); + + ADD_SIGNAL(MethodInfo("profile_updated")); + + BIND_ENUM_CONSTANT(TAIL_DIRECTION_AVERAGE_CHILDREN); + BIND_ENUM_CONSTANT(TAIL_DIRECTION_SPECIFIC_CHILD); + BIND_ENUM_CONSTANT(TAIL_DIRECTION_END); +} + +SkeletonProfile::SkeletonProfile() { +} + +SkeletonProfile::~SkeletonProfile() { +} + +SkeletonProfileHumanoid::SkeletonProfileHumanoid() { + is_read_only = true; + + root_bone = "Root"; + scale_base_bone = "Hips"; + + groups.resize(4); + + groups.write[0].group_name = "Body"; + groups.write[1].group_name = "Face"; + groups.write[2].group_name = "LeftHand"; + groups.write[3].group_name = "RightHand"; + + bones.resize(56); + + bones.write[0].bone_name = "Root"; + bones.write[0].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + bones.write[0].handle_offset = Vector2(0.5, 0.91); + bones.write[0].group = "Body"; + + bones.write[1].bone_name = "Hips"; + bones.write[1].bone_parent = "Root"; + bones.write[1].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[1].bone_tail = "Spine"; + bones.write[1].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0); + bones.write[1].handle_offset = Vector2(0.5, 0.5); + bones.write[1].group = "Body"; + bones.write[1].require = true; + + bones.write[2].bone_name = "Spine"; + bones.write[2].bone_parent = "Hips"; + bones.write[2].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); + bones.write[2].handle_offset = Vector2(0.5, 0.43); + bones.write[2].group = "Body"; + bones.write[2].require = true; + + bones.write[3].bone_name = "Chest"; + bones.write[3].bone_parent = "Spine"; + bones.write[3].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); + bones.write[3].handle_offset = Vector2(0.5, 0.36); + bones.write[3].group = "Body"; + + bones.write[4].bone_name = "UpperChest"; + bones.write[4].bone_parent = "Chest"; + bones.write[4].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); + bones.write[4].handle_offset = Vector2(0.5, 0.29); + bones.write[4].group = "Body"; + + bones.write[5].bone_name = "Neck"; + bones.write[5].bone_parent = "UpperChest"; + bones.write[5].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[5].bone_tail = "Head"; + bones.write[5].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); + bones.write[5].handle_offset = Vector2(0.5, 0.23); + bones.write[5].group = "Body"; + bones.write[5].require = true; + + bones.write[6].bone_name = "Head"; + bones.write[6].bone_parent = "Neck"; + bones.write[6].tail_direction = TAIL_DIRECTION_END; + bones.write[6].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0); + bones.write[6].handle_offset = Vector2(0.5, 0.18); + bones.write[6].group = "Body"; + bones.write[6].require = true; + + bones.write[7].bone_name = "LeftEye"; + bones.write[7].bone_parent = "Head"; + bones.write[7].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, 0.05, 0.15, 0); + bones.write[7].handle_offset = Vector2(0.6, 0.46); + bones.write[7].group = "Face"; + + bones.write[8].bone_name = "RightEye"; + bones.write[8].bone_parent = "Head"; + bones.write[8].reference_pose = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, -0.05, 0.15, 0); + bones.write[8].handle_offset = Vector2(0.37, 0.46); + bones.write[8].group = "Face"; + + bones.write[9].bone_name = "Jaw"; + bones.write[9].bone_parent = "Head"; + bones.write[9].reference_pose = Transform3D(-1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0.05, 0.05); + bones.write[9].handle_offset = Vector2(0.46, 0.75); + bones.write[9].group = "Face"; + + bones.write[10].bone_name = "LeftShoulder"; + bones.write[10].bone_parent = "UpperChest"; + bones.write[10].reference_pose = Transform3D(0, 1, 0, 0, 0, 1, 1, 0, 0, 0.05, 0.1, 0); + bones.write[10].handle_offset = Vector2(0.55, 0.235); + bones.write[10].group = "Body"; + bones.write[10].require = true; + + bones.write[11].bone_name = "LeftUpperArm"; + bones.write[11].bone_parent = "LeftShoulder"; + bones.write[11].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); + bones.write[11].handle_offset = Vector2(0.6, 0.24); + bones.write[11].group = "Body"; + bones.write[11].require = true; + + bones.write[12].bone_name = "LeftLowerArm"; + bones.write[12].bone_parent = "LeftUpperArm"; + bones.write[12].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); + bones.write[12].handle_offset = Vector2(0.7, 0.24); + bones.write[12].group = "Body"; + bones.write[12].require = true; + + bones.write[13].bone_name = "LeftHand"; + bones.write[13].bone_parent = "LeftLowerArm"; + bones.write[13].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[13].bone_tail = "LeftMiddleProximal"; + bones.write[13].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); + bones.write[13].handle_offset = Vector2(0.82, 0.235); + bones.write[13].group = "Body"; + bones.write[13].require = true; + + bones.write[14].bone_name = "LeftThumbMetacarpal"; + bones.write[14].bone_parent = "LeftHand"; + bones.write[14].reference_pose = Transform3D(0, -0.577, 0.816, 0.707, 0.577, 0.408, -0.707, 0.577, 0.408, -0.025, 0, 0); + bones.write[14].handle_offset = Vector2(0.4, 0.8); + bones.write[14].group = "LeftHand"; + + bones.write[15].bone_name = "LeftThumbProximal"; + bones.write[15].bone_parent = "LeftThumbMetacarpal"; + bones.write[15].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); + bones.write[15].handle_offset = Vector2(0.3, 0.69); + bones.write[15].group = "LeftHand"; + + bones.write[16].bone_name = "LeftThumbDistal"; + bones.write[16].bone_parent = "LeftThumbProximal"; + bones.write[16].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); + bones.write[16].handle_offset = Vector2(0.23, 0.555); + bones.write[16].group = "LeftHand"; + + bones.write[17].bone_name = "LeftIndexProximal"; + bones.write[17].bone_parent = "LeftHand"; + bones.write[17].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0); + bones.write[17].handle_offset = Vector2(0.413, 0.52); + bones.write[17].group = "LeftHand"; + + bones.write[18].bone_name = "LeftIndexIntermediate"; + bones.write[18].bone_parent = "LeftIndexProximal"; + bones.write[18].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[18].handle_offset = Vector2(0.403, 0.36); + bones.write[18].group = "LeftHand"; + + bones.write[19].bone_name = "LeftIndexDistal"; + bones.write[19].bone_parent = "LeftIndexIntermediate"; + bones.write[19].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[19].handle_offset = Vector2(0.403, 0.255); + bones.write[19].group = "LeftHand"; + + bones.write[20].bone_name = "LeftMiddleProximal"; + bones.write[20].bone_parent = "LeftHand"; + bones.write[20].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); + bones.write[20].handle_offset = Vector2(0.5, 0.51); + bones.write[20].group = "LeftHand"; + + bones.write[21].bone_name = "LeftMiddleIntermediate"; + bones.write[21].bone_parent = "LeftMiddleProximal"; + bones.write[21].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); + bones.write[21].handle_offset = Vector2(0.5, 0.345); + bones.write[21].group = "LeftHand"; + + bones.write[22].bone_name = "LeftMiddleDistal"; + bones.write[22].bone_parent = "LeftMiddleIntermediate"; + bones.write[22].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[22].handle_offset = Vector2(0.5, 0.22); + bones.write[22].group = "LeftHand"; + + bones.write[23].bone_name = "LeftRingProximal"; + bones.write[23].bone_parent = "LeftHand"; + bones.write[23].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0); + bones.write[23].handle_offset = Vector2(0.586, 0.52); + bones.write[23].group = "LeftHand"; + + bones.write[24].bone_name = "LeftRingIntermediate"; + bones.write[24].bone_parent = "LeftRingProximal"; + bones.write[24].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[24].handle_offset = Vector2(0.59, 0.36); + bones.write[24].group = "LeftHand"; + + bones.write[25].bone_name = "LeftRingDistal"; + bones.write[25].bone_parent = "LeftRingIntermediate"; + bones.write[25].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[25].handle_offset = Vector2(0.591, 0.25); + bones.write[25].group = "LeftHand"; + + bones.write[26].bone_name = "LeftLittleProximal"; + bones.write[26].bone_parent = "LeftHand"; + bones.write[26].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.05, 0.05, 0); + bones.write[26].handle_offset = Vector2(0.663, 0.543); + bones.write[26].group = "LeftHand"; + + bones.write[27].bone_name = "LeftLittleIntermediate"; + bones.write[27].bone_parent = "LeftLittleProximal"; + bones.write[27].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[27].handle_offset = Vector2(0.672, 0.415); + bones.write[27].group = "LeftHand"; + + bones.write[28].bone_name = "LeftLittleDistal"; + bones.write[28].bone_parent = "LeftLittleIntermediate"; + bones.write[28].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[28].handle_offset = Vector2(0.672, 0.32); + bones.write[28].group = "LeftHand"; + + bones.write[29].bone_name = "RightShoulder"; + bones.write[29].bone_parent = "UpperChest"; + bones.write[29].reference_pose = Transform3D(0, -1, 0, 0, 0, 1, -1, 0, 0, -0.05, 0.1, 0); + bones.write[29].handle_offset = Vector2(0.45, 0.235); + bones.write[29].group = "Body"; + bones.write[29].require = true; + + bones.write[30].bone_name = "RightUpperArm"; + bones.write[30].bone_parent = "RightShoulder"; + bones.write[30].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0); + bones.write[30].handle_offset = Vector2(0.4, 0.24); + bones.write[30].group = "Body"; + bones.write[30].require = true; + + bones.write[31].bone_name = "RightLowerArm"; + bones.write[31].bone_parent = "RightUpperArm"; + bones.write[31].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0); + bones.write[31].handle_offset = Vector2(0.3, 0.24); + bones.write[31].group = "Body"; + bones.write[31].require = true; + + bones.write[32].bone_name = "RightHand"; + bones.write[32].bone_parent = "RightLowerArm"; + bones.write[32].tail_direction = TAIL_DIRECTION_SPECIFIC_CHILD; + bones.write[32].bone_tail = "RightMiddleProximal"; + bones.write[32].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0); + bones.write[32].handle_offset = Vector2(0.18, 0.235); + bones.write[32].group = "Body"; + bones.write[32].require = true; + + bones.write[33].bone_name = "RightThumbMetacarpal"; + bones.write[33].bone_parent = "RightHand"; + bones.write[33].reference_pose = Transform3D(0, 0.577, -0.816, -0.707, 0.577, 0.408, 0.707, 0.577, 0.408, 0.025, 0, 0); + bones.write[33].handle_offset = Vector2(0.6, 0.8); + bones.write[33].group = "RightHand"; + + bones.write[34].bone_name = "RightThumbProximal"; + bones.write[34].bone_parent = "RightThumbMetacarpal"; + bones.write[34].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); + bones.write[34].handle_offset = Vector2(0.7, 0.69); + bones.write[34].group = "RightHand"; + + bones.write[35].bone_name = "RightThumbDistal"; + bones.write[35].bone_parent = "RightThumbProximal"; + bones.write[35].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.043, 0); + bones.write[35].handle_offset = Vector2(0.77, 0.555); + bones.write[35].group = "RightHand"; + + bones.write[36].bone_name = "RightIndexProximal"; + bones.write[36].bone_parent = "RightHand"; + bones.write[36].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.025, 0.075, 0); + bones.write[36].handle_offset = Vector2(0.587, 0.52); + bones.write[36].group = "RightHand"; + + bones.write[37].bone_name = "RightIndexIntermediate"; + bones.write[37].bone_parent = "RightIndexProximal"; + bones.write[37].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[37].handle_offset = Vector2(0.597, 0.36); + bones.write[37].group = "RightHand"; + + bones.write[38].bone_name = "RightIndexDistal"; + bones.write[38].bone_parent = "RightIndexIntermediate"; + bones.write[38].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[38].handle_offset = Vector2(0.597, 0.255); + bones.write[38].group = "RightHand"; + + bones.write[39].bone_name = "RightMiddleProximal"; + bones.write[39].bone_parent = "RightHand"; + bones.write[39].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); + bones.write[39].handle_offset = Vector2(0.5, 0.51); + bones.write[39].group = "RightHand"; + + bones.write[40].bone_name = "RightMiddleIntermediate"; + bones.write[40].bone_parent = "RightMiddleProximal"; + bones.write[40].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.075, 0); + bones.write[40].handle_offset = Vector2(0.5, 0.345); + bones.write[40].group = "RightHand"; + + bones.write[41].bone_name = "RightMiddleDistal"; + bones.write[41].bone_parent = "RightMiddleIntermediate"; + bones.write[41].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[41].handle_offset = Vector2(0.5, 0.22); + bones.write[41].group = "RightHand"; + + bones.write[42].bone_name = "RightRingProximal"; + bones.write[42].bone_parent = "RightHand"; + bones.write[42].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.025, 0.075, 0); + bones.write[42].handle_offset = Vector2(0.414, 0.52); + bones.write[42].group = "RightHand"; + + bones.write[43].bone_name = "RightRingIntermediate"; + bones.write[43].bone_parent = "RightRingProximal"; + bones.write[43].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[43].handle_offset = Vector2(0.41, 0.36); + bones.write[43].group = "RightHand"; + + bones.write[44].bone_name = "RightRingDistal"; + bones.write[44].bone_parent = "RightRingIntermediate"; + bones.write[44].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[44].handle_offset = Vector2(0.409, 0.25); + bones.write[44].group = "RightHand"; + + bones.write[45].bone_name = "RightLittleProximal"; + bones.write[45].bone_parent = "RightHand"; + bones.write[45].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.05, 0.05, 0); + bones.write[45].handle_offset = Vector2(0.337, 0.543); + bones.write[45].group = "RightHand"; + + bones.write[46].bone_name = "RightLittleIntermediate"; + bones.write[46].bone_parent = "RightLittleProximal"; + bones.write[46].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0); + bones.write[46].handle_offset = Vector2(0.328, 0.415); + bones.write[46].group = "RightHand"; + + bones.write[47].bone_name = "RightLittleDistal"; + bones.write[47].bone_parent = "RightLittleIntermediate"; + bones.write[47].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.025, 0); + bones.write[47].handle_offset = Vector2(0.328, 0.32); + bones.write[47].group = "RightHand"; + + bones.write[48].bone_name = "LeftUpperLeg"; + bones.write[48].bone_parent = "Hips"; + bones.write[48].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, 0.1, 0, 0); + bones.write[48].handle_offset = Vector2(0.549, 0.49); + bones.write[48].group = "Body"; + bones.write[48].require = true; + + bones.write[49].bone_name = "LeftLowerLeg"; + bones.write[49].bone_parent = "LeftUpperLeg"; + bones.write[49].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); + bones.write[49].handle_offset = Vector2(0.548, 0.683); + bones.write[49].group = "Body"; + bones.write[49].require = true; + + bones.write[50].bone_name = "LeftFoot"; + bones.write[50].bone_parent = "LeftLowerLeg"; + bones.write[50].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); + bones.write[50].handle_offset = Vector2(0.545, 0.9); + bones.write[50].group = "Body"; + bones.write[50].require = true; + + bones.write[51].bone_name = "LeftToes"; + bones.write[51].bone_parent = "LeftFoot"; + bones.write[51].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0); + bones.write[51].handle_offset = Vector2(0.545, 0.95); + bones.write[51].group = "Body"; + + bones.write[52].bone_name = "RightUpperLeg"; + bones.write[52].bone_parent = "Hips"; + bones.write[52].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, -0.1, 0, 0); + bones.write[52].handle_offset = Vector2(0.451, 0.49); + bones.write[52].group = "Body"; + bones.write[52].require = true; + + bones.write[53].bone_name = "RightLowerLeg"; + bones.write[53].bone_parent = "RightUpperLeg"; + bones.write[53].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0); + bones.write[53].handle_offset = Vector2(0.452, 0.683); + bones.write[53].group = "Body"; + bones.write[53].require = true; + + bones.write[54].bone_name = "RightFoot"; + bones.write[54].bone_parent = "RightLowerLeg"; + bones.write[54].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0); + bones.write[54].handle_offset = Vector2(0.455, 0.9); + bones.write[54].group = "Body"; + bones.write[54].require = true; + + bones.write[55].bone_name = "RightToes"; + bones.write[55].bone_parent = "RightFoot"; + bones.write[55].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.15, 0); + bones.write[55].handle_offset = Vector2(0.455, 0.95); + bones.write[55].group = "Body"; +} + +SkeletonProfileHumanoid::~SkeletonProfileHumanoid() { +} + +////////////////////////////////////// diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h new file mode 100644 index 0000000000..84dfca458e --- /dev/null +++ b/scene/resources/skeleton_profile.h @@ -0,0 +1,140 @@ +/*************************************************************************/ +/* skeleton_profile.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 SKELETON_PROFILE_H +#define SKELETON_PROFILE_H + +#include "texture.h" + +class SkeletonProfile : public Resource { + GDCLASS(SkeletonProfile, Resource); + +public: + enum TailDirection { + TAIL_DIRECTION_AVERAGE_CHILDREN, + TAIL_DIRECTION_SPECIFIC_CHILD, + TAIL_DIRECTION_END + }; + +protected: + // Note: SkeletonProfileHumanoid which extends SkeletonProfile exists to unify standard bone names. + // That is what is_read_only is for, so don't make it public. + bool is_read_only = false; + + struct SkeletonProfileGroup { + StringName group_name; + Ref<Texture2D> texture; + }; + + struct SkeletonProfileBone { + StringName bone_name; + StringName bone_parent; + TailDirection tail_direction = TAIL_DIRECTION_AVERAGE_CHILDREN; + StringName bone_tail; + Transform3D reference_pose; + Vector2 handle_offset; + StringName group; + bool require = false; + }; + + StringName root_bone; + StringName scale_base_bone; + + Vector<SkeletonProfileGroup> groups; + Vector<SkeletonProfileBone> bones; + + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + virtual void _validate_property(PropertyInfo &property) const override; + void _get_property_list(List<PropertyInfo> *p_list) const; + static void _bind_methods(); + +public: + StringName get_root_bone(); + void set_root_bone(StringName p_bone_name); + + StringName get_scale_base_bone(); + void set_scale_base_bone(StringName p_bone_name); + + int get_group_size(); + void set_group_size(int p_size); + + StringName get_group_name(int p_group_idx) const; + void set_group_name(int p_group_idx, const StringName p_group_name); + + Ref<Texture2D> get_texture(int p_group_idx) const; + void set_texture(int p_group_idx, const Ref<Texture2D> &p_texture); + + int get_bone_size(); + void set_bone_size(int p_size); + + int find_bone(const StringName p_bone_name) const; + + StringName get_bone_name(int p_bone_idx) const; + void set_bone_name(int p_bone_idx, const StringName p_bone_name); + + StringName get_bone_parent(int p_bone_idx) const; + void set_bone_parent(int p_bone_idx, const StringName p_bone_parent); + + TailDirection get_tail_direction(int p_bone_idx) const; + void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction); + + StringName get_bone_tail(int p_bone_idx) const; + void set_bone_tail(int p_bone_idx, const StringName p_bone_tail); + + Transform3D get_reference_pose(int p_bone_idx) const; + void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose); + + Vector2 get_handle_offset(int p_bone_idx) const; + void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset); + + StringName get_group(int p_bone_idx) const; + void set_group(int p_bone_idx, const StringName p_group); + + bool is_require(int p_bone_idx) const; + void set_require(int p_bone_idx, const bool p_require); + + bool has_bone(StringName p_bone_name); + + SkeletonProfile(); + ~SkeletonProfile(); +}; + +class SkeletonProfileHumanoid : public SkeletonProfile { + GDCLASS(SkeletonProfileHumanoid, SkeletonProfile); + +public: + SkeletonProfileHumanoid(); + ~SkeletonProfileHumanoid(); +}; + +VARIANT_ENUM_CAST(SkeletonProfile::TailDirection); + +#endif // SKELETON_PROFILE_H diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index 54ed71999c..1c04ba0cd4 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -130,11 +130,12 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const { } void Skin::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("bind_count"), PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); 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_NO_EDITOR : PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose")); + const String prefix = vformat("%s/%d/", PNAME("bind"), i); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, prefix + PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::INT, prefix + PNAME("bone"), PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prefix + PNAME("pose"))); } } diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 9cb6a16f5c..735134e27b 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -83,7 +83,7 @@ void Sky::_bind_methods() { ClassDB::bind_method(D_METHOD("get_material"), &Sky::get_material); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial"), "set_material", "get_material"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,HighQuality,HighQualityIncremental,RealTime"), "set_process_mode", "get_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,High-Quality,High-Quality Incremental,Real-Time"), "set_process_mode", "get_process_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size"); BIND_ENUM_CONSTANT(RADIANCE_SIZE_32); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 8e633a4075..5d1a223cc7 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -71,6 +71,25 @@ float ProceduralSkyMaterial::get_sky_energy() const { return sky_energy; } +void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) { + sky_cover = p_sky_cover; + RID tex_rid = p_sky_cover.is_valid() ? p_sky_cover->get_rid() : RID(); + RS::get_singleton()->material_set_param(_get_material(), "sky_cover", tex_rid); +} + +Ref<Texture2D> ProceduralSkyMaterial::get_sky_cover() const { + return sky_cover; +} + +void ProceduralSkyMaterial::set_sky_cover_modulate(const Color &p_sky_cover_modulate) { + sky_cover_modulate = p_sky_cover_modulate; + RS::get_singleton()->material_set_param(_get_material(), "sky_cover_modulate", sky_cover_modulate); +} + +Color ProceduralSkyMaterial::get_sky_cover_modulate() const { + return sky_cover_modulate; +} + void ProceduralSkyMaterial::set_ground_bottom_color(const Color &p_ground_bottom) { ground_bottom_color = p_ground_bottom; RS::get_singleton()->material_set_param(_get_material(), "ground_bottom_color", ground_bottom_color); @@ -125,6 +144,15 @@ float ProceduralSkyMaterial::get_sun_curve() const { return sun_curve; } +void ProceduralSkyMaterial::set_use_debanding(bool p_use_debanding) { + use_debanding = p_use_debanding; + RS::get_singleton()->material_set_param(_get_material(), "use_debanding", use_debanding); +} + +bool ProceduralSkyMaterial::get_use_debanding() const { + return use_debanding; +} + Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -156,6 +184,12 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSkyMaterial::set_sky_energy); ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSkyMaterial::get_sky_energy); + ClassDB::bind_method(D_METHOD("set_sky_cover", "sky_cover"), &ProceduralSkyMaterial::set_sky_cover); + ClassDB::bind_method(D_METHOD("get_sky_cover"), &ProceduralSkyMaterial::get_sky_cover); + + ClassDB::bind_method(D_METHOD("set_sky_cover_modulate", "color"), &ProceduralSkyMaterial::set_sky_cover_modulate); + ClassDB::bind_method(D_METHOD("get_sky_cover_modulate"), &ProceduralSkyMaterial::get_sky_cover_modulate); + ClassDB::bind_method(D_METHOD("set_ground_bottom_color", "color"), &ProceduralSkyMaterial::set_ground_bottom_color); ClassDB::bind_method(D_METHOD("get_ground_bottom_color"), &ProceduralSkyMaterial::get_ground_bottom_color); @@ -174,11 +208,16 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sun_curve", "curve"), &ProceduralSkyMaterial::set_sun_curve); ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve); + ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &ProceduralSkyMaterial::set_use_debanding); + ClassDB::bind_method(D_METHOD("get_use_debanding"), &ProceduralSkyMaterial::get_use_debanding); + ADD_GROUP("Sky", "sky_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_cover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_sky_cover", "get_sky_cover"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_cover_modulate"), "set_sky_cover_modulate", "get_sky_cover_modulate"); ADD_GROUP("Ground", "ground_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); @@ -187,8 +226,11 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); ADD_GROUP("Sun", "sun_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_angle_max", "get_sun_angle_max"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01,degrees"), "set_sun_angle_max", "get_sun_angle_max"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve"); + + ADD_GROUP("", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding"); } void ProceduralSkyMaterial::cleanup_shader() { @@ -208,16 +250,26 @@ void ProceduralSkyMaterial::_update_shader() { shader_type sky; -uniform vec4 sky_top_color : hint_color = vec4(0.385, 0.454, 0.55, 1.0); -uniform vec4 sky_horizon_color : hint_color = vec4(0.646, 0.656, 0.67, 1.0); +uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); +uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); uniform float sky_curve : hint_range(0, 1) = 0.15; uniform float sky_energy = 1.0; -uniform vec4 ground_bottom_color : hint_color = vec4(0.2, 0.169, 0.133, 1.0); -uniform vec4 ground_horizon_color : hint_color = vec4(0.646, 0.656, 0.67, 1.0); +uniform sampler2D sky_cover : source_color, hint_default_black; +uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); +uniform vec4 ground_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); uniform float ground_curve : hint_range(0, 1) = 0.02; uniform float ground_energy = 1.0; uniform float sun_angle_max = 30.0; uniform float sun_curve : hint_range(0, 1) = 0.15; +uniform bool use_debanding = true; + +// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare +vec3 interleaved_gradient_noise(vec2 pos) { + const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); + float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0; + return vec3(res, -res, res) / 255.0; +} void sky() { float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); @@ -265,11 +317,17 @@ void sky() { } } + vec4 sky_cover_texture = texture(sky_cover, SKY_COORDS); + sky += (sky_cover_texture.rgb * sky_cover_modulate.rgb) * sky_cover_texture.a * sky_cover_modulate.a * sky_energy; + 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)); + if (use_debanding) { + COLOR += interleaved_gradient_noise(FRAGCOORD.xy); + } } )"); } @@ -281,6 +339,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_sky_curve(0.15); set_sky_energy(1.0); + set_sky_cover_modulate(Color(1, 1, 1)); set_ground_bottom_color(Color(0.2, 0.169, 0.133)); set_ground_horizon_color(Color(0.6463, 0.6558, 0.6708)); @@ -289,6 +348,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sun_angle_max(30.0); set_sun_curve(0.15); + set_use_debanding(true); } ProceduralSkyMaterial::~ProceduralSkyMaterial() { @@ -371,8 +431,11 @@ void PanoramaSkyMaterial::_update_shader() { // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"( // NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial. + shader_type sky; -uniform sampler2D source_panorama : %s, hint_albedo; + +uniform sampler2D source_panorama : %s, source_color, hint_default_black; + void sky() { COLOR = texture(source_panorama, SKY_COORDS).rgb; } @@ -474,13 +537,13 @@ float PhysicalSkyMaterial::get_exposure() const { return exposure; } -void PhysicalSkyMaterial::set_dither_strength(float p_dither_strength) { - dither_strength = p_dither_strength; - RS::get_singleton()->material_set_param(_get_material(), "dither_strength", dither_strength); +void PhysicalSkyMaterial::set_use_debanding(bool p_use_debanding) { + use_debanding = p_use_debanding; + RS::get_singleton()->material_set_param(_get_material(), "use_debanding", use_debanding); } -float PhysicalSkyMaterial::get_dither_strength() const { - return dither_strength; +bool PhysicalSkyMaterial::get_use_debanding() const { + return use_debanding; } void PhysicalSkyMaterial::set_night_sky(const Ref<Texture2D> &p_night_sky) { @@ -542,8 +605,8 @@ void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_exposure", "exposure"), &PhysicalSkyMaterial::set_exposure); ClassDB::bind_method(D_METHOD("get_exposure"), &PhysicalSkyMaterial::get_exposure); - ClassDB::bind_method(D_METHOD("set_dither_strength", "strength"), &PhysicalSkyMaterial::set_dither_strength); - ClassDB::bind_method(D_METHOD("get_dither_strength"), &PhysicalSkyMaterial::get_dither_strength); + ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &PhysicalSkyMaterial::set_use_debanding); + ClassDB::bind_method(D_METHOD("get_use_debanding"), &PhysicalSkyMaterial::get_use_debanding); ClassDB::bind_method(D_METHOD("set_night_sky", "night_sky"), &PhysicalSkyMaterial::set_night_sky); ClassDB::bind_method(D_METHOD("get_night_sky"), &PhysicalSkyMaterial::get_night_sky); @@ -561,7 +624,7 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); } @@ -583,18 +646,18 @@ void PhysicalSkyMaterial::_update_shader() { shader_type sky; uniform float rayleigh : hint_range(0, 64) = 2.0; -uniform vec4 rayleigh_color : hint_color = vec4(0.3, 0.405, 0.6, 1.0); +uniform vec4 rayleigh_color : source_color = vec4(0.3, 0.405, 0.6, 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.69, 0.729, 0.812, 1.0); +uniform vec4 mie_color : source_color = vec4(0.69, 0.729, 0.812, 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(0.1, 0.07, 0.034, 1.0); +uniform vec4 ground_color : source_color = vec4(0.1, 0.07, 0.034, 1.0); uniform float exposure : hint_range(0, 128) = 0.1; -uniform float dither_strength : hint_range(0, 10) = 1.0; +uniform bool use_debanding = true; -uniform sampler2D night_sky : hint_black_albedo; +uniform sampler2D night_sky : source_color, hint_default_black; const vec3 UP = vec3( 0.0, 1.0, 0.0 ); @@ -610,11 +673,11 @@ float henyey_greenstein(float cos_theta, float g) { 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)); +// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare +vec3 interleaved_gradient_noise(vec2 pos) { + const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); + float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0; + return vec3(res, -res, res) / 255.0; } void sky() { @@ -664,8 +727,9 @@ void sky() { 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; + if (use_debanding) { + COLOR += interleaved_gradient_noise(FRAGCOORD.xy); + } } else { // There is no sun, so display night_sky and nothing else. COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04; @@ -688,7 +752,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { set_sun_disk_scale(1.0); set_ground_color(Color(0.1, 0.07, 0.034)); set_exposure(0.1); - set_dither_strength(1.0); + set_use_debanding(true); } PhysicalSkyMaterial::~PhysicalSkyMaterial() { diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 7f421beb8d..61999af3c4 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -40,16 +40,19 @@ class ProceduralSkyMaterial : public Material { private: Color sky_top_color; Color sky_horizon_color; - float sky_curve; - float sky_energy; + float sky_curve = 0.0f; + float sky_energy = 0.0f; + Ref<Texture2D> sky_cover; + Color sky_cover_modulate; Color ground_bottom_color; Color ground_horizon_color; - float ground_curve; - float ground_energy; + float ground_curve = 0.0f; + float ground_energy = 0.0f; - float sun_angle_max; - float sun_curve; + float sun_angle_max = 0.0f; + float sun_curve = 0.0f; + bool use_debanding = true; static Mutex shader_mutex; static RID shader; @@ -72,6 +75,12 @@ public: void set_sky_energy(float p_energy); float get_sky_energy() const; + void set_sky_cover(const Ref<Texture2D> &p_sky_cover); + Ref<Texture2D> get_sky_cover() const; + + void set_sky_cover_modulate(const Color &p_sky_cover_modulate); + Color get_sky_cover_modulate() const; + void set_ground_bottom_color(const Color &p_ground_bottom); Color get_ground_bottom_color() const; @@ -90,6 +99,9 @@ public: void set_sun_curve(float p_curve); float get_sun_curve() const; + void set_use_debanding(bool p_use_debanding); + bool get_use_debanding() const; + virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; virtual RID get_rid() const override; @@ -146,16 +158,16 @@ private: static Mutex shader_mutex; static RID shader; - float rayleigh; + float rayleigh = 0.0f; Color rayleigh_color; - float mie; - float mie_eccentricity; + float mie = 0.0f; + float mie_eccentricity = 0.0f; Color mie_color; - float turbidity; - float sun_disk_scale; + float turbidity = 0.0f; + float sun_disk_scale = 0.0f; Color ground_color; - float exposure; - float dither_strength; + float exposure = 0.0f; + bool use_debanding = true; Ref<Texture2D> night_sky; static void _update_shader(); mutable bool shader_set = false; @@ -191,8 +203,8 @@ public: void set_exposure(float p_exposure); float get_exposure() const; - void set_dither_strength(float p_dither_strength); - float get_dither_strength() const; + void set_use_debanding(bool p_use_debanding); + bool get_use_debanding() const; void set_night_sky(const Ref<Texture2D> &p_night_sky); Ref<Texture2D> get_night_sky() const; @@ -207,4 +219,4 @@ public: ~PhysicalSkyMaterial(); }; -#endif /* !SKY_MATERIAL_H */ +#endif // SKY_MATERIAL_H diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp index ee789362c9..92efe3ce6f 100644 --- a/scene/resources/sphere_shape_3d.cpp +++ b/scene/resources/sphere_shape_3d.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "sphere_shape_3d.h" + #include "servers/physics_server_3d.h" Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const { @@ -63,6 +64,7 @@ void SphereShape3D::_update_shape() { } void SphereShape3D::set_radius(float p_radius) { + ERR_FAIL_COND_MSG(p_radius < 0, "SphereShape3D radius cannot be negative."); radius = p_radius; _update_shape(); notify_change_to_owners(); @@ -76,10 +78,10 @@ void SphereShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereShape3D::set_radius); ClassDB::bind_method(D_METHOD("get_radius"), &SphereShape3D::get_radius); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_radius", "get_radius"); } SphereShape3D::SphereShape3D() : Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SPHERE)) { - set_radius(1.0); + set_radius(0.5); } diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h index ff6d883940..91d72e01ec 100644 --- a/scene/resources/sphere_shape_3d.h +++ b/scene/resources/sphere_shape_3d.h @@ -35,7 +35,7 @@ class SphereShape3D : public Shape3D { GDCLASS(SphereShape3D, Shape3D); - float radius; + float radius = 0.5f; protected: static void _bind_methods(); @@ -52,4 +52,4 @@ public: SphereShape3D(); }; -#endif // SPHERE_SHAPE_H +#endif // SPHERE_SHAPE_3D_H diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index ece126791e..55c9d7397d 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -33,38 +33,38 @@ #include "scene/scene_string_names.h" void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) { - E->get().frames.insert(p_at_pos, p_frame); + if (p_at_pos >= 0 && p_at_pos < E->value.frames.size()) { + E->value.frames.insert(p_at_pos, p_frame); } else { - E->get().frames.push_back(p_frame); + E->value.frames.push_back(p_frame); } emit_changed(); } int SpriteFrames::get_frame_count(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().frames.size(); + return E->value.frames.size(); } void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().frames.remove_at(p_idx); + E->value.frames.remove_at(p_idx); emit_changed(); } void SpriteFrames::clear(const StringName &p_anim) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().frames.clear(); + E->value.frames.clear(); emit_changed(); } @@ -96,17 +96,6 @@ void SpriteFrames::rename_animation(const StringName &p_prev, const StringName & animations[p_next] = anim; } -Vector<String> SpriteFrames::_get_animation_list() const { - Vector<String> ret; - List<StringName> al; - get_animation_list(&al); - for (const StringName &E : al) { - ret.push_back(E); - } - - return ret; -} - void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { for (const KeyValue<StringName, Anim> &E : animations) { r_animations->push_back(E.key); @@ -124,54 +113,45 @@ Vector<String> SpriteFrames::get_animation_names() const { void SpriteFrames::set_animation_speed(const StringName &p_anim, double p_fps) { ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ")."); - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().speed = p_fps; + E->value.speed = p_fps; } double SpriteFrames::get_animation_speed(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().speed; + return E->value.speed; } void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().loop = p_loop; + E->value.loop = p_loop; } bool SpriteFrames::get_animation_loop(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().loop; -} - -void SpriteFrames::_set_frames(const Array &p_frames) { - clear_all(); - Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default); - ERR_FAIL_COND(!E); - - E->get().frames.resize(p_frames.size()); - for (int i = 0; i < E->get().frames.size(); i++) { - E->get().frames.write[i] = p_frames[i]; - } -} - -Array SpriteFrames::_get_frames() const { - return Array(); + return E->value.loop; } Array SpriteFrames::_get_animations() const { Array anims; - for (const KeyValue<StringName, Anim> &E : animations) { + + List<StringName> sorted_names; + get_animation_list(&sorted_names); + sorted_names.sort_custom<StringName::AlphCompare>(); + + for (const StringName &name : sorted_names) { + const Anim &anim = animations[name]; Dictionary d; - d["name"] = E.key; - d["speed"] = E.value.speed; - d["loop"] = E.value.loop; + d["name"] = name; + d["speed"] = anim.speed; + d["loop"] = anim.loop; Array frames; - for (int i = 0; i < E.value.frames.size(); i++) { - frames.push_back(E.value.frames[i]); + for (int i = 0; i < anim.frames.size(); i++) { + frames.push_back(anim.frames[i]); } d["frames"] = frames; anims.push_back(d); @@ -195,7 +175,7 @@ void SpriteFrames::_set_animations(const Array &p_animations) { anim.loop = d["loop"]; Array frames = d["frames"]; for (int j = 0; j < frames.size(); j++) { - RES res = frames[j]; + Ref<Resource> res = frames[j]; anim.frames.push_back(res); } @@ -225,15 +205,12 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("clear", "anim"), &SpriteFrames::clear); ClassDB::bind_method(D_METHOD("clear_all"), &SpriteFrames::clear_all); - 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, "", PROPERTY_USAGE_NONE), "_set_frames", "_get_frames"); //compatibility + // `animations` property is for serialization. ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); } SpriteFrames::SpriteFrames() { diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index 12b69afde1..87d84b70c0 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -42,16 +42,11 @@ class SpriteFrames : public Resource { Vector<Ref<Texture2D>> frames; }; - Map<StringName, Anim> animations; - - Array _get_frames() const; - void _set_frames(const Array &p_frames); + HashMap<StringName, Anim> animations; Array _get_animations() const; void _set_animations(const Array &p_animations); - Vector<String> _get_animation_list() const; - protected: static void _bind_methods(); @@ -73,24 +68,24 @@ public: void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1); int get_frame_count(const StringName &p_anim) const; _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist."); ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); - if (p_idx >= E->get().frames.size()) { + if (p_idx >= E->value.frames.size()) { return Ref<Texture2D>(); } - return E->get().frames[p_idx]; + return E->value.frames[p_idx]; } void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); ERR_FAIL_COND(p_idx < 0); - if (p_idx >= E->get().frames.size()) { + if (p_idx >= E->value.frames.size()) { return; } - E->get().frames.write[p_idx] = p_frame; + E->value.frames.write[p_idx] = p_frame; } void remove_frame(const StringName &p_anim, int p_idx); void clear(const StringName &p_anim); diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 5afd424e03..a53c299d00 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -34,10 +34,28 @@ #include <limits.h> +float StyleBox::get_style_margin(Side p_side) const { + float ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret)) { + return ret; + } + return 0; +} bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { + bool ret; + if (GDVIRTUAL_CALL(_test_mask, p_point, p_rect, ret)) { + return ret; + } + return true; } +void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const { + if (GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect)) { + return; + } +} + void StyleBox::set_default_margin(Side p_side, float p_value) { ERR_FAIL_INDEX((int)p_side, 4); @@ -74,10 +92,19 @@ Point2 StyleBox::get_offset() const { } Size2 StyleBox::get_center_size() const { + Size2 ret; + if (GDVIRTUAL_CALL(_get_center_size, ret)) { + return ret; + } + return Size2(); } Rect2 StyleBox::get_draw_rect(const Rect2 &p_rect) const { + Rect2 ret; + if (GDVIRTUAL_CALL(_get_draw_rect, p_rect, ret)) { + return ret; + } return p_rect; } @@ -95,11 +122,17 @@ void StyleBox::_bind_methods() { ClassDB::bind_method(D_METHOD("draw", "canvas_item", "rect"), &StyleBox::draw); - ADD_GROUP("Content Margin", "content_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_BOTTOM); + ADD_GROUP("Content Margins", "content_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1,suffix:px"), "set_default_margin", "get_default_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1,suffix:px"), "set_default_margin", "get_default_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1,suffix:px"), "set_default_margin", "get_default_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1,suffix:px"), "set_default_margin", "get_default_margin", SIDE_BOTTOM); + + GDVIRTUAL_BIND(_get_style_margin, "side") + GDVIRTUAL_BIND(_test_mask, "point", "rect") + GDVIRTUAL_BIND(_get_center_size) + GDVIRTUAL_BIND(_get_draw_rect, "rect") + GDVIRTUAL_BIND(_draw, "to_canvas_item", "rect") } StyleBox::StyleBox() { @@ -282,20 +315,26 @@ void StyleBoxTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode); 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_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_BOTTOM); - ADD_GROUP("Expand Margin", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM); + + ADD_GROUP("Margins", "margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_margin_size", "get_margin_size", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_margin_size", "get_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_margin_size", "get_margin_size", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_margin_size", "get_margin_size", SIDE_BOTTOM); + + ADD_GROUP("Expand Margins", "expand_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM); + ADD_GROUP("Axis Stretch", "axis_stretch_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); + + ADD_GROUP("Sub-Region", "region_"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect"); + ADD_GROUP("Modulate", "modulate_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); @@ -424,6 +463,15 @@ bool StyleBoxFlat::is_draw_center_enabled() const { return draw_center; } +void StyleBoxFlat::set_skew(Vector2 p_skew) { + skew = p_skew; + emit_changed(); +} + +Vector2 StyleBoxFlat::get_skew() const { + return skew; +} + void StyleBoxFlat::set_shadow_color(const Color &p_color) { shadow_color = p_color; emit_changed(); @@ -509,7 +557,7 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re } inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4], - const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) { + const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool fill_center = false) { int vert_offset = verts.size(); if (!vert_offset) { vert_offset = 0; @@ -553,9 +601,12 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color color = outer_color; corner_point = outer_points[corner_index]; } - real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; - real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; - verts.push_back(Vector2(x, y)); + + const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; + const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; + const float x_skew = -skew.x * (y - ring_rect.get_center().y); + const float y_skew = -skew.y * (x - ring_rect.get_center().x); + verts.push_back(Vector2(x + x_skew, y + y_skew)); colors.push_back(color); } } @@ -633,10 +684,12 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { return; } - bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); - bool aa_on = rounded_corners && anti_aliased; + const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); + // Only enable antialiasing if it is actually needed. This improve performances + // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. + const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased; - bool blend_on = blend_border && draw_border; + const bool blend_on = blend_border && draw_border; Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); Color border_color_blend = (draw_center ? bg_color : border_color_alpha); @@ -683,24 +736,24 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail); + shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew); if (draw_center) { draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, true); + shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true); } } // Create border (no AA). if (draw_border && !aa_on) { draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - border_style_rect, infill_rect, border_color_inner, border_color, corner_detail); + border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew); } // Create infill (no AA). if (draw_center && (!aa_on || blend_on || !draw_border)) { draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect, infill_rect, bg_color, bg_color, corner_detail, true); + infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true); } if (aa_on) { @@ -732,7 +785,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]); // Create infill within AA border. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, true); + infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, skew, true); } if (!blend_on || !draw_border) { @@ -743,7 +796,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { // Create infill fake AA gradient. draw_ring(verts, indices, colors, style_rect, adapted_corner, - infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail); + infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail, skew); } } @@ -757,17 +810,17 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { // Create border. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail); + border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail, skew); if (!blend_on) { // Create inner border fake AA gradient. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail); + infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail, skew); } // Create outer border fake AA gradient. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail); + style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail, skew); } } @@ -825,6 +878,9 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center); ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled); + ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew); + ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew); + ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color); ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color); @@ -846,12 +902,13 @@ void StyleBoxFlat::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew"); ADD_GROUP("Border Width", "border_width_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_BOTTOM); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM); ADD_GROUP("Border", "border_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color"); @@ -859,27 +916,27 @@ void StyleBoxFlat::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend"); ADD_GROUP("Corner Radius", "corner_radius_"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT); ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail"); - ADD_GROUP("Expand Margin", "expand_margin_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); + ADD_GROUP("Expand Margins", "expand_margin_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_shadow_size", "get_shadow_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset"), "set_shadow_offset", "get_shadow_offset"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); ADD_GROUP("Anti Aliasing", "anti_aliasing_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001"), "set_aa_size", "get_aa_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px"), "set_aa_size", "get_aa_size"); } StyleBoxFlat::StyleBoxFlat() {} @@ -944,9 +1001,9 @@ void StyleBoxLine::_bind_methods() { ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_begin", "get_grow_begin"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_end", "get_grow_end"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10,suffix:px"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 4c41f42293..9f4f69d3ba 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -44,9 +44,15 @@ class StyleBox : public Resource { float margin[4]; protected: - virtual float get_style_margin(Side p_side) const = 0; + virtual float get_style_margin(Side p_side) const; static void _bind_methods(); + GDVIRTUAL1RC(float, _get_style_margin, Side) + GDVIRTUAL2RC(bool, _test_mask, Point2, Rect2) + GDVIRTUAL0RC(Size2, _get_center_size) + GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2) + GDVIRTUAL2C(_draw, RID, Rect2) + public: virtual bool test_mask(const Point2 &p_point, const Rect2 &p_rect) const; @@ -56,7 +62,7 @@ public: virtual Size2 get_center_size() const; virtual Rect2 get_draw_rect(const Rect2 &p_rect) const; - virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const = 0; + virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const; CanvasItem *get_current_item_drawn() const; @@ -149,6 +155,7 @@ class StyleBoxFlat : public StyleBox { bool draw_center = true; bool blend_border = false; + Vector2 skew; bool anti_aliased = true; int corner_detail = 8; @@ -194,6 +201,9 @@ public: void set_draw_center(bool p_enabled); bool is_draw_center_enabled() const; + void set_skew(Vector2 p_skew); + Vector2 get_skew() const; + void set_shadow_color(const Color &p_color); Color get_shadow_color() const; @@ -254,4 +264,4 @@ public: ~StyleBoxLine(); }; -#endif +#endif // STYLE_BOX_H diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 8ff1fde2cf..9829c7e86b 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -141,7 +141,8 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) { h = hash_djb2_buffer((const uint8_t *)p_vtx.bones.ptr(), p_vtx.bones.size() * sizeof(int), h); h = hash_djb2_buffer((const uint8_t *)p_vtx.weights.ptr(), p_vtx.weights.size() * sizeof(float), h); h = hash_djb2_buffer((const uint8_t *)&p_vtx.custom[0], sizeof(Color) * RS::ARRAY_CUSTOM_COUNT, h); - h = hash_djb2_one_32(p_vtx.smooth_group, h); + h = hash_murmur3_one_32(p_vtx.smooth_group, h); + h = hash_fmix32(h); return h; } @@ -315,19 +316,17 @@ void SurfaceTool::set_uv2(const Vector2 &p_uv2) { last_uv2 = p_uv2; } -void SurfaceTool::set_custom(int p_index, const Color &p_custom) { - ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT); +void SurfaceTool::set_custom(int p_channel_index, const Color &p_custom) { + ERR_FAIL_INDEX(p_channel_index, RS::ARRAY_CUSTOM_COUNT); ERR_FAIL_COND(!begun); - ERR_FAIL_COND(last_custom_format[p_index] == CUSTOM_MAX); + ERR_FAIL_COND(last_custom_format[p_channel_index] == CUSTOM_MAX); static const uint32_t mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 }; - static const uint32_t shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; - ERR_FAIL_COND(!first && !(format & mask[p_index])); + ERR_FAIL_COND(!first && !(format & mask[p_channel_index])); if (first) { - format |= mask[p_index]; - format |= last_custom_format[p_index] << shift[p_index]; + format |= mask[p_channel_index]; } - last_custom[p_index] = p_custom; + last_custom[p_channel_index] = p_custom; } void SurfaceTool::set_bones(const Vector<int> &p_bones) { @@ -689,7 +688,7 @@ Array SurfaceTool::commit_to_arrays() { return a; } -Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_flags) { +Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_compress_flags) { Ref<ArrayMesh> mesh; if (p_existing.is_valid()) { mesh = p_existing; @@ -707,7 +706,15 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_ Array a = commit_to_arrays(); - mesh->add_surface_from_arrays(primitive, a, Array(), Dictionary(), p_flags); + uint32_t compress_flags = (p_compress_flags >> RS::ARRAY_COMPRESS_FLAGS_BASE) << RS::ARRAY_COMPRESS_FLAGS_BASE; + static const uint32_t shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { + if (last_custom_format[i] != CUSTOM_MAX) { + compress_flags |= last_custom_format[i] << shift[i]; + } + } + + mesh->add_surface_from_arrays(primitive, a, Array(), Dictionary(), compress_flags); if (material.is_valid()) { mesh->surface_set_material(surface, material); @@ -988,9 +995,6 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) { if (format & custom_mask[j]) { CustomFormat new_format = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK); - if (last_custom_format[j] != CUSTOM_MAX && last_custom_format[j] != new_format) { - WARN_PRINT(vformat("Custom %d format %d mismatch when appending format %d", j, last_custom_format[j], new_format)); - } last_custom_format[j] = new_format; } } @@ -1165,7 +1169,7 @@ void SurfaceTool::generate_normals(bool p_flip) { for (int i = 0; i < 3; i++) { Vector3 *lv = vertex_hash.getptr(v[i]); if (!lv) { - vertex_hash.set(v[i], normal); + vertex_hash.insert(v[i], normal); } else { (*lv) += normal; } @@ -1220,22 +1224,24 @@ SurfaceTool::SkinWeightCount SurfaceTool::get_skin_weight_count() const { return skin_weights; } -void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) { - ERR_FAIL_INDEX(p_index, RS::ARRAY_CUSTOM_COUNT); - ERR_FAIL_COND(begun); - last_custom_format[p_index] = p_format; +void SurfaceTool::set_custom_format(int p_channel_index, CustomFormat p_format) { + ERR_FAIL_INDEX(p_channel_index, RS::ARRAY_CUSTOM_COUNT); + ERR_FAIL_COND(!begun); + ERR_FAIL_INDEX(p_format, CUSTOM_MAX + 1); + last_custom_format[p_channel_index] = p_format; } -Mesh::PrimitiveType SurfaceTool::get_primitive() const { +Mesh::PrimitiveType SurfaceTool::get_primitive_type() const { return primitive; } -SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const { - ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX); - return last_custom_format[p_index]; +SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_channel_index) const { + ERR_FAIL_INDEX_V(p_channel_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX); + return last_custom_format[p_channel_index]; } void SurfaceTool::optimize_indices_for_cache() { ERR_FAIL_COND(optimize_vertex_cache_func == nullptr); ERR_FAIL_COND(index_array.size() == 0); + ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES); ERR_FAIL_COND(index_array.size() % 3 != 0); LocalVector old_index_array = index_array; @@ -1243,8 +1249,8 @@ void SurfaceTool::optimize_indices_for_cache() { optimize_vertex_cache_func((unsigned int *)index_array.ptr(), (unsigned int *)old_index_array.ptr(), old_index_array.size(), vertex_array.size()); } -float SurfaceTool::get_max_axis_length() const { - ERR_FAIL_COND_V(vertex_array.size() == 0, 0); +AABB SurfaceTool::get_aabb() const { + ERR_FAIL_COND_V(vertex_array.size() == 0, AABB()); AABB aabb; for (uint32_t i = 0; i < vertex_array.size(); i++) { @@ -1255,7 +1261,7 @@ float SurfaceTool::get_max_axis_length() const { } } - return aabb.get_longest_axis_size(); + return aabb; } Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_count) { Vector<int> lod; @@ -1288,8 +1294,8 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skin_weight_count", "count"), &SurfaceTool::set_skin_weight_count); ClassDB::bind_method(D_METHOD("get_skin_weight_count"), &SurfaceTool::get_skin_weight_count); - ClassDB::bind_method(D_METHOD("set_custom_format", "index", "format"), &SurfaceTool::set_custom_format); - ClassDB::bind_method(D_METHOD("get_custom_format", "index"), &SurfaceTool::get_custom_format); + ClassDB::bind_method(D_METHOD("set_custom_format", "channel_index", "format"), &SurfaceTool::set_custom_format); + ClassDB::bind_method(D_METHOD("get_custom_format", "channel_index"), &SurfaceTool::get_custom_format); ClassDB::bind_method(D_METHOD("begin", "primitive"), &SurfaceTool::begin); @@ -1301,7 +1307,7 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("set_uv2", "uv2"), &SurfaceTool::set_uv2); ClassDB::bind_method(D_METHOD("set_bones", "bones"), &SurfaceTool::set_bones); ClassDB::bind_method(D_METHOD("set_weights", "weights"), &SurfaceTool::set_weights); - ClassDB::bind_method(D_METHOD("set_custom", "index", "custom"), &SurfaceTool::set_custom); + ClassDB::bind_method(D_METHOD("set_custom", "channel_index", "custom_color"), &SurfaceTool::set_custom); ClassDB::bind_method(D_METHOD("set_smooth_group", "index"), &SurfaceTool::set_smooth_group); ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertices", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>())); @@ -1315,11 +1321,11 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("optimize_indices_for_cache"), &SurfaceTool::optimize_indices_for_cache); - ClassDB::bind_method(D_METHOD("get_max_axis_length"), &SurfaceTool::get_max_axis_length); + ClassDB::bind_method(D_METHOD("get_aabb"), &SurfaceTool::get_aabb); ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3)); ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material); - ClassDB::bind_method(D_METHOD("get_primitive"), &SurfaceTool::get_primitive); + ClassDB::bind_method(D_METHOD("get_primitive_type"), &SurfaceTool::get_primitive_type); ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear); diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index bf4332ad2a..6735d6623f 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -154,10 +154,10 @@ public: void set_skin_weight_count(SkinWeightCount p_weights); SkinWeightCount get_skin_weight_count() const; - void set_custom_format(int p_index, CustomFormat p_format); - CustomFormat get_custom_format(int p_index) const; + void set_custom_format(int p_channel_index, CustomFormat p_format); + CustomFormat get_custom_format(int p_channel_index) const; - Mesh::PrimitiveType get_primitive() const; + Mesh::PrimitiveType get_primitive_type() const; void begin(Mesh::PrimitiveType p_primitive); @@ -166,7 +166,7 @@ public: void set_tangent(const Plane &p_tangent); void set_uv(const Vector2 &p_uv); void set_uv2(const Vector2 &p_uv2); - void set_custom(int p_index, const Color &p_custom); + void set_custom(int p_channel_index, const Color &p_custom); void set_bones(const Vector<int> &p_bones); void set_weights(const Vector<float> &p_weights); void set_smooth_group(uint32_t p_group); @@ -183,7 +183,7 @@ public: void generate_tangents(); void optimize_indices_for_cache(); - float get_max_axis_length() const; + AABB get_aabb() const; Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3); void set_material(const Ref<Material> &p_material); @@ -199,7 +199,7 @@ public: 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 Transform3D &p_xform); - Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = 0); + Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_compress_flags = 0); SurfaceTool(); }; @@ -207,4 +207,4 @@ public: VARIANT_ENUM_CAST(SurfaceTool::CustomFormat) VARIANT_ENUM_CAST(SurfaceTool::SkinWeightCount) -#endif +#endif // SURFACE_TOOL_H diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index 143f1679c6..69131ffbee 100644 --- a/scene/resources/syntax_highlighter.h +++ b/scene/resources/syntax_highlighter.h @@ -41,7 +41,7 @@ class SyntaxHighlighter : public Resource { GDCLASS(SyntaxHighlighter, Resource) private: - Map<int, Dictionary> highlighting_cache; + RBMap<int, Dictionary> highlighting_cache; void _lines_edited_from(int p_from_line, int p_to_line); protected: @@ -83,7 +83,7 @@ private: bool line_only = false; }; Vector<ColorRegion> color_regions; - Map<int, int> color_region_cache; + HashMap<int, int> color_region_cache; Dictionary keywords; Dictionary member_keywords; @@ -142,4 +142,4 @@ public: Color get_member_variable_color() const; }; -#endif +#endif // SYNTAX_HIGHLIGHTER_H diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp index cbfee754e2..0404e1f79b 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -51,7 +51,7 @@ void TextFile::reload_from_file() { Error TextFile::load_text(const String &p_path) { Vector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'."); @@ -59,13 +59,12 @@ Error TextFile::load_text(const String &p_path) { sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); uint64_t r = f->get_buffer(w, len); - f->close(); - memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); w[len] = 0; String s; - ERR_FAIL_COND_V_MSG(s.parse_utf8((const char *)w), ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode."); + ERR_FAIL_COND_V_MSG(s.parse_utf8((const char *)w) != OK, ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode."); text = s; path = p_path; return OK; diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h index 0c8cf855f0..168e4f5879 100644 --- a/scene/resources/text_file.h +++ b/scene/resources/text_file.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTFILE_H -#define TEXTFILE_H +#ifndef TEXT_FILE_H +#define TEXT_FILE_H #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" @@ -51,4 +51,4 @@ public: Error load_text(const String &p_path); }; -#endif // TEXTFILE_H +#endif // TEXT_FILE_H diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index c3b5bd3564..823d742d72 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -36,7 +36,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction); ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction); - ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Left-to-right,Right-to-left"), "set_direction", "get_direction"); ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextLine::set_orientation); ClassDB::bind_method(D_METHOD("get_orientation"), &TextLine::get_orientation); @@ -55,7 +55,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextLine::add_string, DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -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 Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_flags", "get_flags"); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextLine::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextLine::get_text_overrun_behavior); @@ -98,12 +98,6 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color"), &TextLine::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextLine::hit_test); - - BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS); } void TextLine::_shape() { @@ -112,32 +106,32 @@ void TextLine::_shape() { TS->shaped_text_tab_align(rid, tab_stops); } - uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; - if (overrun_behavior != OVERRUN_NO_TRIMMING) { + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; + if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { - case OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + case TextServer::OVERRUN_TRIM_WORD: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; - case OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + case TextServer::OVERRUN_TRIM_CHAR: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; - case OVERRUN_NO_TRIMMING: + case TextServer::OVERRUN_NO_TRIMMING: break; } if (alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(rid, width, flags); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); } else { TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags); @@ -155,8 +149,6 @@ RID TextLine::get_rid() const { void TextLine::clear() { TS->shaped_text_clear(rid); - spacing_top = 0; - spacing_bottom = 0; } void TextLine::set_preserve_invalid(bool p_enabled) { @@ -200,11 +192,12 @@ void TextLine::set_bidi_override(const Array &p_override) { dirty = true; } -bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { - ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); +bool TextLine::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) { + ERR_FAIL_COND_V(p_font.is_null(), false); + bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } dirty = true; return res; } @@ -248,31 +241,31 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) { dirty = true; } -void TextLine::set_flags(uint16_t p_flags) { +void TextLine::set_flags(BitField<TextServer::JustificationFlag> p_flags) { if (flags != p_flags) { flags = p_flags; dirty = true; } } -uint16_t TextLine::get_flags() const { +BitField<TextServer::JustificationFlag> TextLine::get_flags() const { return flags; } -void TextLine::set_text_overrun_behavior(TextLine::OverrunBehavior p_behavior) { +void TextLine::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { if (overrun_behavior != p_behavior) { overrun_behavior = p_behavior; dirty = true; } } -TextLine::OverrunBehavior TextLine::get_text_overrun_behavior() const { +TextServer::OverrunBehavior TextLine::get_text_overrun_behavior() const { return overrun_behavior; } void TextLine::set_width(float p_width) { width = p_width; - if (alignment == HORIZONTAL_ALIGNMENT_FILL || overrun_behavior != OVERRUN_NO_TRIMMING) { + if (alignment == HORIZONTAL_ALIGNMENT_FILL || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { dirty = true; } } @@ -284,20 +277,20 @@ float TextLine::get_width() const { Size2 TextLine::get_size() const { const_cast<TextLine *>(this)->_shape(); if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } else { - return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } } float TextLine::get_line_ascent() const { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_get_ascent(rid) + spacing_top; + return TS->shaped_text_get_ascent(rid); } float TextLine::get_line_descent() const { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_get_descent(rid) + spacing_bottom; + return TS->shaped_text_get_descent(rid); } float TextLine::get_line_width() const { @@ -327,10 +320,18 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((width - length) / 2.0); - } else { - ofs.y += Math::floor((width - length) / 2.0); + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -345,10 +346,10 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co float clip_l; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.x - ofs.x); } else { - ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color); @@ -366,10 +367,18 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((width - length) / 2.0); - } else { - ofs.y += Math::floor((width - length) / 2.0); + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -384,10 +393,10 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si float clip_l; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.x - ofs.x); } else { - ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color); @@ -399,11 +408,14 @@ int TextLine::hit_test(float p_coords) const { return TS->shaped_text_hit_test_position(rid, p_coords); } -TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +TextLine::TextLine(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); - TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + if (p_font.is_valid()) { + TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } + } } TextLine::TextLine() { @@ -411,5 +423,5 @@ TextLine::TextLine() { } TextLine::~TextLine() { - TS->free(rid); + TS->free_rid(rid); } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index c5762db0f2..e70e82cf2b 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -39,26 +39,15 @@ class TextLine : public RefCounted { GDCLASS(TextLine, RefCounted); -public: - enum OverrunBehavior { - OVERRUN_NO_TRIMMING, - OVERRUN_TRIM_CHAR, - OVERRUN_TRIM_WORD, - OVERRUN_TRIM_ELLIPSIS, - OVERRUN_TRIM_WORD_ELLIPSIS, - }; - private: RID rid; - int spacing_top = 0; - int spacing_bottom = 0; bool dirty = true; float width = -1.0; - uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + BitField<TextServer::JustificationFlag> flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; - OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS; + TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_TRIM_ELLIPSIS; Vector<float> tab_stops; @@ -86,7 +75,7 @@ public: void set_preserve_control(bool p_enabled); bool get_preserve_control() const; - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); + bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); @@ -95,11 +84,11 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint16_t p_flags); - uint16_t get_flags() const; + void set_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_flags() const; - void set_text_overrun_behavior(OverrunBehavior p_behavior); - OverrunBehavior get_text_overrun_behavior() const; + void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior); + TextServer::OverrunBehavior get_text_overrun_behavior() const; void set_width(float p_width); float get_width() const; @@ -120,11 +109,9 @@ public: int hit_test(float p_coords) const; - TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + TextLine(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextLine(); ~TextLine(); }; -VARIANT_ENUM_CAST(TextLine::OverrunBehavior); - #endif // TEXT_LINE_H diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 4d75874199..43d3f329fa 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -60,10 +60,10 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override); - ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_dropcap", "text", "font", "font_size", "dropcap_margins", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL("")); ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -74,10 +74,15 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align); - ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags); - ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags); + ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags); + ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive"), "set_break_flags", "get_break_flags"); + + ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags); + ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification,Word Justification,Trim Edge Spaces After Justification,Justify Only After Last Tab,Constrain Ellipsis"), "set_justification_flags", "get_justification_flags"); ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior); @@ -113,9 +118,6 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position); ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness); - ClassDB::bind_method(D_METHOD("get_spacing_top"), &TextParagraph::get_spacing_top); - ClassDB::bind_method(D_METHOD("get_spacing_bottom"), &TextParagraph::get_spacing_bottom); - ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size); ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines); @@ -129,18 +131,12 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test); - - BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS); } void TextParagraph::_shape_lines() { if (lines_dirty) { - for (int i = 0; i < lines_rid.size(); i++) { - TS->free(lines_rid[i]); + for (int i = 0; i < (int)lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); } lines_rid.clear(); @@ -163,12 +159,12 @@ void TextParagraph::_shape_lines() { if (h_offset > 0) { // Dropcap, flow around. - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, brk_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x; if (v_offset < h) { - TS->free(line); + TS->free_rid(line); break; } if (!tab_stops.is_empty()) { @@ -181,7 +177,7 @@ void TextParagraph::_shape_lines() { } } // Use fixed for the rest of lines. - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, brk_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); if (!tab_stops.is_empty()) { @@ -190,43 +186,43 @@ void TextParagraph::_shape_lines() { lines_rid.push_back(line); } - uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; - if (overrun_behavior != OVERRUN_NO_TRIMMING) { + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; + if (overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { switch (overrun_behavior) { - case OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + case TextServer::OVERRUN_TRIM_WORD: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; - case OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + case TextServer::OVERRUN_TRIM_CHAR: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; - case OVERRUN_NO_TRIMMING: + case TextServer::OVERRUN_NO_TRIMMING: break; } } - bool autowrap_enabled = ((flags & TextServer::BREAK_WORD_BOUND) == TextServer::BREAK_WORD_BOUND) || ((flags & TextServer::BREAK_GRAPHEME_BOUND) == TextServer::BREAK_GRAPHEME_BOUND); + bool autowrap_enabled = brk_flags.has_flag(TextServer::BREAK_WORD_BOUND) || brk_flags.has_flag(TextServer::BREAK_GRAPHEME_BOUND); // Fill after min_size calculation. if (autowrap_enabled) { - int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); - bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); + int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); + bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size(); if (lines_hidden) { - overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - for (int i = 0; i < lines_rid.size(); i++) { - if (i < visible_lines - 1 || lines_rid.size() == 1) { - TS->shaped_text_fit_to_width(lines_rid[i], width, flags); + for (int i = 0; i < (int)lines_rid.size(); i++) { + if (i < visible_lines - 1 || (int)lines_rid.size() == 1) { + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); } else if (i == (visible_lines - 1)) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } @@ -238,12 +234,12 @@ void TextParagraph::_shape_lines() { } else { // Autowrap disabled. - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - TS->shaped_text_fit_to_width(lines_rid[i], width, flags); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags); + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); - TS->shaped_text_fit_to_width(lines_rid[i], width, flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); + TS->shaped_text_fit_to_width(lines_rid[i], width, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); } @@ -258,8 +254,10 @@ RID TextParagraph::get_rid() const { } RID TextParagraph::get_line_rid(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), RID()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), RID()); return lines_rid[p_line]; } @@ -268,10 +266,10 @@ RID TextParagraph::get_dropcap_rid() const { } void TextParagraph::clear() { - spacing_top = 0; - spacing_bottom = 0; - for (int i = 0; i < lines_rid.size(); i++) { - TS->free(lines_rid[i]); + _THREAD_SAFE_METHOD_ + + for (int i = 0; i < (int)lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); } lines_rid.clear(); TS->shaped_text_clear(rid); @@ -279,106 +277,133 @@ void TextParagraph::clear() { } void TextParagraph::set_preserve_invalid(bool p_enabled) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_preserve_invalid(rid, p_enabled); TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled); lines_dirty = true; } bool TextParagraph::get_preserve_invalid() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_preserve_invalid(rid); } void TextParagraph::set_preserve_control(bool p_enabled) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_preserve_control(rid, p_enabled); TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled); lines_dirty = true; } bool TextParagraph::get_preserve_control() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_preserve_control(rid); } void TextParagraph::set_direction(TextServer::Direction p_direction) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_direction(rid, p_direction); TS->shaped_text_set_direction(dropcap_rid, p_direction); lines_dirty = true; } TextServer::Direction TextParagraph::get_direction() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); return TS->shaped_text_get_direction(rid); } void TextParagraph::set_custom_punctuation(const String &p_punct) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_custom_punctuation(rid, p_punct); lines_dirty = true; } String TextParagraph::get_custom_punctuation() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_custom_punctuation(rid); } void TextParagraph::set_orientation(TextServer::Orientation p_orientation) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_orientation(rid, p_orientation); TS->shaped_text_set_orientation(dropcap_rid, p_orientation); lines_dirty = true; } TextServer::Orientation TextParagraph::get_orientation() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); return TS->shaped_text_get_orientation(rid); } -bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { - ERR_FAIL_COND_V(p_fonts.is_null(), false); +bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins, const String &p_language) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(p_font.is_null(), false); TS->shaped_text_clear(dropcap_rid); dropcap_margins = p_dropcap_margins; - bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(dropcap_rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } lines_dirty = true; return res; } void TextParagraph::clear_dropcap() { + _THREAD_SAFE_METHOD_ dropcap_margins = Rect2(); TS->shaped_text_clear(dropcap_rid); lines_dirty = true; } -bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { - ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); +bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(p_font.is_null(), false); + bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } lines_dirty = true; return res; } -int TextParagraph::get_spacing_top() const { - return spacing_top; -} - -int TextParagraph::get_spacing_bottom() const { - return spacing_bottom; -} - void TextParagraph::set_bidi_override(const Array &p_override) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_bidi_override(rid, p_override); lines_dirty = true; } bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { + _THREAD_SAFE_METHOD_ + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); lines_dirty = true; return res; } bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { + _THREAD_SAFE_METHOD_ + bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); lines_dirty = true; return res; } void TextParagraph::set_alignment(HorizontalAlignment p_alignment) { + _THREAD_SAFE_METHOD_ + if (alignment != p_alignment) { if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { alignment = p_alignment; @@ -394,33 +419,54 @@ HorizontalAlignment TextParagraph::get_alignment() const { } void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { + _THREAD_SAFE_METHOD_ + tab_stops = p_tab_stops; lines_dirty = true; } -void TextParagraph::set_flags(uint16_t p_flags) { - if (flags != p_flags) { - flags = p_flags; +void TextParagraph::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { + _THREAD_SAFE_METHOD_ + + if (jst_flags != p_flags) { + jst_flags = p_flags; + lines_dirty = true; + } +} + +BitField<TextServer::JustificationFlag> TextParagraph::get_justification_flags() const { + return jst_flags; +} + +void TextParagraph::set_break_flags(BitField<TextServer::LineBreakFlag> p_flags) { + _THREAD_SAFE_METHOD_ + + if (brk_flags != p_flags) { + brk_flags = p_flags; lines_dirty = true; } } -uint16_t TextParagraph::get_flags() const { - return flags; +BitField<TextServer::LineBreakFlag> TextParagraph::get_break_flags() const { + return brk_flags; } -void TextParagraph::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) { +void TextParagraph::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { + _THREAD_SAFE_METHOD_ + if (overrun_behavior != p_behavior) { overrun_behavior = p_behavior; lines_dirty = true; } } -TextParagraph::OverrunBehavior TextParagraph::get_text_overrun_behavior() const { +TextServer::OverrunBehavior TextParagraph::get_text_overrun_behavior() const { return overrun_behavior; } void TextParagraph::set_width(float p_width) { + _THREAD_SAFE_METHOD_ + if (width != p_width) { width = p_width; lines_dirty = true; @@ -432,25 +478,29 @@ float TextParagraph::get_width() const { } Size2 TextParagraph::get_non_wrapped_size() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } else { - return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } } Size2 TextParagraph::get_size() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Size2 size; - int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); + int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); for (int i = 0; i < visible_lines; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { size.x = MAX(size.x, lsize.x); - size.y += lsize.y + spacing_top + spacing_bottom; + size.y += lsize.y; } else { - size.x += lsize.x + spacing_top + spacing_bottom; + size.x += lsize.x; size.y = MAX(size.y, lsize.y); } } @@ -458,11 +508,15 @@ Size2 TextParagraph::get_size() const { } int TextParagraph::get_line_count() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - return lines_rid.size(); + return (int)lines_rid.size(); } void TextParagraph::set_max_lines_visible(int p_lines) { + _THREAD_SAFE_METHOD_ + if (p_lines != max_lines_visible) { max_lines_visible = p_lines; lines_dirty = true; @@ -474,73 +528,93 @@ int TextParagraph::get_max_lines_visible() const { } Array TextParagraph::get_line_objects(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Array()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Array()); return TS->shaped_text_get_objects(lines_rid[p_line]); } Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Rect2()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2()); Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key); for (int i = 0; i < p_line; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - xrect.position.y += lsize.y + spacing_top + spacing_bottom; + xrect.position.y += lsize.y; } else { - xrect.position.x += lsize.x + spacing_top + spacing_bottom; + xrect.position.x += lsize.x; } } return xrect; } Size2 TextParagraph::get_line_size(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Size2()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2()); if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y); } else { - return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines_rid[p_line]).y); + return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y); } } Vector2i TextParagraph::get_line_range(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Vector2i()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Vector2i()); return TS->shaped_text_get_range(lines_rid[p_line]); } float TextParagraph::get_line_ascent(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); - return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); + return TS->shaped_text_get_ascent(lines_rid[p_line]); } float TextParagraph::get_line_descent(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); - return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom; + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); + return TS->shaped_text_get_descent(lines_rid[p_line]); } float TextParagraph::get_line_width(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_width(lines_rid[p_line]); } float TextParagraph::get_line_underline_position(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_underline_position(lines_rid[p_line]); } float TextParagraph::get_line_underline_thickness(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_underline_thickness(lines_rid[p_line]); } Size2 TextParagraph::get_dropcap_size() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position; } @@ -549,6 +623,8 @@ int TextParagraph::get_dropcap_lines() const { } void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs = p_pos; float h_offset = 0.f; @@ -571,13 +647,13 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color); } - int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); + int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); for (int i = 0; i < lines_visible; i++) { float l_width = width; if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -586,7 +662,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo } } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -609,10 +685,18 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((l_width - line_width) / 2.0); - } else { - ofs.y += Math::floor((l_width - line_width) / 2.0); + if (line_width <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - line_width) / 2.0); + } else { + ofs.y += Math::floor((l_width - line_width) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - line_width; + } else { + ofs.y += l_width - line_width; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -633,15 +717,17 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]); } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]); } } } void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs = p_pos; @@ -665,11 +751,11 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color); } - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { float l_width = width; if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -678,7 +764,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli } } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -701,10 +787,18 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((l_width - length) / 2.0); - } else { - ofs.y += Math::floor((l_width - length) / 2.0); + if (length <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - length) / 2.0); + } else { + ofs.y += Math::floor((l_width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -725,15 +819,17 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]); } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]); } } } int TextParagraph::hit_test(const Point2 &p_coords) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -745,23 +841,25 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { return 0; } } - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) { return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).y + spacing_bottom + spacing_top; + ofs.y += TS->shaped_text_get_size(lines_rid[i]).y; } else { if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) { return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).x + spacing_bottom + spacing_top; + ofs.y += TS->shaped_text_get_size(lines_rid[i]).x; } } return TS->shaped_text_get_range(rid).y; } void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + Vector2 ofs = p_pos; float h_offset = 0.f; if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -784,6 +882,8 @@ void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color } void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + Vector2 ofs = p_pos; float h_offset = 0.f; if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -806,37 +906,44 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int } void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size()); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); Vector2 ofs = p_pos; if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); } else { - ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); } return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color); } void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size()); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); Vector2 ofs = p_pos; if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); } else { - ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); } return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color); } -TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); - TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); + if (p_font.is_valid()) { + TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } + } width = p_width; } @@ -846,10 +953,10 @@ TextParagraph::TextParagraph() { } TextParagraph::~TextParagraph() { - for (int i = 0; i < lines_rid.size(); i++) { - TS->free(lines_rid[i]); + for (int i = 0; i < (int)lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); } lines_rid.clear(); - TS->free(rid); - TS->free(dropcap_rid); + TS->free_rid(rid); + TS->free_rid(dropcap_rid); } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 8a8a53943b..0fe82b4364 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -31,6 +31,7 @@ #ifndef TEXT_PARAGRAPH_H #define TEXT_PARAGRAPH_H +#include "core/templates/local_vector.h" #include "scene/resources/font.h" #include "servers/text_server.h" @@ -38,15 +39,7 @@ class TextParagraph : public RefCounted { GDCLASS(TextParagraph, RefCounted); - -public: - enum OverrunBehavior { - OVERRUN_NO_TRIMMING, - OVERRUN_TRIM_CHAR, - OVERRUN_TRIM_WORD, - OVERRUN_TRIM_ELLIPSIS, - OVERRUN_TRIM_WORD_ELLIPSIS, - }; + _THREAD_SAFE_CLASS_ private: RID dropcap_rid; @@ -54,17 +47,16 @@ private: Rect2 dropcap_margins; RID rid; - Vector<RID> lines_rid; - int spacing_top = 0; - int spacing_bottom = 0; + LocalVector<RID> lines_rid; bool lines_dirty = true; float width = -1.0; int max_lines_visible = -1; - uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; - OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING; + BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND; + BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA; + TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; @@ -99,10 +91,10 @@ public: void set_custom_punctuation(const String &p_punct); String get_custom_punctuation() const; - bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins = Rect2(), const String &p_language = ""); void clear_dropcap(); - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); + bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); @@ -111,11 +103,14 @@ public: void tab_align(const Vector<float> &p_tab_stops); - void set_flags(uint16_t p_flags); - uint16_t get_flags() const; + void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags); + BitField<TextServer::JustificationFlag> get_justification_flags() const; + + void set_break_flags(BitField<TextServer::LineBreakFlag> p_flags); + BitField<TextServer::LineBreakFlag> get_break_flags() const; - void set_text_overrun_behavior(OverrunBehavior p_behavior); - OverrunBehavior get_text_overrun_behavior() const; + void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior); + TextServer::OverrunBehavior get_text_overrun_behavior() const; void set_width(float p_width); float get_width() const; @@ -156,11 +151,11 @@ public: int hit_test(const Point2 &p_coords) const; - TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + Mutex &get_mutex() const { return _thread_safe_; }; + + TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); ~TextParagraph(); }; -VARIANT_ENUM_CAST(TextParagraph::OverrunBehavior); - #endif // TEXT_PARAGRAPH_H diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 0ee0e4b33e..0aefe34f7d 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -32,29 +32,67 @@ #include "core/core_string_names.h" #include "core/io/image_loader.h" +#include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/os/os.h" #include "mesh.h" #include "scene/resources/bit_map.h" #include "servers/camera/camera_feed.h" +int Texture2D::get_width() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { + return ret; + } + return 0; +} + +int Texture2D::get_height() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { + return ret; + } + return 0; +} Size2 Texture2D::get_size() const { return Size2(get_width(), get_height()); } bool Texture2D::is_pixel_opaque(int p_x, int p_y) const { + bool ret; + if (GDVIRTUAL_CALL(_is_pixel_opaque, p_x, p_y, ret)) { + return ret; + } + + return true; +} +bool Texture2D::has_alpha() const { + bool ret; + if (GDVIRTUAL_CALL(_has_alpha, ret)) { + return ret; + } + return true; } void Texture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { + if (GDVIRTUAL_CALL(_draw, p_canvas_item, p_pos, p_modulate, p_transpose)) { + return; + } RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, get_size()), get_rid(), false, p_modulate, p_transpose); } void Texture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { + if (GDVIRTUAL_CALL(_draw_rect, p_canvas_item, p_rect, p_tile, p_modulate, p_transpose)) { + return; + } RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, get_rid(), p_tile, p_modulate, p_transpose); } void Texture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { + if (GDVIRTUAL_CALL(_draw_rect_region, p_canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_clip_uv)) { + return; + } RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, get_rid(), p_src_rect, p_modulate, p_transpose, p_clip_uv); } @@ -75,6 +113,15 @@ void Texture2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_image"), &Texture2D::get_image); ADD_GROUP("", ""); + + GDVIRTUAL_BIND(_get_width); + GDVIRTUAL_BIND(_get_height); + GDVIRTUAL_BIND(_is_pixel_opaque, "x", "y"); + GDVIRTUAL_BIND(_has_alpha); + + GDVIRTUAL_BIND(_draw, "to_canvas_item", "pos", "modulate", "transpose") + GDVIRTUAL_BIND(_draw_rect, "to_canvas_item", "rect", "tile", "modulate", "transpose") + GDVIRTUAL_BIND(_draw_rect_region, "tp_canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"); } Texture2D::Texture2D() { @@ -92,7 +139,7 @@ void ImageTexture::reload_from_file() { img.instantiate(); if (ImageLoader::load_image(path, img) == OK) { - create_from_image(img); + set_image(img); } else { Resource::reload_from_file(); notify_property_list_changed(); @@ -102,37 +149,34 @@ void ImageTexture::reload_from_file() { bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "image") { - create_from_image(p_value); - } else if (p_name == "size") { - Size2 s = p_value; - w = s.width; - h = s.height; - RenderingServer::get_singleton()->texture_set_size_override(texture, w, h); - } else { - return false; + set_image(p_value); + return true; } - - return true; + return false; } bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "image") { r_ret = get_image(); - } else if (p_name == "size") { - r_ret = Size2(w, h); - } else { - return false; + return true; } - - return true; + return false; } void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("image"), PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); } -void ImageTexture::create_from_image(const Ref<Image> &p_image) { +Ref<ImageTexture> ImageTexture::create_from_image(const Ref<Image> &p_image) { + ERR_FAIL_COND_V_MSG(p_image.is_null() || p_image->is_empty(), Ref<ImageTexture>(), "Invalid image"); + + Ref<ImageTexture> image_texture; + image_texture.instantiate(); + image_texture->set_image(p_image); + return image_texture; +} + +void ImageTexture::set_image(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image"); w = p_image->get_width(); h = p_image->get_height(); @@ -165,7 +209,7 @@ void ImageTexture::update(const Ref<Image> &p_image) { ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(), "The new image mipmaps configuration must match the texture's image mipmaps configuration"); - RenderingServer::get_singleton()->texture_2d_update(texture, p_image); + RS::get_singleton()->texture_2d_update(texture, p_image); notify_property_list_changed(); emit_changed(); @@ -256,8 +300,8 @@ bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { return true; } -void ImageTexture::set_size_override(const Size2 &p_size) { - Size2 s = p_size; +void ImageTexture::set_size_override(const Size2i &p_size) { + Size2i s = p_size; if (s.x != 0) { w = s.x; } @@ -276,9 +320,10 @@ void ImageTexture::set_path(const String &p_path, bool p_take_over) { } void ImageTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image); + ClassDB::bind_static_method("ImageTexture", 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("set_image", "image"), &ImageTexture::set_image); ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update); ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override); } @@ -291,9 +336,315 @@ ImageTexture::~ImageTexture() { } } +///////////////////// + +void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { + if (p_data.size() == 0) { + return; //nothing to do + } + + const uint8_t *data = p_data.ptr(); + uint32_t data_size = p_data.size(); + ERR_FAIL_COND(data_size < 20); + compression_mode = CompressionMode(decode_uint32(data + 0)); + format = Image::Format(decode_uint32(data + 4)); + uint32_t mipmap_count = decode_uint32(data + 8); + size.width = decode_uint32(data + 12); + size.height = decode_uint32(data + 16); + mipmaps = mipmap_count > 1; + + data += 20; + data_size -= 20; + + Ref<Image> image; + + switch (compression_mode) { + case COMPRESSION_MODE_LOSSLESS: + case COMPRESSION_MODE_LOSSY: { + Vector<uint8_t> image_data; + + ERR_FAIL_COND(data_size < 4); + for (uint32_t i = 0; i < mipmap_count; i++) { + uint32_t mipsize = decode_uint32(data); + data += 4; + data_size -= 4; + ERR_FAIL_COND(mipsize < data_size); + Ref<Image> img = memnew(Image(data, data_size)); + ERR_FAIL_COND(img->is_empty()); + if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps. + img->convert(format); + } + image_data.append_array(img->get_data()); + + data += mipsize; + data_size -= mipsize; + } + + image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data))); + + } break; + case COMPRESSION_MODE_BASIS_UNIVERSAL: { + ERR_FAIL_COND(!Image::basis_universal_unpacker_ptr); + image = Image::basis_universal_unpacker_ptr(data, data_size); + + } break; + case COMPRESSION_MODE_S3TC: + case COMPRESSION_MODE_ETC2: + case COMPRESSION_MODE_BPTC: { + image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20)))); + } break; + } + ERR_FAIL_COND(image.is_null()); + + if (texture.is_null()) { + texture = RenderingServer::get_singleton()->texture_2d_create(image); + } else { + RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image); + RenderingServer::get_singleton()->texture_replace(texture, new_texture); + } + + image_stored = true; + RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height); + alpha_cache.unref(); + + if (keep_all_compressed_buffers || keep_compressed_buffer) { + compressed_buffer = p_data; + } else { + compressed_buffer.clear(); + } +} + +PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const { + return compression_mode; +} +Vector<uint8_t> PortableCompressedTexture2D::_get_data() const { + return compressed_buffer; +} + +void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) { + ERR_FAIL_COND(p_image.is_null() || p_image->is_empty()); + + Vector<uint8_t> buffer; + + buffer.resize(20); + encode_uint32(p_compression_mode, buffer.ptrw()); + encode_uint32(p_image->get_format(), buffer.ptrw() + 4); + encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8); + encode_uint32(p_image->get_width(), buffer.ptrw() + 12); + encode_uint32(p_image->get_height(), buffer.ptrw() + 16); + + switch (p_compression_mode) { + case COMPRESSION_MODE_LOSSLESS: + case COMPRESSION_MODE_LOSSY: { + for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { + Vector<uint8_t> data; + if (p_compression_mode == COMPRESSION_MODE_LOSSY) { + data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + } else { + data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + } + int data_len = data.size(); + buffer.resize(buffer.size() + 4); + encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4); + buffer.append_array(data); + } + } break; + case COMPRESSION_MODE_BASIS_UNIVERSAL: { + Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC); + Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc); + buffer.append_array(budata); + + } break; + case COMPRESSION_MODE_S3TC: + case COMPRESSION_MODE_ETC2: + case COMPRESSION_MODE_BPTC: { + Ref<Image> copy = p_image->duplicate(); + switch (p_compression_mode) { + case COMPRESSION_MODE_S3TC: + copy->compress(Image::COMPRESS_S3TC); + break; + case COMPRESSION_MODE_ETC2: + copy->compress(Image::COMPRESS_ETC2); + break; + case COMPRESSION_MODE_BPTC: + copy->compress(Image::COMPRESS_BPTC); + break; + default: { + }; + } + + buffer.append_array(copy->get_data()); + + } break; + } + + _set_data(buffer); +} + +Image::Format PortableCompressedTexture2D::get_format() const { + return format; +} + +Ref<Image> PortableCompressedTexture2D::get_image() const { + if (image_stored) { + return RenderingServer::get_singleton()->texture_2d_get(texture); + } else { + return Ref<Image>(); + } +} + +int PortableCompressedTexture2D::get_width() const { + return size.width; +} + +int PortableCompressedTexture2D::get_height() const { + return size.height; +} + +RID PortableCompressedTexture2D::get_rid() const { + if (texture.is_null()) { + //we are in trouble, create something temporary + texture = RenderingServer::get_singleton()->texture_2d_placeholder_create(); + } + return texture; +} + +bool PortableCompressedTexture2D::has_alpha() const { + return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8); +} + +void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { + if (size.width == 0 || size.height == 0) { + return; + } + RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose); +} + +void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { + if (size.width == 0 || size.height == 0) { + return; + } + RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose); +} + +void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { + if (size.width == 0 || size.height == 0) { + return; + } + RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv); +} + +bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { + if (!alpha_cache.is_valid()) { + Ref<Image> img = get_image(); + if (img.is_valid()) { + if (img->is_compressed()) { //must decompress, if compressed + Ref<Image> decom = img->duplicate(); + decom->decompress(); + img = decom; + } + alpha_cache.instantiate(); + alpha_cache->create_from_image_alpha(img); + } + } + + if (alpha_cache.is_valid()) { + int aw = int(alpha_cache->get_size().width); + int ah = int(alpha_cache->get_size().height); + if (aw == 0 || ah == 0) { + return true; + } + + int x = p_x * aw / size.width; + int y = p_y * ah / size.height; + + x = CLAMP(x, 0, aw); + y = CLAMP(y, 0, ah); + + return alpha_cache->get_bit(Point2(x, y)); + } + + return true; +} + +void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) { + size_override = p_size; + RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height); +} + +Size2 PortableCompressedTexture2D::get_size_override() const { + return size_override; +} + +void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + RenderingServer::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +bool PortableCompressedTexture2D::keep_all_compressed_buffers = false; + +void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) { + keep_all_compressed_buffers = p_keep; +} + +bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() { + return keep_all_compressed_buffers; +} + +void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) { + keep_compressed_buffer = p_keep; + if (!p_keep) { + compressed_buffer.clear(); + } +} + +bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const { + return keep_compressed_buffer; +} + +void PortableCompressedTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8)); + ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format); + ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode); + + ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override); + ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override); + + ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer); + ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data); + + ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers); + ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_override", "get_size_override"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer"); + + BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS); + BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY); + BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL); + BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC); + BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2); + BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC); +} + +PortableCompressedTexture2D::PortableCompressedTexture2D() {} + +PortableCompressedTexture2D::~PortableCompressedTexture2D() { + if (texture.is_valid()) { + RenderingServer::get_singleton()->free(texture); + } +} + ////////////////////////////////////////// -Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit) { +Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) { uint32_t data_format = f->get_32(); uint32_t w = f->get_16(); uint32_t h = f->get_16(); @@ -426,7 +777,7 @@ Ref<Image> StreamTexture2D::load_image_from_file(FileAccess *f, int p_size_limit return Ref<Image>(); } -void StreamTexture2D::set_path(const String &p_path, bool p_take_over) { +void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) { if (texture.is_valid()) { RenderingServer::get_singleton()->texture_set_path(texture, p_path); } @@ -434,55 +785,53 @@ void StreamTexture2D::set_path(const String &p_path, bool p_take_over) { Resource::set_path(p_path, p_take_over); } -void StreamTexture2D::_requested_3d(void *p_ud) { - StreamTexture2D *st = (StreamTexture2D *)p_ud; - Ref<StreamTexture2D> stex(st); +void CompressedTexture2D::_requested_3d(void *p_ud) { + CompressedTexture2D *ct = (CompressedTexture2D *)p_ud; + Ref<CompressedTexture2D> ctex(ct); ERR_FAIL_COND(!request_3d_callback); - request_3d_callback(stex); + request_3d_callback(ctex); } -void StreamTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) { - StreamTexture2D *st = (StreamTexture2D *)p_ud; - Ref<StreamTexture2D> stex(st); +void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) { + CompressedTexture2D *ct = (CompressedTexture2D *)p_ud; + Ref<CompressedTexture2D> ctex(ct); ERR_FAIL_COND(!request_roughness_callback); - request_roughness_callback(stex, p_normal_path, p_roughness_channel); + request_roughness_callback(ctex, p_normal_path, p_roughness_channel); } -void StreamTexture2D::_requested_normal(void *p_ud) { - StreamTexture2D *st = (StreamTexture2D *)p_ud; - Ref<StreamTexture2D> stex(st); +void CompressedTexture2D::_requested_normal(void *p_ud) { + CompressedTexture2D *ct = (CompressedTexture2D *)p_ud; + Ref<CompressedTexture2D> ctex(ct); ERR_FAIL_COND(!request_normal_callback); - request_normal_callback(stex); + request_normal_callback(ctex); } -StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_3d_callback = nullptr; -StreamTexture2D::TextureFormatRoughnessRequestCallback StreamTexture2D::request_roughness_callback = nullptr; -StreamTexture2D::TextureFormatRequestCallback StreamTexture2D::request_normal_callback = nullptr; +CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr; +CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr; +CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr; -Image::Format StreamTexture2D::get_format() const { +Image::Format CompressedTexture2D::get_format() const { return format; } -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) { +Error CompressedTexture2D::_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); - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') { - memdelete(f); - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is corrupt (Bad header)."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header)."); } uint32_t version = f->get_32(); if (version > FORMAT_VERSION) { - memdelete(f); - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new."); } r_width = f->get_32(); r_height = f->get_32(); @@ -514,8 +863,6 @@ Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_hei image = load_image_from_file(f, p_size_limit); - memdelete(f); - if (image.is_null() || image->is_empty()) { return ERR_CANT_OPEN; } @@ -523,7 +870,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_hei return OK; } -Error StreamTexture2D::load(const String &p_path) { +Error CompressedTexture2D::load(const String &p_path) { int lw, lh; Ref<Image> image; image.instantiate(); @@ -590,51 +937,51 @@ Error StreamTexture2D::load(const String &p_path) { return OK; } -String StreamTexture2D::get_load_path() const { +String CompressedTexture2D::get_load_path() const { return path_to_file; } -int StreamTexture2D::get_width() const { +int CompressedTexture2D::get_width() const { return w; } -int StreamTexture2D::get_height() const { +int CompressedTexture2D::get_height() const { return h; } -RID StreamTexture2D::get_rid() const { +RID CompressedTexture2D::get_rid() const { if (!texture.is_valid()) { texture = RS::get_singleton()->texture_2d_placeholder_create(); } return texture; } -void StreamTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { +void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { if ((w | h) == 0) { return; } RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose); } -void StreamTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { +void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { if ((w | h) == 0) { return; } RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose); } -void StreamTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { +void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { if ((w | h) == 0) { return; } RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv); } -bool StreamTexture2D::has_alpha() const { +bool CompressedTexture2D::has_alpha() const { return false; } -Ref<Image> StreamTexture2D::get_image() const { +Ref<Image> CompressedTexture2D::get_image() const { if (texture.is_valid()) { return RS::get_singleton()->texture_2d_get(texture); } else { @@ -642,7 +989,7 @@ Ref<Image> StreamTexture2D::get_image() const { } } -bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const { +bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { if (!alpha_cache.is_valid()) { Ref<Image> img = get_image(); if (img.is_valid()) { @@ -676,7 +1023,7 @@ bool StreamTexture2D::is_pixel_opaque(int p_x, int p_y) const { return true; } -void StreamTexture2D::reload_from_file() { +void CompressedTexture2D::reload_from_file() { String path = get_path(); if (!path.is_resource_file()) { return; @@ -691,56 +1038,56 @@ void StreamTexture2D::reload_from_file() { load(path); } -void StreamTexture2D::_validate_property(PropertyInfo &property) const { +void CompressedTexture2D::_validate_property(PropertyInfo &property) const { } -void StreamTexture2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture2D::load); - ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture2D::get_load_path); +void CompressedTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load); + ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path"); } -StreamTexture2D::StreamTexture2D() {} +CompressedTexture2D::CompressedTexture2D() {} -StreamTexture2D::~StreamTexture2D() { +CompressedTexture2D::~CompressedTexture2D() { if (texture.is_valid()) { RS::get_singleton()->free(texture); } } -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; +Ref<Resource> ResourceFormatLoaderCompressedTexture2D::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<CompressedTexture2D> st; st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } return st; } -void ResourceFormatLoaderStreamTexture2D::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("stex"); +void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("ctex"); } -bool ResourceFormatLoaderStreamTexture2D::handles_type(const String &p_type) const { - return p_type == "StreamTexture2D"; +bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const { + return p_type == "CompressedTexture2D"; } -String ResourceFormatLoaderStreamTexture2D::get_resource_type(const String &p_path) const { - if (p_path.get_extension().to_lower() == "stex") { - return "StreamTexture2D"; +String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const { + if (p_path.get_extension().to_lower() == "ctex") { + return "CompressedTexture2D"; } return ""; } //////////////////////////////////// -TypedArray<Image> Texture3D::_get_data() const { +TypedArray<Image> Texture3D::_get_datai() const { Vector<Ref<Image>> data = get_data(); TypedArray<Image> ret; @@ -751,13 +1098,73 @@ TypedArray<Image> Texture3D::_get_data() const { return ret; } +Image::Format Texture3D::get_format() const { + Image::Format ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) { + return ret; + } + return Image::FORMAT_MAX; +} + +int Texture3D::get_width() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { + return ret; + } + return 0; +} + +int Texture3D::get_height() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { + return ret; + } + return 0; +} + +int Texture3D::get_depth() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_depth, ret)) { + return ret; + } + + return 0; +} + +bool Texture3D::has_mipmaps() const { + bool ret; + if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) { + return ret; + } + return false; +} + +Vector<Ref<Image>> Texture3D::get_data() const { + TypedArray<Image> ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_data, ret)) { + Vector<Ref<Image>> data; + data.resize(ret.size()); + for (int i = 0; i < data.size(); i++) { + data.write[i] = ret[i]; + } + return data; + } + return Vector<Ref<Image>>(); +} void Texture3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format); ClassDB::bind_method(D_METHOD("get_width"), &Texture3D::get_width); ClassDB::bind_method(D_METHOD("get_height"), &Texture3D::get_height); ClassDB::bind_method(D_METHOD("get_depth"), &Texture3D::get_depth); ClassDB::bind_method(D_METHOD("has_mipmaps"), &Texture3D::has_mipmaps); - ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_data); + ClassDB::bind_method(D_METHOD("get_data"), &Texture3D::_get_datai); + + GDVIRTUAL_BIND(_get_format); + GDVIRTUAL_BIND(_get_width); + GDVIRTUAL_BIND(_get_height); + GDVIRTUAL_BIND(_get_depth); + GDVIRTUAL_BIND(_has_mipmaps); + GDVIRTUAL_BIND(_get_data); } ////////////////////////////////////////// @@ -846,7 +1253,7 @@ ImageTexture3D::~ImageTexture3D() { //////////////////////////////////////////// -void StreamTexture3D::set_path(const String &p_path, bool p_take_over) { +void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) { if (texture.is_valid()) { RenderingServer::get_singleton()->texture_set_path(texture, p_path); } @@ -854,23 +1261,23 @@ void StreamTexture3D::set_path(const String &p_path, bool p_take_over) { Resource::set_path(p_path, p_take_over); } -Image::Format StreamTexture3D::get_format() const { +Image::Format CompressedTexture3D::get_format() const { return format; } -Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) { - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); +Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED); - //stored as stream textures (used for lossless and lossy compression) + //stored as compressed textures (used for lossless and lossy compression) uint32_t version = f->get_32(); if (version > FORMAT_VERSION) { - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new."); } r_depth = f->get_32(); //depth @@ -887,7 +1294,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da r_data.clear(); for (int i = 0; i < (r_depth + mipmaps); i++) { - Ref<Image> image = StreamTexture2D::load_image_from_file(f, 0); + Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0); ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN); if (i == 0) { r_format = image->get_format(); @@ -900,7 +1307,7 @@ Error StreamTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_da return OK; } -Error StreamTexture3D::load(const String &p_path) { +Error CompressedTexture3D::load(const String &p_path) { Vector<Ref<Image>> data; int tw, th, td; @@ -937,34 +1344,34 @@ Error StreamTexture3D::load(const String &p_path) { return OK; } -String StreamTexture3D::get_load_path() const { +String CompressedTexture3D::get_load_path() const { return path_to_file; } -int StreamTexture3D::get_width() const { +int CompressedTexture3D::get_width() const { return w; } -int StreamTexture3D::get_height() const { +int CompressedTexture3D::get_height() const { return h; } -int StreamTexture3D::get_depth() const { +int CompressedTexture3D::get_depth() const { return d; } -bool StreamTexture3D::has_mipmaps() const { +bool CompressedTexture3D::has_mipmaps() const { return mipmaps; } -RID StreamTexture3D::get_rid() const { +RID CompressedTexture3D::get_rid() const { if (!texture.is_valid()) { texture = RS::get_singleton()->texture_3d_placeholder_create(); } return texture; } -Vector<Ref<Image>> StreamTexture3D::get_data() const { +Vector<Ref<Image>> CompressedTexture3D::get_data() const { if (texture.is_valid()) { return RS::get_singleton()->texture_3d_get(texture); } else { @@ -972,7 +1379,7 @@ Vector<Ref<Image>> StreamTexture3D::get_data() const { } } -void StreamTexture3D::reload_from_file() { +void CompressedTexture3D::reload_from_file() { String path = get_path(); if (!path.is_resource_file()) { return; @@ -987,19 +1394,19 @@ void StreamTexture3D::reload_from_file() { load(path); } -void StreamTexture3D::_validate_property(PropertyInfo &property) const { +void CompressedTexture3D::_validate_property(PropertyInfo &property) const { } -void StreamTexture3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("load", "path"), &StreamTexture3D::load); - ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTexture3D::get_load_path); +void CompressedTexture3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load); + ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path"); } -StreamTexture3D::StreamTexture3D() {} +CompressedTexture3D::CompressedTexture3D() {} -StreamTexture3D::~StreamTexture3D() { +CompressedTexture3D::~CompressedTexture3D() { if (texture.is_valid()) { RS::get_singleton()->free(texture); } @@ -1007,31 +1414,31 @@ 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; +Ref<Resource> ResourceFormatLoaderCompressedTexture3D::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<CompressedTexture3D> st; st.instantiate(); Error err = st->load(p_path); if (r_error) { *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } return st; } -void ResourceFormatLoaderStreamTexture3D::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("stex3d"); +void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("ctex3d"); } -bool ResourceFormatLoaderStreamTexture3D::handles_type(const String &p_type) const { - return p_type == "StreamTexture3D"; +bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const { + return p_type == "CompressedTexture3D"; } -String ResourceFormatLoaderStreamTexture3D::get_resource_type(const String &p_path) const { - if (p_path.get_extension().to_lower() == "stex3d") { - return "StreamTexture3D"; +String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const { + if (p_path.get_extension().to_lower() == "ctex3d") { + return "CompressedTexture3D"; } return ""; } @@ -1136,8 +1543,8 @@ void AtlasTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("has_filter_clip"), &AtlasTexture::has_filter_clip); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_atlas", "get_atlas"); - ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region"), "set_region", "get_region"); - ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region", PROPERTY_HINT_NONE, "suffix:px"), "set_region", "get_region"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin", PROPERTY_HINT_NONE, "suffix:px"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip"); } @@ -1308,8 +1715,8 @@ void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_mo Transform2D xform; xform.set_origin(p_pos); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -1330,8 +1737,8 @@ void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, xform.set_scale(p_rect.size / size); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -1352,8 +1759,8 @@ void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const xform.set_scale(p_rect.size / size); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -1378,7 +1785,7 @@ void MeshTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_base_texture", "get_base_texture"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1"), "set_image_size", "get_image_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_image_size", "get_image_size"); } MeshTexture::MeshTexture() { @@ -1397,7 +1804,7 @@ void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "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"); @@ -1545,7 +1952,7 @@ void CurveXYZTexture::_bind_methods() { 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::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "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"); @@ -1751,8 +2158,8 @@ void GradientTexture1D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_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,4096"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); } @@ -1840,7 +2247,7 @@ void GradientTexture1D::_update() { } void GradientTexture1D::set_width(int p_width) { - ERR_FAIL_COND(p_width <= 0); + ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range."); width = p_width; _queue_update(); } @@ -1869,6 +2276,8 @@ Ref<Image> GradientTexture1D::get_image() const { return RenderingServer::get_singleton()->texture_2d_get(texture); } +////////////////// + GradientTexture2D::GradientTexture2D() { _queue_update(); } @@ -1890,7 +2299,8 @@ void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) { if (gradient.is_valid()) { gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); } - _queue_update(); + _update(); + emit_changed(); } Ref<Gradient> GradientTexture2D::get_gradient() const { @@ -2001,6 +2411,7 @@ float GradientTexture2D::_get_gradient_offset_at(int x, int y) const { } void GradientTexture2D::set_width(int p_width) { + ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range."); width = p_width; _queue_update(); } @@ -2010,6 +2421,7 @@ int GradientTexture2D::get_width() const { } void GradientTexture2D::set_height(int p_height) { + ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be within 1 to 16384 range."); height = p_height; _queue_update(); } @@ -2103,8 +2515,8 @@ void GradientTexture2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048"), "set_width", "get_width"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_height", "get_height"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr"); ADD_GROUP("Fill", "fill_"); @@ -2424,7 +2836,7 @@ void AnimatedTexture::_bind_methods() { for (int i = 0; i < MAX_FRAMES; i++) { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_delay", "get_frame_delay", i); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_delay", "get_frame_delay", i); } BIND_CONSTANT(MAX_FRAMES); @@ -2446,6 +2858,63 @@ AnimatedTexture::~AnimatedTexture() { /////////////////////////////// +Image::Format TextureLayered::get_format() const { + Image::Format ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) { + return ret; + } + return Image::FORMAT_MAX; +} + +TextureLayered::LayeredType TextureLayered::get_layered_type() const { + uint32_t ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret)) { + return (LayeredType)ret; + } + return LAYERED_TYPE_2D_ARRAY; +} + +int TextureLayered::get_width() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { + return ret; + } + return 0; +} + +int TextureLayered::get_height() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { + return ret; + } + return 0; +} + +int TextureLayered::get_layers() const { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_layers, ret)) { + return ret; + } + + return 0; +} + +bool TextureLayered::has_mipmaps() const { + bool ret; + if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) { + return ret; + } + return false; +} + +Ref<Image> TextureLayered::get_layer_data(int p_layer) const { + Ref<Image> ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret)) { + return ret; + } + return Ref<Image>(); +} + void TextureLayered::_bind_methods() { ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format); ClassDB::bind_method(D_METHOD("get_layered_type"), &TextureLayered::get_layered_type); @@ -2458,6 +2927,14 @@ void TextureLayered::_bind_methods() { BIND_ENUM_CONSTANT(LAYERED_TYPE_2D_ARRAY); BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP); BIND_ENUM_CONSTANT(LAYERED_TYPE_CUBEMAP_ARRAY); + + GDVIRTUAL_BIND(_get_format); + GDVIRTUAL_BIND(_get_layered_type); + GDVIRTUAL_BIND(_get_width); + GDVIRTUAL_BIND(_get_height); + GDVIRTUAL_BIND(_get_layers); + GDVIRTUAL_BIND(_has_mipmaps); + GDVIRTUAL_BIND(_get_layer_data, "layer_index"); } /////////////////////////////// @@ -2549,12 +3026,12 @@ Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) { } void ImageTextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) { - ERR_FAIL_COND(texture.is_valid()); - ERR_FAIL_COND(p_image.is_null()); - ERR_FAIL_COND(p_image->get_format() != format); - ERR_FAIL_COND(p_image->get_width() != width || p_image->get_height() != height); - ERR_FAIL_INDEX(p_layer, layers); - ERR_FAIL_COND(p_image->has_mipmaps() != mipmaps); + ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized."); + ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image."); + ERR_FAIL_COND_MSG(p_image->get_format() != format, "Image format must match texture's image format."); + ERR_FAIL_COND_MSG(p_image->get_width() != width || p_image->get_height() != height, "Image size must match texture's image size."); + ERR_FAIL_COND_MSG(p_image->has_mipmaps() != mipmaps, "Image mipmap configuration must match texture's image mipmap configuration."); + ERR_FAIL_INDEX_MSG(p_layer, layers, "Layer index is out of bounds."); RS::get_singleton()->texture_2d_update(texture, p_image, p_layer); } @@ -2599,7 +3076,7 @@ ImageTextureLayered::~ImageTextureLayered() { /////////////////////////////////////////// -void StreamTextureLayered::set_path(const String &p_path, bool p_take_over) { +void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) { if (texture.is_valid()) { RenderingServer::get_singleton()->texture_set_path(texture, p_path); } @@ -2607,26 +3084,26 @@ void StreamTextureLayered::set_path(const String &p_path, bool p_take_over) { Resource::set_path(p_path, p_take_over); } -Image::Format StreamTextureLayered::get_format() const { +Image::Format CompressedTextureLayered::get_format() const { return format; } -Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) { +Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) { ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER); - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') { - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture layered file is corrupt (Bad header)."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header)."); } uint32_t version = f->get_32(); if (version > FORMAT_VERSION) { - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new."); } uint32_t layer_count = f->get_32(); //layer count @@ -2647,7 +3124,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> images.resize(layer_count); for (uint32_t i = 0; i < layer_count; i++) { - Ref<Image> image = StreamTexture2D::load_image_from_file(f, p_size_limit); + Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit); ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN); images.write[i] = image; } @@ -2655,7 +3132,7 @@ Error StreamTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> return OK; } -Error StreamTextureLayered::load(const String &p_path) { +Error CompressedTextureLayered::load(const String &p_path) { Vector<Ref<Image>> images; int mipmap_limit; @@ -2690,38 +3167,38 @@ Error StreamTextureLayered::load(const String &p_path) { return OK; } -String StreamTextureLayered::get_load_path() const { +String CompressedTextureLayered::get_load_path() const { return path_to_file; } -int StreamTextureLayered::get_width() const { +int CompressedTextureLayered::get_width() const { return w; } -int StreamTextureLayered::get_height() const { +int CompressedTextureLayered::get_height() const { return h; } -int StreamTextureLayered::get_layers() const { +int CompressedTextureLayered::get_layers() const { return layers; } -bool StreamTextureLayered::has_mipmaps() const { +bool CompressedTextureLayered::has_mipmaps() const { return mipmaps; } -TextureLayered::LayeredType StreamTextureLayered::get_layered_type() const { +TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const { return layered_type; } -RID StreamTextureLayered::get_rid() const { +RID CompressedTextureLayered::get_rid() const { if (!texture.is_valid()) { texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); } return texture; } -Ref<Image> StreamTextureLayered::get_layer_data(int p_layer) const { +Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const { if (texture.is_valid()) { return RS::get_singleton()->texture_2d_layer_get(texture, p_layer); } else { @@ -2729,7 +3206,7 @@ Ref<Image> StreamTextureLayered::get_layer_data(int p_layer) const { } } -void StreamTextureLayered::reload_from_file() { +void CompressedTextureLayered::reload_from_file() { String path = get_path(); if (!path.is_resource_file()) { return; @@ -2744,21 +3221,21 @@ void StreamTextureLayered::reload_from_file() { load(path); } -void StreamTextureLayered::_validate_property(PropertyInfo &property) const { +void CompressedTextureLayered::_validate_property(PropertyInfo &property) const { } -void StreamTextureLayered::_bind_methods() { - ClassDB::bind_method(D_METHOD("load", "path"), &StreamTextureLayered::load); - ClassDB::bind_method(D_METHOD("get_load_path"), &StreamTextureLayered::get_load_path); +void CompressedTextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load); + ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.stex"), "load", "get_load_path"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path"); } -StreamTextureLayered::StreamTextureLayered(LayeredType p_type) { +CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) { layered_type = p_type; } -StreamTextureLayered::~StreamTextureLayered() { +CompressedTextureLayered::~CompressedTextureLayered() { if (texture.is_valid()) { RS::get_singleton()->free(texture); } @@ -2766,56 +3243,56 @@ StreamTextureLayered::~StreamTextureLayered() { ///////////////////////////////////////////////// -RES ResourceFormatLoaderStreamTextureLayered::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<StreamTextureLayered> st; - if (p_path.get_extension().to_lower() == "stexarray") { - Ref<StreamTexture2DArray> s; - s.instantiate(); - st = s; - } else if (p_path.get_extension().to_lower() == "scube") { - Ref<StreamCubemap> s; - s.instantiate(); - st = s; - } else if (p_path.get_extension().to_lower() == "scubearray") { - Ref<StreamCubemapArray> s; - s.instantiate(); - st = s; +Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::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<CompressedTextureLayered> ct; + if (p_path.get_extension().to_lower() == "ctexarray") { + Ref<CompressedTexture2DArray> c; + c.instantiate(); + ct = c; + } else if (p_path.get_extension().to_lower() == "ccube") { + Ref<CompressedCubemap> c; + c.instantiate(); + ct = c; + } else if (p_path.get_extension().to_lower() == "ccubearray") { + Ref<CompressedCubemapArray> c; + c.instantiate(); + ct = c; } else { if (r_error) { *r_error = ERR_FILE_UNRECOGNIZED; } - return RES(); + return Ref<Resource>(); } - Error err = st->load(p_path); + Error err = ct->load(p_path); if (r_error) { *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } - return st; + return ct; } -void ResourceFormatLoaderStreamTextureLayered::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("stexarray"); - p_extensions->push_back("scube"); - p_extensions->push_back("scubearray"); +void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("ctexarray"); + p_extensions->push_back("ccube"); + p_extensions->push_back("ccubearray"); } -bool ResourceFormatLoaderStreamTextureLayered::handles_type(const String &p_type) const { - return p_type == "StreamTexture2DArray" || p_type == "StreamCubemap" || p_type == "StreamCubemapArray"; +bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const { + return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray"; } -String ResourceFormatLoaderStreamTextureLayered::get_resource_type(const String &p_path) const { - if (p_path.get_extension().to_lower() == "stexarray") { - return "StreamTexture2DArray"; +String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const { + if (p_path.get_extension().to_lower() == "ctexarray") { + return "CompressedTexture2DArray"; } - if (p_path.get_extension().to_lower() == "scube") { - return "StreamCubemap"; + if (p_path.get_extension().to_lower() == "ccube") { + return "CompressedCubemap"; } - if (p_path.get_extension().to_lower() == "scubearray") { - return "StreamCubemapArray"; + if (p_path.get_extension().to_lower() == "ccubearray") { + return "CompressedCubemapArray"; } return ""; } @@ -2918,3 +3395,148 @@ CameraTexture::~CameraTexture() { RenderingServer::get_singleton()->free(_texture); } } + +/////////////////////////// + +void PlaceholderTexture2D::set_size(Size2 p_size) { + size = p_size; +} + +int PlaceholderTexture2D::get_width() const { + return size.width; +} + +int PlaceholderTexture2D::get_height() const { + return size.height; +} + +bool PlaceholderTexture2D::has_alpha() const { + return false; +} + +Ref<Image> PlaceholderTexture2D::get_image() const { + return Ref<Image>(); +} + +RID PlaceholderTexture2D::get_rid() const { + return rid; +} + +void PlaceholderTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size"); +} + +PlaceholderTexture2D::PlaceholderTexture2D() { + rid = RS::get_singleton()->texture_2d_placeholder_create(); +} + +PlaceholderTexture2D::~PlaceholderTexture2D() { + RS::get_singleton()->free(rid); +} + +/////////////////////////////////////////////// + +void PlaceholderTexture3D::set_size(const Vector3i &p_size) { + size = p_size; +} + +Vector3i PlaceholderTexture3D::get_size() const { + return size; +} + +Image::Format PlaceholderTexture3D::get_format() const { + return Image::FORMAT_RGB8; +} + +int PlaceholderTexture3D::get_width() const { + return size.x; +} + +int PlaceholderTexture3D::get_height() const { + return size.y; +} + +int PlaceholderTexture3D::get_depth() const { + return size.z; +} + +bool PlaceholderTexture3D::has_mipmaps() const { + return false; +} + +Vector<Ref<Image>> PlaceholderTexture3D::get_data() const { + return Vector<Ref<Image>>(); +} + +void PlaceholderTexture3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size"); +} + +PlaceholderTexture3D::PlaceholderTexture3D() { + rid = RS::get_singleton()->texture_3d_placeholder_create(); +} +PlaceholderTexture3D::~PlaceholderTexture3D() { + RS::get_singleton()->free(rid); +} + +///////////////////////////////////////////////// + +void PlaceholderTextureLayered::set_size(const Size2i &p_size) { + size = p_size; +} + +Size2i PlaceholderTextureLayered::get_size() const { + return size; +} + +void PlaceholderTextureLayered::set_layers(int p_layers) { + layers = p_layers; +} + +Image::Format PlaceholderTextureLayered::get_format() const { + return Image::FORMAT_RGB8; +} + +TextureLayered::LayeredType PlaceholderTextureLayered::get_layered_type() const { + return layered_type; +} + +int PlaceholderTextureLayered::get_width() const { + return size.x; +} + +int PlaceholderTextureLayered::get_height() const { + return size.y; +} + +int PlaceholderTextureLayered::get_layers() const { + return layers; +} + +bool PlaceholderTextureLayered::has_mipmaps() const { + return false; +} + +Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const { + return Ref<Image>(); +} + +void PlaceholderTextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size); + ClassDB::bind_method(D_METHOD("set_layers", "layers"), &PlaceholderTextureLayered::set_layers); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_RANGE, "1,4096"), "set_layers", "get_layers"); +} + +PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) { + layered_type = p_type; + rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); +} +PlaceholderTextureLayered::~PlaceholderTextureLayered() { + RS::get_singleton()->free(rid); +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 8075497c42..36b193c5d4 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -57,14 +57,23 @@ class Texture2D : public Texture { protected: static void _bind_methods(); + GDVIRTUAL0RC(int, _get_width) + GDVIRTUAL0RC(int, _get_height) + GDVIRTUAL2RC(bool, _is_pixel_opaque, int, int) + GDVIRTUAL0RC(bool, _has_alpha) + + GDVIRTUAL4C(_draw, RID, Point2, Color, bool) + GDVIRTUAL5C(_draw_rect, RID, Rect2, bool, Color, bool) + GDVIRTUAL6C(_draw_rect_region, RID, Rect2, Rect2, Color, bool, bool) + public: - virtual int get_width() const = 0; - virtual int get_height() const = 0; + virtual int get_width() const; + virtual int get_height() const; virtual Size2 get_size() const; virtual bool is_pixel_opaque(int p_x, int p_y) const; - virtual bool has_alpha() const = 0; + virtual bool has_alpha() const; virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const; virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const; @@ -101,7 +110,8 @@ protected: static void _bind_methods(); public: - void create_from_image(const Ref<Image> &p_image); + void set_image(const Ref<Image> &p_image); + static Ref<ImageTexture> create_from_image(const Ref<Image> &p_image); Image::Format get_format() const; @@ -120,7 +130,7 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; - void set_size_override(const Size2 &p_size); + void set_size_override(const Size2i &p_size); virtual void set_path(const String &p_path, bool p_take_over = false) override; @@ -128,8 +138,80 @@ public: ~ImageTexture(); }; -class StreamTexture2D : public Texture2D { - GDCLASS(StreamTexture2D, Texture2D); +class PortableCompressedTexture2D : public Texture2D { + GDCLASS(PortableCompressedTexture2D, Texture2D); + +public: + enum CompressionMode { + COMPRESSION_MODE_LOSSLESS, + COMPRESSION_MODE_LOSSY, + COMPRESSION_MODE_BASIS_UNIVERSAL, + COMPRESSION_MODE_S3TC, + COMPRESSION_MODE_ETC2, + COMPRESSION_MODE_BPTC, + }; + +private: + CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS; + static bool keep_all_compressed_buffers; + bool keep_compressed_buffer = false; + Vector<uint8_t> compressed_buffer; + Size2 size; + Size2 size_override; + bool mipmaps = false; + Image::Format format = Image::FORMAT_L8; + + mutable RID texture; + mutable Ref<BitMap> alpha_cache; + + bool image_stored = false; + +protected: + Vector<uint8_t> _get_data() const; + void _set_data(const Vector<uint8_t> &p_data); + + static void _bind_methods(); + +public: + CompressionMode get_compression_mode() const; + void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8); + + Image::Format get_format() const; + + void update(const Ref<Image> &p_image); + Ref<Image> get_image() const override; + + int get_width() const override; + int get_height() const override; + + virtual RID get_rid() const override; + + bool has_alpha() const override; + virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; + virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; + virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override; + + bool is_pixel_opaque(int p_x, int p_y) const override; + + virtual void set_path(const String &p_path, bool p_take_over = false) override; + + void set_size_override(const Size2 &p_size); + Size2 get_size_override() const; + + void set_keep_compressed_buffer(bool p_keep); + bool is_keeping_compressed_buffer() const; + + static void set_keep_all_compressed_buffers(bool p_keep); + static bool is_keeping_all_compressed_buffers(); + + PortableCompressedTexture2D(); + ~PortableCompressedTexture2D(); +}; + +VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode) + +class CompressedTexture2D : public Texture2D { + GDCLASS(CompressedTexture2D, Texture2D); public: enum DataFormat { @@ -172,10 +254,10 @@ protected: void _validate_property(PropertyInfo &property) const override; public: - static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit); + static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit); - typedef void (*TextureFormatRequestCallback)(const Ref<StreamTexture2D> &); - typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<StreamTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel); + typedef void (*TextureFormatRequestCallback)(const Ref<CompressedTexture2D> &); + typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<CompressedTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel); static TextureFormatRequestCallback request_3d_callback; static TextureFormatRoughnessRequestCallback request_roughness_callback; @@ -200,13 +282,13 @@ public: virtual Ref<Image> get_image() const override; - StreamTexture2D(); - ~StreamTexture2D(); + CompressedTexture2D(); + ~CompressedTexture2D(); }; -class ResourceFormatLoaderStreamTexture2D : public ResourceFormatLoader { +class ResourceFormatLoaderCompressedTexture2D : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -300,6 +382,13 @@ class TextureLayered : public Texture { protected: static void _bind_methods(); + GDVIRTUAL0RC(Image::Format, _get_format) + GDVIRTUAL0RC(uint32_t, _get_layered_type) + GDVIRTUAL0RC(int, _get_width) + GDVIRTUAL0RC(int, _get_height) + GDVIRTUAL0RC(int, _get_layers) + GDVIRTUAL0RC(bool, _has_mipmaps) + GDVIRTUAL1RC(Ref<Image>, _get_layer_data, int) public: enum LayeredType { LAYERED_TYPE_2D_ARRAY, @@ -307,13 +396,15 @@ public: LAYERED_TYPE_CUBEMAP_ARRAY }; - virtual Image::Format get_format() const = 0; - virtual LayeredType get_layered_type() const = 0; - virtual int get_width() const = 0; - virtual int get_height() const = 0; - virtual int get_layers() const = 0; - virtual bool has_mipmaps() const = 0; - virtual Ref<Image> get_layer_data(int p_layer) const = 0; + virtual Image::Format get_format() const; + virtual LayeredType get_layered_type() const; + virtual int get_width() const; + virtual int get_height() const; + virtual int get_layers() const; + virtual bool has_mipmaps() const; + virtual Ref<Image> get_layer_data(int p_layer) const; + + TextureLayered() {} }; VARIANT_ENUM_CAST(TextureLayered::LayeredType) @@ -380,8 +471,8 @@ public: ImageTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} }; -class StreamTextureLayered : public TextureLayered { - GDCLASS(StreamTextureLayered, TextureLayered); +class CompressedTextureLayered : public TextureLayered { + GDCLASS(CompressedTextureLayered, TextureLayered); public: enum DataFormat { @@ -433,36 +524,36 @@ public: virtual Ref<Image> get_layer_data(int p_layer) const override; - StreamTextureLayered(LayeredType p_layered_type); - ~StreamTextureLayered(); + CompressedTextureLayered(LayeredType p_layered_type); + ~CompressedTextureLayered(); }; -class StreamTexture2DArray : public StreamTextureLayered { - GDCLASS(StreamTexture2DArray, StreamTextureLayered) +class CompressedTexture2DArray : public CompressedTextureLayered { + GDCLASS(CompressedTexture2DArray, CompressedTextureLayered) public: - StreamTexture2DArray() : - StreamTextureLayered(LAYERED_TYPE_2D_ARRAY) {} + CompressedTexture2DArray() : + CompressedTextureLayered(LAYERED_TYPE_2D_ARRAY) {} }; -class StreamCubemap : public StreamTextureLayered { - GDCLASS(StreamCubemap, StreamTextureLayered); +class CompressedCubemap : public CompressedTextureLayered { + GDCLASS(CompressedCubemap, CompressedTextureLayered); public: - StreamCubemap() : - StreamTextureLayered(LAYERED_TYPE_CUBEMAP) {} + CompressedCubemap() : + CompressedTextureLayered(LAYERED_TYPE_CUBEMAP) {} }; -class StreamCubemapArray : public StreamTextureLayered { - GDCLASS(StreamCubemapArray, StreamTextureLayered); +class CompressedCubemapArray : public CompressedTextureLayered { + GDCLASS(CompressedCubemapArray, CompressedTextureLayered); public: - StreamCubemapArray() : - StreamTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} + CompressedCubemapArray() : + CompressedTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} }; -class ResourceFormatLoaderStreamTextureLayered : public ResourceFormatLoader { +class ResourceFormatLoaderCompressedTextureLayered : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -474,15 +565,21 @@ class Texture3D : public Texture { protected: static void _bind_methods(); - TypedArray<Image> _get_data() const; + TypedArray<Image> _get_datai() const; + GDVIRTUAL0RC(Image::Format, _get_format) + GDVIRTUAL0RC(int, _get_width) + GDVIRTUAL0RC(int, _get_height) + GDVIRTUAL0RC(int, _get_depth) + GDVIRTUAL0RC(bool, _has_mipmaps) + GDVIRTUAL0RC(TypedArray<Image>, _get_data) public: - virtual Image::Format get_format() const = 0; - virtual int get_width() const = 0; - virtual int get_height() const = 0; - virtual int get_depth() const = 0; - virtual bool has_mipmaps() const = 0; - virtual Vector<Ref<Image>> get_data() const = 0; + virtual Image::Format get_format() const; + virtual int get_width() const; + virtual int get_height() const; + virtual int get_depth() const; + virtual bool has_mipmaps() const; + virtual Vector<Ref<Image>> get_data() const; }; class ImageTexture3D : public Texture3D { @@ -520,8 +617,8 @@ public: ~ImageTexture3D(); }; -class StreamTexture3D : public Texture3D { - GDCLASS(StreamTexture3D, Texture3D); +class CompressedTexture3D : public Texture3D { + GDCLASS(CompressedTexture3D, Texture3D); public: enum DataFormat { @@ -571,13 +668,13 @@ public: virtual Vector<Ref<Image>> get_data() const override; - StreamTexture3D(); - ~StreamTexture3D(); + CompressedTexture3D(); + ~CompressedTexture3D(); }; -class ResourceFormatLoaderStreamTexture3D : public ResourceFormatLoader { +class ResourceFormatLoaderCompressedTexture3D : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -915,4 +1012,98 @@ public: ~CameraTexture(); }; -#endif +class PlaceholderTexture2D : public Texture2D { + GDCLASS(PlaceholderTexture2D, Texture2D) + + RID rid; + Size2 size = Size2(1, 1); + +protected: + static void _bind_methods(); + +public: + void set_size(Size2 p_size); + + virtual int get_width() const override; + virtual int get_height() const override; + virtual RID get_rid() const override; + virtual bool has_alpha() const override; + + virtual Ref<Image> get_image() const override; + + PlaceholderTexture2D(); + ~PlaceholderTexture2D(); +}; + +class PlaceholderTexture3D : public Texture3D { + GDCLASS(PlaceholderTexture3D, Texture3D) + + RID rid; + Vector3i size = Vector3i(1, 1, 1); + +protected: + static void _bind_methods(); + +public: + void set_size(const Vector3i &p_size); + Vector3i get_size() const; + virtual Image::Format get_format() const override; + virtual int get_width() const override; + virtual int get_height() const override; + virtual int get_depth() const override; + virtual bool has_mipmaps() const override; + virtual Vector<Ref<Image>> get_data() const override; + + PlaceholderTexture3D(); + ~PlaceholderTexture3D(); +}; + +class PlaceholderTextureLayered : public TextureLayered { + GDCLASS(PlaceholderTextureLayered, TextureLayered) + + RID rid; + Size2i size = Size2i(1, 1); + int layers = 1; + LayeredType layered_type = LAYERED_TYPE_2D_ARRAY; + +protected: + static void _bind_methods(); + +public: + void set_size(const Size2i &p_size); + Size2i get_size() const; + void set_layers(int p_layers); + virtual Image::Format get_format() const override; + virtual LayeredType get_layered_type() const override; + virtual int get_width() const override; + virtual int get_height() const override; + virtual int get_layers() const override; + virtual bool has_mipmaps() const override; + virtual Ref<Image> get_layer_data(int p_layer) const override; + + PlaceholderTextureLayered(LayeredType p_type); + ~PlaceholderTextureLayered(); +}; + +class PlaceholderTexture2DArray : public PlaceholderTextureLayered { + GDCLASS(PlaceholderTexture2DArray, PlaceholderTextureLayered) +public: + PlaceholderTexture2DArray() : + PlaceholderTextureLayered(LAYERED_TYPE_2D_ARRAY) {} +}; + +class PlaceholderCubemap : public PlaceholderTextureLayered { + GDCLASS(PlaceholderCubemap, PlaceholderTextureLayered) +public: + PlaceholderCubemap() : + PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP) {} +}; + +class PlaceholderCubemapArray : public PlaceholderTextureLayered { + GDCLASS(PlaceholderCubemapArray, PlaceholderTextureLayered) +public: + PlaceholderCubemapArray() : + PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} +}; + +#endif // TEXTURE_H diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 373fbb94ea..39b77568cf 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -123,82 +123,64 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const { void Theme::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> list; - const StringName *key = nullptr; - // Type variations. - while ((key = variation_map.next(key))) { - list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type")); + for (const KeyValue<StringName, StringName> &E : variation_map) { + list.push_back(PropertyInfo(Variant::STRING_NAME, String() + E.key + "/base_type")); } - key = nullptr; - // Icons. - while ((key = icon_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = icon_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { + for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { + list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/icons/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } - key = nullptr; - // Styles. - while ((key = style_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = style_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { + for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { + list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/styles/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } - key = nullptr; - // Fonts. - while ((key = font_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = font_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { + for (const KeyValue<StringName, Ref<Font>> &F : E.value) { + list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/fonts/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } - 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, PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { + for (const KeyValue<StringName, int> &F : E.value) { + list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/font_sizes/" + F.key, PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px")); } } - key = nullptr; - // Colors. - while ((key = color_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = color_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2)); + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { + for (const KeyValue<StringName, Color> &F : E.value) { + list.push_back(PropertyInfo(Variant::COLOR, String() + E.key + "/colors/" + F.key)); } } - key = nullptr; - // Constants. - while ((key = constant_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = constant_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2)); + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { + for (const KeyValue<StringName, int> &F : E.value) { + list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key)); } } // Sort and store properties. list.sort(); + String prev_type; for (const PropertyInfo &E : list) { + // Add groups for types so that their names are left unchanged in the inspector. + String current_type = E.name.get_slice("/", 0); + if (prev_type != current_type) { + p_list->push_back(PropertyInfo(Variant::NIL, current_type, PROPERTY_HINT_NONE, current_type + "/", PROPERTY_USAGE_GROUP)); + prev_type = current_type; + } + p_list->push_back(E); } } @@ -261,6 +243,27 @@ int Theme::get_fallback_font_size() { return fallback_font_size; } +bool Theme::is_valid_type_name(const String &p_name) { + for (int i = 0; i < p_name.length(); i++) { + if (!is_ascii_identifier_char(p_name[i])) { + return false; + } + } + return true; +} + +bool Theme::is_valid_item_name(const String &p_name) { + if (p_name.is_empty()) { + return false; + } + for (int i = 0; i < p_name.length(); i++) { + if (!is_ascii_identifier_char(p_name[i])) { + return false; + } + } + return true; +} + // Fallback values for theme item types, configurable per theme. void Theme::set_default_base_scale(float p_base_scale) { if (default_base_scale == p_base_scale) { @@ -326,6 +329,9 @@ bool Theme::has_default_font_size() const { // Icons. void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -358,6 +364,8 @@ bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme } void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist."); @@ -388,18 +396,18 @@ void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) con return; } - const StringName *key = nullptr; - - while ((key = icon_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_icon_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (icon_map.has(p_theme_type)) { return; } - icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>(); + icon_map[p_theme_type] = ThemeIconMap(); } void Theme::remove_icon_type(const StringName &p_theme_type) { @@ -409,9 +417,8 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = icon_map[p_theme_type].next(L))) { - Ref<Texture2D> icon = icon_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) { + Ref<Texture2D> icon = E.value; if (icon.is_valid()) { icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -425,14 +432,16 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { void Theme::get_icon_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = icon_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { + p_list->push_back(E.key); } } // Styleboxes. void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -465,6 +474,8 @@ bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_t } void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist."); @@ -495,18 +506,18 @@ void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) return; } - const StringName *key = nullptr; - - while ((key = style_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_stylebox_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (style_map.has(p_theme_type)) { return; } - style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>(); + style_map[p_theme_type] = ThemeStyleMap(); } void Theme::remove_stylebox_type(const StringName &p_theme_type) { @@ -516,9 +527,8 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = style_map[p_theme_type].next(L))) { - Ref<StyleBox> style = style_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) { + Ref<StyleBox> style = E.value; if (style.is_valid()) { style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -532,14 +542,16 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { void Theme::get_stylebox_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = style_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { + p_list->push_back(E.key); } } // Fonts. void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (font_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -574,6 +586,8 @@ bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme } void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist."); @@ -604,18 +618,18 @@ void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) con return; } - const StringName *key = nullptr; - - while ((key = font_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_font_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (font_map.has(p_theme_type)) { return; } - font_map[p_theme_type] = HashMap<StringName, Ref<Font>>(); + font_map[p_theme_type] = ThemeFontMap(); } void Theme::remove_font_type(const StringName &p_theme_type) { @@ -625,9 +639,8 @@ void Theme::remove_font_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = font_map[p_theme_type].next(L))) { - Ref<Font> font = font_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) { + Ref<Font> font = E.value; if (font.is_valid()) { font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -641,14 +654,16 @@ void Theme::remove_font_type(const StringName &p_theme_type) { void Theme::get_font_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = font_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { + p_list->push_back(E.key); } } // Font sizes. void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_font_size_nocheck(p_name, p_theme_type); font_size_map[p_theme_type][p_name] = p_font_size; @@ -674,6 +689,8 @@ bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_ } void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist."); @@ -700,18 +717,18 @@ void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list return; } - const StringName *key = nullptr; - - while ((key = font_size_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, int> &E : font_size_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_font_size_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (font_size_map.has(p_theme_type)) { return; } - font_size_map[p_theme_type] = HashMap<StringName, int>(); + font_size_map[p_theme_type] = ThemeFontSizeMap(); } void Theme::remove_font_size_type(const StringName &p_theme_type) { @@ -725,14 +742,16 @@ void Theme::remove_font_size_type(const StringName &p_theme_type) { void Theme::get_font_size_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = font_size_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { + p_list->push_back(E.key); } } // Colors. void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_color_nocheck(p_name, p_theme_type); color_map[p_theme_type][p_name] = p_color; @@ -756,6 +775,8 @@ bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_them } void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist."); @@ -782,18 +803,18 @@ void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) co return; } - const StringName *key = nullptr; - - while ((key = color_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Color> &E : color_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_color_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (color_map.has(p_theme_type)) { return; } - color_map[p_theme_type] = HashMap<StringName, Color>(); + color_map[p_theme_type] = ThemeColorMap(); } void Theme::remove_color_type(const StringName &p_theme_type) { @@ -807,14 +828,16 @@ void Theme::remove_color_type(const StringName &p_theme_type) { void Theme::get_color_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = color_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { + p_list->push_back(E.key); } } // Theme constants. void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_constant_nocheck(p_name, p_theme_type); constant_map[p_theme_type][p_name] = p_constant; @@ -838,6 +861,8 @@ bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_t } void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist."); @@ -864,18 +889,18 @@ void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) return; } - const StringName *key = nullptr; - - while ((key = constant_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, int> &E : constant_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_constant_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (constant_map.has(p_theme_type)) { return; } - constant_map[p_theme_type] = HashMap<StringName, int>(); + constant_map[p_theme_type] = ThemeConstantMap(); } void Theme::remove_constant_type(const StringName &p_theme_type) { @@ -889,9 +914,8 @@ void Theme::remove_constant_type(const StringName &p_theme_type) { void Theme::get_constant_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - const StringName *key = nullptr; - while ((key = constant_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { + p_list->push_back(E.key); } } @@ -1154,6 +1178,8 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l // Theme type variations. void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_base_type), vformat("Invalid type name: '%s'", 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."); @@ -1246,51 +1272,43 @@ void Theme::remove_type(const StringName &p_theme_type) { void Theme::get_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - Set<StringName> types; - const StringName *key = nullptr; + // This Set guarantees uniqueness. + // Because each map can have the same type defined, but for this method + // we only want one occurrence of each type. + HashSet<StringName> types; // Icons. - while ((key = icon_map.next(key))) { - types.insert(*key); + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { + types.insert(E.key); } - key = nullptr; - - // StyleBoxes. - while ((key = style_map.next(key))) { - types.insert(*key); + // Styles. + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { + types.insert(E.key); } - key = nullptr; - // Fonts. - while ((key = font_map.next(key))) { - types.insert(*key); + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { + types.insert(E.key); } - key = nullptr; - // Font sizes. - while ((key = font_size_map.next(key))) { - types.insert(*key); + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { + types.insert(E.key); } - key = nullptr; - // Colors. - while ((key = color_map.next(key))) { - types.insert(*key); + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { + types.insert(E.key); } - key = nullptr; - // Constants. - while ((key = constant_map.next(key))) { - types.insert(*key); + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { + types.insert(E.key); } - for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { - p_list->push_back(E->get()); + for (const StringName &E : types) { + p_list->push_back(E); } } @@ -1602,75 +1620,62 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Colors. { - const StringName *K = nullptr; - while ((K = p_other->color_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->color_map[*K].next(L))) { - set_color(*L, *K, p_other->color_map[*K][*L]); + for (const KeyValue<StringName, ThemeColorMap> &E : p_other->color_map) { + for (const KeyValue<StringName, Color> &F : E.value) { + set_color(F.key, E.key, F.value); } } } // Constants. { - const StringName *K = nullptr; - while ((K = p_other->constant_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->constant_map[*K].next(L))) { - set_constant(*L, *K, p_other->constant_map[*K][*L]); + for (const KeyValue<StringName, ThemeConstantMap> &E : p_other->constant_map) { + for (const KeyValue<StringName, int> &F : E.value) { + set_constant(F.key, E.key, F.value); } } } // Fonts. { - const StringName *K = nullptr; - while ((K = p_other->font_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->font_map[*K].next(L))) { - set_font(*L, *K, p_other->font_map[*K][*L]); + for (const KeyValue<StringName, ThemeFontMap> &E : p_other->font_map) { + for (const KeyValue<StringName, Ref<Font>> &F : E.value) { + set_font(F.key, E.key, F.value); } } } // Font sizes. { - const StringName *K = nullptr; - while ((K = p_other->font_size_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->font_size_map[*K].next(L))) { - set_font_size(*L, *K, p_other->font_size_map[*K][*L]); + for (const KeyValue<StringName, ThemeFontSizeMap> &E : p_other->font_size_map) { + for (const KeyValue<StringName, int> &F : E.value) { + set_font_size(F.key, E.key, F.value); } } } // Icons. { - const StringName *K = nullptr; - while ((K = p_other->icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->icon_map[*K].next(L))) { - set_icon(*L, *K, p_other->icon_map[*K][*L]); + for (const KeyValue<StringName, ThemeIconMap> &E : p_other->icon_map) { + for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { + set_icon(F.key, E.key, F.value); } } } // Styleboxes. { - const StringName *K = nullptr; - while ((K = p_other->style_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->style_map[*K].next(L))) { - set_stylebox(*L, *K, p_other->style_map[*K][*L]); + for (const KeyValue<StringName, ThemeStyleMap> &E : p_other->style_map) { + for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { + set_stylebox(F.key, E.key, F.value); } } } // Type variations. { - const StringName *K = nullptr; - while ((K = p_other->variation_map.next(K))) { - set_type_variation(*K, p_other->variation_map[*K]); + for (const KeyValue<StringName, StringName> &E : p_other->variation_map) { + set_type_variation(E.key, E.value); } } @@ -1680,12 +1685,10 @@ void Theme::merge_with(const Ref<Theme> &p_other) { void Theme::clear() { // These items need disconnecting. { - const StringName *K = nullptr; - while ((K = icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = icon_map[*K].next(L))) { - Ref<Texture2D> icon = icon_map[*K][*L]; - if (icon.is_valid()) { + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { + for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { + if (F.value.is_valid()) { + Ref<Texture2D> icon = F.value; icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1693,12 +1696,10 @@ void Theme::clear() { } { - const StringName *K = nullptr; - while ((K = style_map.next(K))) { - const StringName *L = nullptr; - while ((L = style_map[*K].next(L))) { - Ref<StyleBox> style = style_map[*K][*L]; - if (style.is_valid()) { + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { + for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { + if (F.value.is_valid()) { + Ref<StyleBox> style = F.value; style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1706,12 +1707,10 @@ void Theme::clear() { } { - const StringName *K = nullptr; - while ((K = font_map.next(K))) { - const StringName *L = nullptr; - while ((L = font_map[*K].next(L))) { - Ref<Font> font = font_map[*K][*L]; - if (font.is_valid()) { + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { + for (const KeyValue<StringName, Ref<Font>> &F : E.value) { + if (F.value.is_valid()) { + Ref<Font> font = F.value; font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1819,7 +1818,7 @@ void Theme::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater"), "set_default_font_size", "get_default_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px"), "set_default_font_size", "get_default_font_size"); BIND_ENUM_CONSTANT(DATA_TYPE_COLOR); BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 9afe05007d..a2aca5e61f 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -47,6 +47,13 @@ class Theme : public Resource { #endif public: + using ThemeIconMap = HashMap<StringName, Ref<Texture2D>>; + using ThemeStyleMap = HashMap<StringName, Ref<StyleBox>>; + using ThemeFontMap = HashMap<StringName, Ref<Font>>; + using ThemeFontSizeMap = HashMap<StringName, int>; + using ThemeColorMap = HashMap<StringName, Color>; + using ThemeConstantMap = HashMap<StringName, int>; + enum DataType { DATA_TYPE_COLOR, DATA_TYPE_CONSTANT, @@ -62,12 +69,12 @@ private: void _emit_theme_changed(bool p_notify_list_changed = false); - HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; - HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map; - HashMap<StringName, HashMap<StringName, Ref<Font>>> font_map; - HashMap<StringName, HashMap<StringName, int>> font_size_map; - HashMap<StringName, HashMap<StringName, Color>> color_map; - HashMap<StringName, HashMap<StringName, int>> constant_map; + HashMap<StringName, ThemeIconMap> icon_map; + HashMap<StringName, ThemeStyleMap> style_map; + HashMap<StringName, ThemeFontMap> font_map; + HashMap<StringName, ThemeFontSizeMap> font_size_map; + HashMap<StringName, ThemeColorMap> color_map; + HashMap<StringName, ThemeConstantMap> constant_map; HashMap<StringName, StringName> variation_map; HashMap<StringName, List<StringName>> variation_base_map; @@ -137,6 +144,9 @@ public: static Ref<Font> get_fallback_font(); static int get_fallback_font_size(); + static bool is_valid_type_name(const String &p_name); + static bool is_valid_item_name(const String &p_name); + void set_default_base_scale(float p_base_scale); float get_default_base_scale() const; bool has_default_base_scale() const; @@ -246,4 +256,4 @@ public: VARIANT_ENUM_CAST(Theme::DataType); -#endif +#endif // THEME_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 1e84947b87..a59870f4a9 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -34,7 +34,7 @@ #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/templates/local_vector.h" - +#include "core/templates/rb_set.h" #include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -236,6 +236,9 @@ bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_patte return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i]; } } + if (terrain != p_terrains_pattern.terrain) { + return terrain < p_terrains_pattern.terrain; + } for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) { return bits[i] < p_terrains_pattern.bits[i]; @@ -253,10 +256,23 @@ bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_patt return false; } } + if (terrain != p_terrains_pattern.terrain) { + return false; + } return true; } -void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) { +void TileSet::TerrainsPattern::set_terrain(int p_terrain) { + ERR_FAIL_COND(p_terrain < -1); + + terrain = p_terrain; +} + +int TileSet::TerrainsPattern::get_terrain() const { + return terrain; +} + +void TileSet::TerrainsPattern::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain) { ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX); ERR_FAIL_COND(!is_valid_bit[p_peering_bit]); ERR_FAIL_COND(p_terrain < -1); @@ -271,25 +287,27 @@ void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, bits[p_peering_bit] = p_terrain; } -int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const { +int TileSet::TerrainsPattern::get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const { ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1); ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1); return bits[p_peering_bit]; } -void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) { - int in_array_index = 0; +void TileSet::TerrainsPattern::from_array(Array p_terrains) { + set_terrain(p_terrains[0]); + int in_array_index = 1; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { if (is_valid_bit[i]) { - ERR_FAIL_COND(in_array_index >= p_terrains.size()); - set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]); + ERR_FAIL_INDEX(in_array_index, p_terrains.size()); + set_terrain_peering_bit(TileSet::CellNeighbor(i), p_terrains[in_array_index]); in_array_index++; } } } -Array TileSet::TerrainsPattern::get_terrains_as_array() const { +Array TileSet::TerrainsPattern::as_array() const { Array output; + output.push_back(get_terrain()); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { if (is_valid_bit[i]) { output.push_back(bits[i]); @@ -297,10 +315,11 @@ Array TileSet::TerrainsPattern::get_terrains_as_array() const { } return output; } + TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) { ERR_FAIL_COND(p_terrain_set < 0); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { - is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i))); + is_valid_bit[i] = (p_tile_set->is_valid_terrain_peering_bit(p_terrain_set, TileSet::CellNeighbor(i))); bits[i] = -1; } valid = true; @@ -410,11 +429,16 @@ void TileSet::_update_terrains_cache() { TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern(); + // Main terrain. + if (terrains_pattern.get_terrain() >= 0) { + per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell); + } + // Terrain bits. 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 = terrains_pattern.get_terrain(bit); + if (is_valid_terrain_peering_bit(terrain_set, bit)) { + int terrain = terrains_pattern.get_terrain_peering_bit(bit); if (terrain >= 0) { per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell); } @@ -822,7 +846,7 @@ 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_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { +bool TileSet::is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { 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 || @@ -905,13 +929,13 @@ bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, return false; } -bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { +bool TileSet::is_valid_terrain_peering_bit(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); + return is_valid_terrain_peering_bit_for_mode(terrain_mode, p_peering_bit); } // Navigation @@ -1345,19 +1369,19 @@ int TileSet::get_patterns_count() { return patterns.size(); } -Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>()); +RBSet<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileSet::TerrainsPattern>()); _update_terrains_cache(); - Set<TileSet::TerrainsPattern> output; - for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) { + RBSet<TileSet::TerrainsPattern> output; + for (KeyValue<TileSet::TerrainsPattern, RBSet<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) { output.insert(kv.key); } return output; } -Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>()); +RBSet<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileMapCell>()); _update_terrains_cache(); return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; } @@ -1368,13 +1392,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti // Count the sum of probabilities. double sum = 0.0; - Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; - for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { - if (E->get().source_id >= 0) { - Ref<TileSetSource> source = sources[E->get().source_id]; + RBSet<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; + for (const TileMapCell &E : set) { + if (E.source_id >= 0) { + Ref<TileSetSource> source = sources[E.source_id]; Ref<TileSetAtlasSource> atlas_source = source; if (atlas_source.is_valid()) { - TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile); + TileData *tile_data = atlas_source->get_tile_data(E.get_atlas_coords(), E.alternative_tile); sum += tile_data->get_probability(); } else { sum += 1.0; @@ -1389,13 +1413,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti double picked = Math::random(0.0, sum); // Pick the tile. - for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { - if (E->get().source_id >= 0) { - Ref<TileSetSource> source = sources[E->get().source_id]; + for (const TileMapCell &E : set) { + if (E.source_id >= 0) { + Ref<TileSetSource> source = sources[E.source_id]; Ref<TileSetAtlasSource> atlas_source = source; if (atlas_source.is_valid()) { - TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile); + TileData *tile_data = atlas_source->get_tile_data(E.get_atlas_coords(), E.alternative_tile); count += tile_data->get_probability(); } else { count += 1.0; @@ -1405,7 +1429,7 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti } if (count >= picked) { - return E->get(); + return E; } } @@ -1494,26 +1518,48 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform } } -Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::get_terrain_polygon(int p_terrain_set) { + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + return _get_square_terrain_polygon(tile_size); + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + return _get_isometric_terrain_polygon(tile_size); + } 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; + } + return _get_half_offset_terrain_polygon(tile_size, overlap, tile_offset_axis); + } + return Vector<Point2>(); +} + +Vector<Point2> TileSet::get_terrain_peering_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); + return _get_square_corner_or_side_terrain_peering_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); + return _get_square_corner_terrain_peering_bit_polygon(tile_size, p_bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - return _get_square_side_terrain_bit_polygon(tile_size, p_bit); + return _get_square_side_terrain_peering_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); + return _get_isometric_corner_or_side_terrain_peering_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); + return _get_isometric_corner_terrain_peering_bit_polygon(tile_size, p_bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit); + return _get_isometric_side_terrain_peering_bit_polygon(tile_size, p_bit); } } else { float overlap = 0.0; @@ -1528,11 +1574,11 @@ Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::Cell 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); + return _get_half_offset_corner_or_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit); } 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); + return _get_half_offset_corner_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + return _get_half_offset_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit); } } } @@ -1544,30 +1590,68 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, if (terrain_bits_meshes_dirty) { // Recompute the meshes. - terrain_bits_meshes.clear(); + terrain_peering_bits_meshes.clear(); for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) { TerrainMode terrain_mode = TerrainMode(terrain_mode_index); + + // Center terrain + Vector<Vector2> polygon; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + polygon = _get_square_terrain_polygon(tile_size); + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + polygon = _get_isometric_terrain_polygon(tile_size); + } 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; + } + polygon = _get_half_offset_terrain_polygon(tile_size, 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_meshes[terrain_mode] = mesh; + } + // Peering bits 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 (is_valid_terrain_peering_bit_for_mode(terrain_mode, bit)) { 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); + polygon = _get_square_corner_or_side_terrain_peering_bit_polygon(tile_size, bit); } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit); + polygon = _get_square_corner_terrain_peering_bit_polygon(tile_size, bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - polygon = _get_square_side_terrain_bit_polygon(tile_size, bit); + polygon = _get_square_side_terrain_peering_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); + polygon = _get_isometric_corner_or_side_terrain_peering_bit_polygon(tile_size, bit); } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit); + polygon = _get_isometric_corner_terrain_peering_bit_polygon(tile_size, bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit); + polygon = _get_isometric_side_terrain_peering_bit_polygon(tile_size, bit); } } else { float overlap = 0.0; @@ -1582,29 +1666,30 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, 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); + polygon = _get_half_offset_corner_or_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit); } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + polygon = _get_half_offset_corner_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit); } else { // TileData::TERRAIN_MODE_MATCH_SIDES - polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + polygon = _get_half_offset_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit); } } - - 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; + { + 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_peering_bits_meshes[terrain_mode][bit] = mesh; + } } } } @@ -1618,14 +1703,21 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, 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); + int terrain_id = p_tile_data->get_terrain(); + if (terrain_id >= 0) { + Color color = get_terrain_color(terrain_set, terrain_id); + color.a = TERRAIN_ALPHA; + p_canvas_item->draw_mesh(terrain_meshes[terrain_mode], Ref<Texture2D>(), Transform2D(), color); + } + 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 (is_valid_terrain_peering_bit(terrain_set, bit)) { + terrain_id = p_tile_data->get_terrain_peering_bit(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); + p_canvas_item->draw_mesh(terrain_peering_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color); } } } @@ -1670,13 +1762,16 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { bit_counts[terrain] = 0; } + if (tile_data->get_terrain() >= 0) { + bit_counts[tile_data->get_terrain()] += 10; + } 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 (is_valid_terrain_peering_bit(terrain_set, cell_neighbor)) { + int terrain = tile_data->get_terrain_peering_bit(cell_neighbor); if (terrain >= 0) { if (terrain >= (int)bit_counts.size()) { - WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain)); + WARN_PRINT(vformat("Invalid terrain peering bit: %d", terrain)); } else { bit_counts[terrain] += 1; } @@ -1706,19 +1801,16 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { 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; + Rect2i 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->blit_rect(texture->get_image(), region, Point2i()); 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); + Ref<ImageTexture> icon = ImageTexture::create_from_image(image); icon->set_size_override(p_size); - output.write[terrain_set].write[terrain] = icon; } } @@ -1730,7 +1822,17 @@ void TileSet::_source_changed() { emit_changed(); } -Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_square_terrain_polygon(Vector2i p_size) { + Rect2 rect(-Vector2(p_size) / 6.0, Vector2(p_size) / 3.0); + return { + rect.position, + Vector2(rect.get_end().x, rect.position.y), + rect.get_end(), + Vector2(rect.position.x, rect.get_end().y) + }; +} + +Vector<Point2> TileSet::_get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Rect2 bit_rect; bit_rect.size = Vector2(p_size) / 3; switch (p_bit) { @@ -1773,7 +1875,7 @@ Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i return polygon; } -Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Vector2 unit = Vector2(p_size) / 6.0; Vector<Vector2> polygon; switch (p_bit) { @@ -1815,7 +1917,7 @@ Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, return polygon; } -Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_square_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Vector2 unit = Vector2(p_size) / 6.0; Vector<Vector2> polygon; switch (p_bit) { @@ -1849,7 +1951,17 @@ Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, Ti return polygon; } -Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_isometric_terrain_polygon(Vector2i p_size) { + Vector2 unit = Vector2(p_size) / 6.0; + return { + Vector2(1, 0) * unit, + Vector2(0, 1) * unit, + Vector2(-1, 0) * unit, + Vector2(0, -1) * unit, + }; +} + +Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Vector2 unit = Vector2(p_size) / 6.0; Vector<Vector2> polygon; switch (p_bit) { @@ -1907,7 +2019,7 @@ Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector return polygon; } -Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Vector2 unit = Vector2(p_size) / 6.0; Vector<Vector2> polygon; switch (p_bit) { @@ -1949,7 +2061,7 @@ Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_siz return polygon; } -Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { +Vector<Point2> TileSet::_get_isometric_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { Vector2 unit = Vector2(p_size) / 6.0; Vector<Vector2> polygon; switch (p_bit) { @@ -1983,7 +2095,30 @@ Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, 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<Point2> TileSet::_get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector2 unit = Vector2(p_size) / 6.0; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + return { + Vector2(1, 1.0 - p_overlap * 2.0) * unit, + Vector2(0, 1) * unit, + Vector2(-1, 1.0 - p_overlap * 2.0) * unit, + Vector2(-1, -1.0 + p_overlap * 2.0) * unit, + Vector2(0, -1) * unit, + Vector2(1, -1.0 + p_overlap * 2.0) * unit, + }; + } else { + return { + Vector2(1, 0) * unit, + Vector2(1.0 - p_overlap * 2.0, -1) * unit, + Vector2(-1.0 + p_overlap * 2.0, -1) * unit, + Vector2(-1, 0) * unit, + Vector2(-1.0 + p_overlap * 2.0, 1) * unit, + Vector2(1.0 - p_overlap * 2.0, 1) * unit, + }; + } +} + +Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) { Vector<Vector2> point_list = { Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0), Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)), @@ -2006,12 +2141,11 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect }; 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) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } switch (p_bit) { case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: polygon.push_back(point_list[17]); @@ -2071,10 +2205,8 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect 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); - } + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit; } switch (p_bit) { case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: @@ -2144,7 +2276,7 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect 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<Point2> TileSet::_get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) { Vector<Vector2> point_list = { Vector2(3, 0), Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)), @@ -2161,12 +2293,11 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s }; 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) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } switch (p_bit) { case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: polygon.push_back(point_list[0]); @@ -2202,10 +2333,8 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s 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); - } + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit; } switch (p_bit) { case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: @@ -2251,7 +2380,7 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s 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<Point2> TileSet::_get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) { Vector<Vector2> point_list = { Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)), Vector2(0, 3), @@ -2262,12 +2391,11 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_siz }; 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) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } switch (p_bit) { case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: polygon.push_back(point_list[5]); @@ -2297,10 +2425,8 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_siz 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); - } + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit; } switch (p_bit) { case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: @@ -2341,9 +2467,42 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_siz } void TileSet::reset_state() { + // Rendering occlusion_layers.clear(); + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); + tile_meshes_dirty = true; + + // Physics physics_layers.clear(); + + // Terrains + terrain_sets.clear(); + terrain_meshes.clear(); + terrain_peering_bits_meshes.clear(); + per_terrain_pattern_tiles.clear(); + terrains_cache_dirty = true; + + // Navigation + navigation_layers.clear(); + custom_data_layers.clear(); + custom_data_layers_by_name.clear(); + + // Proxies + source_level_proxies.clear(); + coords_level_proxies.clear(); + alternative_level_proxies.clear(); + +#ifndef DISABLE_DEPRECATED + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); + } + compatibility_data.clear(); +#endif // DISABLE_DEPRECATED + while (!source_ids.is_empty()) { + remove_source(source_ids[0]); + } } const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); @@ -2391,7 +2550,7 @@ void TileSet::_compatibility_conversion() { value_array.push_back(alternative_tile); if (!compatibility_tilemap_mapping.has(E.key)) { - compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); + compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>(); } compatibility_tilemap_mapping[E.key][key_array] = value_array; compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; @@ -2483,7 +2642,7 @@ void TileSet::_compatibility_conversion() { value_array.push_back(alternative_tile); if (!compatibility_tilemap_mapping.has(E.key)) { - compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); + compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>(); } compatibility_tilemap_mapping[E.key][key_array] = value_array; compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; @@ -2571,7 +2730,7 @@ void TileSet::_compatibility_conversion() { for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { memdelete(E.value); } - compatibility_data = Map<int, CompatibilityTileData *>(); + compatibility_data = HashMap<int, CompatibilityTileData *>(); } Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose) { @@ -2622,12 +2781,12 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { // Get or create the compatibility object CompatibilityTileData *ctd; - Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id); + HashMap<int, CompatibilityTileData *>::Iterator E = compatibility_data.find(id); if (!E) { ctd = memnew(CompatibilityTileData); compatibility_data.insert(id, ctd); } else { - ctd = E->get(); + ctd = E->value; } if (components.size() < 2) { @@ -3099,7 +3258,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Terrains. p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match Corners and Sides,Match Corners,Match Sides")); p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index))); for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); @@ -3175,7 +3334,7 @@ void TileSet::_bind_methods() { 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_size", PROPERTY_HINT_NONE, "suffix:px"), "set_tile_size", "get_tile_size"); // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); @@ -3264,16 +3423,10 @@ void TileSet::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); ADD_ARRAY("occlusion_layers", "occlusion_layer_"); - ADD_GROUP("Physics", ""); + ADD_GROUP("", ""); ADD_ARRAY("physics_layers", "physics_layer_"); - - ADD_GROUP("Terrains", ""); ADD_ARRAY("terrain_sets", "terrain_set_"); - - ADD_GROUP("Navigation", ""); ADD_ARRAY("navigation_layers", "navigation_layer_"); - - ADD_GROUP("Custom data", ""); ADD_ARRAY("custom_data_layers", "custom_data_layer_"); // -- Enum binding -- @@ -3337,6 +3490,10 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; } +void TileSetSource::reset_state() { + tile_set = nullptr; +}; + void TileSetSource::_bind_methods() { // Base tiles ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count); @@ -3520,12 +3677,17 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) { } void TileSetAtlasSource::reset_state() { - // Reset all TileData. + tile_set = nullptr; + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { - for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { - E_alternative.value->reset_state(); + for (const KeyValue<int, TileData *> &E_tile_data : E_tile.value.alternatives) { + memdelete(E_tile_data.value); } } + _coords_mapping_cache.clear(); + tiles.clear(); + tiles_ids.clear(); + _queue_update_padded_texture(); } void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { @@ -3807,7 +3969,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { tile_property_list.push_back(property_info); // animation_frames_count. - tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); // animation_frame_*. bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; @@ -4279,9 +4441,9 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_texture_padding"), &TileSetAtlasSource::get_use_texture_padding); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NO_EDITOR), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_texture_padding", "get_use_texture_padding"); // Base tiles @@ -4432,6 +4594,10 @@ void TileSetAtlasSource::_update_padded_texture() { Ref<Image> src = texture->get_image(); + if (!src.is_valid()) { + return; + } + Ref<Image> image; image.instantiate(); image->create(size.x, size.y, false, src->get_format()); @@ -4467,7 +4633,7 @@ void TileSetAtlasSource::_update_padded_texture() { if (!padded_texture.is_valid()) { padded_texture.instantiate(); } - padded_texture->create_from_image(image); + padded_texture->set_image(image); emit_changed(); } @@ -4797,6 +4963,10 @@ void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { void TileData::remove_terrain(int p_terrain_set, int p_index) { if (terrain_set == p_terrain_set) { + if (terrain == p_index) { + terrain = -1; + } + for (int i = 0; i < 16; i++) { if (terrain_peering_bits[i] == p_index) { terrain_peering_bits[i] = -1; @@ -4847,13 +5017,6 @@ void TileData::remove_custom_data_layer(int p_index) { custom_data.remove_at(p_index); } -void TileData::reset_state() { - occluders.clear(); - physics.clear(); - navigation.clear(); - custom_data.clear(); -} - void TileData::set_allow_transform(bool p_allow_transform) { allow_transform = p_allow_transform; } @@ -4931,11 +5094,11 @@ Vector2i TileData::get_texture_offset() const { return tex_offset; } -void TileData::set_material(Ref<ShaderMaterial> p_material) { +void TileData::set_material(Ref<Material> p_material) { material = p_material; emit_signal(SNAME("changed")); } -Ref<ShaderMaterial> TileData::get_material() const { +Ref<Material> TileData::get_material() const { return material; } @@ -5116,36 +5279,51 @@ int TileData::get_terrain_set() const { return terrain_set; } -void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { +void TileData::set_terrain(int p_terrain) { + ERR_FAIL_COND(terrain_set < 0); + ERR_FAIL_COND(p_terrain < -1); + if (tile_set) { + ERR_FAIL_COND(p_terrain >= tile_set->get_terrains_count(terrain_set)); + } + terrain = p_terrain; + emit_signal(SNAME("changed")); +} + +int TileData::get_terrain() const { + return terrain; +} + +void TileData::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX); 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)); + ERR_FAIL_COND(!is_valid_terrain_peering_bit(p_peering_bit)); } terrain_peering_bits[p_peering_bit] = p_terrain_index; 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); +int TileData::get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(!is_valid_terrain_peering_bit(p_peering_bit), -1); return terrain_peering_bits[p_peering_bit]; } -bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { +bool TileData::is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const { ERR_FAIL_COND_V(!tile_set, false); - return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit); + return tile_set->is_valid_terrain_peering_bit(terrain_set, p_peering_bit); } TileSet::TerrainsPattern TileData::get_terrains_pattern() const { ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern()); TileSet::TerrainsPattern output(tile_set, terrain_set); + output.set_terrain(terrain); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { - if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) { - output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i))); + if (tile_set->is_valid_terrain_peering_bit(terrain_set, TileSet::CellNeighbor(i))) { + output.set_terrain_peering_bit(TileSet::CellNeighbor(i), get_terrain_peering_bit(TileSet::CellNeighbor(i))); } } return output; @@ -5295,7 +5473,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { 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); + set_terrain_peering_bit(bit, p_value); return true; } } @@ -5457,9 +5635,9 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - if (is_valid_peering_bit_terrain(bit)) { + if (is_valid_terrain_peering_bit(bit)) { property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); - if (get_peering_bit_terrain(bit) == -1) { + if (get_terrain_peering_bit(bit) == -1) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -5533,8 +5711,10 @@ void TileData::_bind_methods() { // Terrain ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set); - ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain); - ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain); + ClassDB::bind_method(D_METHOD("set_terrain", "terrain"), &TileData::set_terrain); + ClassDB::bind_method(D_METHOD("get_terrain"), &TileData::get_terrain); + ClassDB::bind_method(D_METHOD("set_terrain_peering_bit", "peering_bit", "terrain"), &TileData::set_terrain_peering_bit); + ClassDB::bind_method(D_METHOD("get_terrain_peering_bit", "peering_bit"), &TileData::get_terrain_peering_bit); // Navigation ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon); @@ -5554,14 +5734,15 @@ void TileData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); ADD_GROUP("Terrains", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain"), "set_terrain", "get_terrain"); ADD_GROUP("Miscellaneous", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability"); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 95de46c9ab..bfd21190d8 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -34,6 +34,7 @@ #include "core/io/resource.h" #include "core/object/object.h" #include "core/templates/local_vector.h" +#include "core/templates/rb_set.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" @@ -69,6 +70,11 @@ union TileMapCell { }; uint64_t _u64t; + + static uint32_t hash(const TileMapCell &p_hash) { + return hash_one_uint64(p_hash._u64t); + } + TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE source_id = p_source_id; set_atlas_coords(p_atlas_coords); @@ -103,13 +109,16 @@ union TileMapCell { bool operator!=(const TileMapCell &p_other) const { return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile); } + bool operator==(const TileMapCell &p_other) const { + return source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile; + } }; class TileMapPattern : public Resource { GDCLASS(TileMapPattern, Resource); Vector2i size; - Map<Vector2i, TileMapCell> pattern; + HashMap<Vector2i, TileMapCell> pattern; void _set_tile_data(const Vector<int> &p_data); Vector<int> _get_tile_data() const; @@ -155,7 +164,7 @@ private: String name; Ref<Texture2D> texture; Vector2 tex_offset; - Ref<ShaderMaterial> material; + Ref<Material> material; Rect2 region; int tile_mode = 0; Color modulate = Color(1, 1, 1); @@ -166,11 +175,11 @@ private: Size2i autotile_tile_size = Size2i(16, 16); int autotile_spacing = 0; - Map<Vector2i, int> autotile_bitmask_flags; - Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; - Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; - Map<Vector2i, int> autotile_priority_map; - Map<Vector2i, int> autotile_z_index_map; + HashMap<Vector2i, int> autotile_bitmask_flags; + HashMap<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; + HashMap<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; + HashMap<Vector2i, int> autotile_priority_map; + HashMap<Vector2i, int> autotile_z_index_map; Vector<CompatibilityShapeData> shapes; Ref<OccluderPolygon2D> occluder; @@ -186,9 +195,9 @@ private: COMPATIBILITY_TILE_MODE_ATLAS_TILE, }; - Map<int, CompatibilityTileData *> compatibility_data; - Map<int, int> compatibility_tilemap_mapping_tile_modes; - Map<int, Map<Array, Array>> compatibility_tilemap_mapping; + HashMap<int, CompatibilityTileData *> compatibility_data; + HashMap<int, int> compatibility_tilemap_mapping_tile_modes; + HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping; void _compatibility_conversion(); @@ -256,6 +265,7 @@ public: class TerrainsPattern { bool valid = false; + int terrain = -1; int bits[TileSet::CELL_NEIGHBOR_MAX]; bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX]; @@ -268,11 +278,14 @@ public: bool operator<(const TerrainsPattern &p_terrains_pattern) const; bool operator==(const TerrainsPattern &p_terrains_pattern) const; - void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain); - int get_terrain(TileSet::CellNeighbor p_peering_bit) const; + void set_terrain(int p_terrain); + int get_terrain() const; + + void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain); + int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const; - void set_terrains_from_array(Array p_terrains); - Array get_terrains_as_array() const; + void from_array(Array p_terrains); + Array as_array() const; TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set); TerrainsPattern() {} @@ -324,10 +337,11 @@ private: }; Vector<TerrainSet> terrain_sets; - Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; + HashMap<TerrainMode, Ref<ArrayMesh>> terrain_meshes; + HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_peering_bits_meshes; bool terrain_bits_meshes_dirty = true; - LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data. + LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data. bool terrains_cache_dirty = true; void _update_terrains_cache(); @@ -343,10 +357,10 @@ private: Variant::Type type = Variant::NIL; }; Vector<CustomDataLayer> custom_data_layers; - Map<String, int> custom_data_layers_by_name; + HashMap<String, int> custom_data_layers_by_name; // Per Atlas source data. - Map<int, Ref<TileSetSource>> sources; + HashMap<int, Ref<TileSetSource>> sources; Vector<int> source_ids; int next_source_id = 0; // --------------------- @@ -357,22 +371,25 @@ private: void _source_changed(); // Tile proxies - Map<int, int> source_level_proxies; - Map<Array, Array> coords_level_proxies; - Map<Array, Array> alternative_level_proxies; + RBMap<int, int> source_level_proxies; + RBMap<Array, Array> coords_level_proxies; + RBMap<Array, Array> alternative_level_proxies; // 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_square_terrain_polygon(Vector2i p_size); + Vector<Point2> _get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_side_terrain_peering_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_isometric_terrain_polygon(Vector2i p_size); + Vector<Point2> _get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_side_terrain_peering_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); + Vector<Point2> _get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit); protected: static void _bind_methods(); @@ -445,8 +462,8 @@ 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; + bool is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const; + bool is_valid_terrain_peering_bit(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation int get_navigation_layers_count() const; @@ -499,15 +516,16 @@ public: int get_patterns_count(); // Terrains. - Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set); - Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); + RBSet<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set); + RBSet<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); // Helpers Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, 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); + Vector<Point2> get_terrain_polygon(int p_terrain_set); + Vector<Point2> get_terrain_peering_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); @@ -551,7 +569,7 @@ public: virtual void add_custom_data_layer(int p_index){}; virtual void move_custom_data_layer(int p_from_index, int p_to_pos){}; virtual void remove_custom_data_layer(int p_index){}; - virtual void reset_state() override{}; + virtual void reset_state() override; // Tiles. virtual int get_tiles_count() const = 0; @@ -579,7 +597,7 @@ private: LocalVector<real_t> animation_frames_durations; // Alternatives - Map<int, TileData *> alternatives; + HashMap<int, TileData *> alternatives; Vector<int> alternatives_ids; int next_alternative_id = 1; }; @@ -589,9 +607,9 @@ private: Vector2i separation; Size2i texture_region_size = Size2i(16, 16); - Map<Vector2i, TileAlternativesData> tiles; + HashMap<Vector2i, TileAlternativesData> tiles; Vector<Vector2i> tiles_ids; - Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile + HashMap<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile); const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const; @@ -716,7 +734,7 @@ private: bool display_placeholder = false; }; Vector<int> scenes_ids; - Map<int, SceneData> scenes; + HashMap<int, SceneData> scenes; int next_scene_id = 1; void _compute_next_alternative_id(); @@ -765,7 +783,7 @@ private: bool flip_v = false; bool transpose = false; Vector2i tex_offset = Vector2i(); - Ref<ShaderMaterial> material = Ref<ShaderMaterial>(); + Ref<Material> material = Ref<Material>(); Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; int y_sort_origin = 0; @@ -789,6 +807,7 @@ private: // Terrain int terrain_set = -1; + int terrain = -1; int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; // Navigation @@ -828,7 +847,6 @@ public: void add_custom_data_layer(int p_index); void move_custom_data_layer(int p_from_index, int p_to_pos); void remove_custom_data_layer(int p_index); - void reset_state(); void set_allow_transform(bool p_allow_transform); bool is_allowing_transform() const; @@ -845,8 +863,8 @@ public: void set_texture_offset(Vector2i p_texture_offset); Vector2i get_texture_offset() const; - void set_material(Ref<ShaderMaterial> p_material); - Ref<ShaderMaterial> get_material() const; + void set_material(Ref<Material> p_material); + Ref<Material> get_material() const; void set_modulate(Color p_modulate); Color get_modulate() const; void set_z_index(int p_z_index); @@ -878,9 +896,11 @@ public: // Terrain void set_terrain_set(int p_terrain_id); int get_terrain_set() const; - void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id); - int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; - bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const; + void set_terrain(int p_terrain_id); + int get_terrain() const; + void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_id); + int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const; + bool is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const; TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed. diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index 3e154d539b..35686b293c 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -72,7 +72,7 @@ class VideoStream : public Resource { public: virtual void set_audio_track(int p_track) = 0; - virtual Ref<VideoStreamPlayback> instance_playback() = 0; + virtual Ref<VideoStreamPlayback> instantiate_playback() = 0; }; -#endif +#endif // VIDEO_STREAM_H diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index dae61c8609..3cf643221b 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -30,6 +30,7 @@ #include "visual_shader.h" +#include "core/templates/rb_map.h" #include "core/templates/vmap.h" #include "servers/rendering/shader_types.h" #include "visual_shader_nodes.h" @@ -74,6 +75,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = pv.x; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = pv.x; + } break; default: break; } @@ -94,6 +99,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = (int)pv.x; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = (int)pv.x; + } break; default: break; } @@ -115,6 +124,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = Vector2(pv.x, pv.y); } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = Vector2(pv.x, pv.y); + } break; default: break; } @@ -136,6 +149,35 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p case Variant::VECTOR3: { value = p_prev_value; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = Vector3(pv.x, pv.y, pv.z); + } break; + default: + break; + } + } break; + case Variant::QUATERNION: { + switch (p_prev_value.get_type()) { + case Variant::INT: { + float pv = (float)(int)p_prev_value; + value = Quaternion(pv, pv, pv, pv); + } break; + case Variant::FLOAT: { + float pv = p_prev_value; + value = Quaternion(pv, pv, pv, pv); + } break; + case Variant::VECTOR2: { + Vector2 pv = p_prev_value; + value = Quaternion(pv.x, pv.y, pv.y, pv.y); + } break; + case Variant::VECTOR3: { + Vector3 pv = p_prev_value; + value = Quaternion(pv.x, pv.y, pv.z, pv.z); + } break; + case Variant::QUATERNION: { + value = p_prev_value; + } break; default: break; } @@ -253,6 +295,9 @@ int VisualShaderNode::get_expanded_output_port_count() const { case PORT_TYPE_VECTOR_3D: { count2 += 3; } break; + case PORT_TYPE_VECTOR_4D: { + count2 += 4; + } break; default: break; } @@ -301,8 +346,8 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const { return Vector<StringName>(); } -Map<StringName, String> VisualShaderNode::get_editable_properties_names() const { - return Map<StringName, String>(); +HashMap<StringName, String> VisualShaderNode::get_editable_properties_names() const { + return HashMap<StringName, String>(); } Array VisualShaderNode::get_default_input_values() const { @@ -360,6 +405,7 @@ void VisualShaderNode::_bind_methods() { BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT); BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(PORT_TYPE_BOOLEAN); BIND_ENUM_CONSTANT(PORT_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(PORT_TYPE_SAMPLER); @@ -455,32 +501,69 @@ 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 = " {\n"; + String _code; GDVIRTUAL_CALL(_get_code, input_vars, output_vars, p_mode, p_type, _code); - bool nend = _code.ends_with("\n"); - _code = _code.insert(0, " "); - _code = _code.replace("\n", "\n "); - code += _code; - if (!nend) { - code += "\n }"; - } else { - code.remove_at(code.size() - 1); - code += "}"; + if (_is_valid_code(_code)) { + String code = " {\n"; + bool nend = _code.ends_with("\n"); + _code = _code.insert(0, " "); + _code = _code.replace("\n", "\n "); + code += _code; + if (!nend) { + code += "\n }"; + } else { + code.remove_at(code.size() - 1); + code += "}"; + } + code += "\n"; + return code; } - code += "\n"; - return code; + return String(); } String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, int p_id) const { - String ret; - if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) { - String code = "// " + get_caption() + "\n"; - code += ret; - code += "\n"; - return code; + String _code; + if (GDVIRTUAL_CALL(_get_global_code, p_mode, _code)) { + if (_is_valid_code(_code)) { + String code = "// " + get_caption() + "\n"; + code += _code; + code += "\n"; + return code; + } } - return ""; + return String(); +} + +String VisualShaderNodeCustom::generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String _code; + if (GDVIRTUAL_CALL(_get_func_code, p_mode, p_type, _code)) { + if (_is_valid_code(_code)) { + bool nend = _code.ends_with("\n"); + String code = "// " + get_caption() + "\n"; + code += " {\n"; + _code = _code.insert(0, " "); + _code = _code.replace("\n", "\n "); + code += _code; + if (!nend) { + code += "\n }"; + } else { + code.remove_at(code.size() - 1); + code += "}"; + } + code += "\n"; + return code; + } + } + return String(); +} + +bool VisualShaderNodeCustom::is_available(Shader::Mode p_mode, VisualShader::Type p_type) const { + bool ret; + if (GDVIRTUAL_CALL(_is_available, p_mode, p_type, ret)) { + return ret; + } + return true; } void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value) { @@ -511,6 +594,13 @@ void VisualShaderNodeCustom::_set_input_port_default_value(int p_port, const Var VisualShaderNode::set_input_port_default_value(p_port, p_value); } +bool VisualShaderNodeCustom::_is_valid_code(const String &p_code) const { + if (p_code.is_empty() || p_code == "null") { + return false; + } + return true; +} + bool VisualShaderNodeCustom::_is_initialized() { return is_initialized; } @@ -531,8 +621,10 @@ void VisualShaderNodeCustom::_bind_methods() { GDVIRTUAL_BIND(_get_output_port_type, "port"); GDVIRTUAL_BIND(_get_output_port_name, "port"); GDVIRTUAL_BIND(_get_code, "input_vars", "output_vars", "mode", "type"); + GDVIRTUAL_BIND(_get_func_code, "mode", "type"); GDVIRTUAL_BIND(_get_global_code, "mode"); GDVIRTUAL_BIND(_is_highend); + GDVIRTUAL_BIND(_is_available, "mode", "type"); ClassDB::bind_method(D_METHOD("_set_initialized", "enabled"), &VisualShaderNodeCustom::_set_initialized); ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized); @@ -555,85 +647,71 @@ VisualShader::Type VisualShader::get_shader_type() const { return current_type; } -void VisualShader::set_engine_version(const Dictionary &p_engine_version) { - ERR_FAIL_COND(!p_engine_version.has("major")); - ERR_FAIL_COND(!p_engine_version.has("minor")); - engine_version["major"] = p_engine_version["major"]; - engine_version["minor"] = p_engine_version["minor"]; +void VisualShader::add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type) { + ERR_FAIL_COND(!p_name.is_valid_identifier()); + ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX); + ERR_FAIL_INDEX((int)p_type, (int)VARYING_TYPE_MAX); + ERR_FAIL_COND(varyings.has(p_name)); + Varying var = Varying(p_name, p_mode, p_type); + varyings[p_name] = var; + varyings_list.push_back(var); + _queue_update(); } -Dictionary VisualShader::get_engine_version() const { - return engine_version; +void VisualShader::remove_varying(const String &p_name) { + ERR_FAIL_COND(!varyings.has(p_name)); + varyings.erase(p_name); + for (List<Varying>::Element *E = varyings_list.front(); E; E = E->next()) { + if (E->get().name == p_name) { + varyings_list.erase(E); + break; + } + } + _queue_update(); } -#ifndef DISABLE_DEPRECATED +bool VisualShader::has_varying(const String &p_name) const { + return varyings.has(p_name); +} -void VisualShader::update_engine_version(const Dictionary &p_new_version) { - if (engine_version.is_empty()) { // before 4.0 - for (int i = 0; i < TYPE_MAX; i++) { - for (KeyValue<int, Node> &E : graph[i].nodes) { - Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E.value.node.ptr()); - if (input.is_valid()) { - if (input->get_input_name() == "side") { - input->set_input_name("front_facing"); - } - } - Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()); - if (expression.is_valid()) { - for (int j = 0; j < expression->get_input_port_count(); j++) { - int type = expression->get_input_port_type(j); - if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D - type += 2; - } - expression->set_input_port_type(j, type); - } - for (int j = 0; j < expression->get_output_port_count(); j++) { - int type = expression->get_output_port_type(j); - if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D - type += 2; - } - expression->set_output_port_type(j, type); - } - } - Ref<VisualShaderNodeStep> step = Object::cast_to<VisualShaderNodeStep>(E.value.node.ptr()); - if (step.is_valid()) { - int op_type = int(step->get_op_type()); - if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR - op_type += 2; - } - step->set_op_type(VisualShaderNodeStep::OpType(op_type)); - } - Ref<VisualShaderNodeSmoothStep> sstep = Object::cast_to<VisualShaderNodeSmoothStep>(E.value.node.ptr()); - if (sstep.is_valid()) { - int op_type = int(sstep->get_op_type()); - if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR - op_type += 2; - } - sstep->set_op_type(VisualShaderNodeSmoothStep::OpType(op_type)); - } - Ref<VisualShaderNodeMix> mix = Object::cast_to<VisualShaderNodeMix>(E.value.node.ptr()); - if (mix.is_valid()) { - int op_type = int(mix->get_op_type()); - if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR - op_type += 2; - } - mix->set_op_type(VisualShaderNodeMix::OpType(op_type)); - } - Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr()); - if (compare.is_valid()) { - int ctype = int(compare->get_comparison_type()); - if (int(ctype) > 0) { // + CTYPE_SCALAR_INT + CTYPE_VECTOR_2D - ctype += 2; - } - compare->set_comparison_type(VisualShaderNodeCompare::ComparisonType(ctype)); - } - } - } +int VisualShader::get_varyings_count() const { + return varyings_list.size(); +} + +const VisualShader::Varying *VisualShader::get_varying_by_index(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, varyings_list.size(), nullptr); + return &varyings_list[p_idx]; +} + +void VisualShader::set_varying_mode(const String &p_name, VaryingMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX); + ERR_FAIL_COND(!varyings.has(p_name)); + if (varyings[p_name].mode == p_mode) { + return; + } + varyings[p_name].mode = p_mode; + _queue_update(); +} + +VisualShader::VaryingMode VisualShader::get_varying_mode(const String &p_name) { + ERR_FAIL_COND_V(!varyings.has(p_name), VARYING_MODE_MAX); + return varyings[p_name].mode; +} + +void VisualShader::set_varying_type(const String &p_name, VaryingType p_type) { + ERR_FAIL_INDEX((int)p_type, (int)VARYING_TYPE_MAX); + ERR_FAIL_COND(!varyings.has(p_name)); + if (varyings[p_name].type == p_type) { + return; } - set_engine_version(p_new_version); + varyings[p_name].type = p_type; + _queue_update(); } -#endif /* DISABLE_DEPRECATED */ +VisualShader::VaryingType VisualShader::get_varying_type(const String &p_name) { + ERR_FAIL_COND_V(!varyings.has(p_name), VARYING_TYPE_MAX); + return varyings[p_name].type; +} void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) { ERR_FAIL_COND(p_node.is_null()); @@ -655,7 +733,6 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co if (input.is_valid()) { input->shader_mode = shader_mode; input->shader_type = p_type; - input->connect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed), varray(p_type, p_id)); } n.node->connect("changed", callable_mp(this, &VisualShader::_queue_update)); @@ -725,11 +802,6 @@ void VisualShader::remove_node(Type p_type, int p_id) { Graph *g = &graph[p_type]; ERR_FAIL_COND(!g->nodes.has(p_id)); - Ref<VisualShaderNodeInput> input = g->nodes[p_id].node; - if (input.is_valid()) { - input->disconnect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed)); - } - g->nodes[p_id].node->disconnect("changed", callable_mp(this, &VisualShader::_queue_update)); g->nodes.erase(p_id); @@ -759,6 +831,48 @@ void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_c return; } VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(p_new_class)); + VisualShaderNode *prev_vsn = g->nodes[p_id].node.ptr(); + + // Update connection data. + for (int i = 0; i < vsn->get_output_port_count(); i++) { + if (i < prev_vsn->get_output_port_count()) { + if (prev_vsn->is_output_port_connected(i)) { + vsn->set_output_port_connected(i, true); + } + + if (prev_vsn->is_output_port_expandable(i) && prev_vsn->_is_output_port_expanded(i) && vsn->is_output_port_expandable(i)) { + vsn->_set_output_port_expanded(i, true); + + int component_count = 0; + switch (prev_vsn->get_output_port_type(i)) { + case VisualShaderNode::PORT_TYPE_VECTOR_2D: + component_count = 2; + break; + case VisualShaderNode::PORT_TYPE_VECTOR_3D: + component_count = 3; + break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + component_count = 4; + break; + default: + break; + } + + for (int j = 0; j < component_count; j++) { + int sub_port = i + 1 + j; + + if (prev_vsn->is_output_port_connected(sub_port)) { + vsn->set_output_port_connected(sub_port, true); + } + } + + i += component_count; + } + } else { + break; + } + } + vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update)); g->nodes[p_id].node = Ref<VisualShaderNode>(vsn); @@ -853,6 +967,12 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from 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()); + for (const Connection &E : g->connections) { + if (E.from_node == p_from_node && E.from_port == p_from_port && E.to_node == p_to_node && E.to_port == p_to_port) { + return; + } + } + Connection c; c.from_node = p_from_node; c.from_port = p_from_port; @@ -1027,16 +1147,16 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port StringBuilder global_code; StringBuilder global_code_per_node; - Map<Type, StringBuilder> global_code_per_func; + HashMap<Type, StringBuilder> global_code_per_func; StringBuilder code; - Set<StringName> classes; + HashSet<StringName> classes; global_code += String() + "shader_type canvas_item;\n"; String global_expressions; for (int i = 0, index = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr()); + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node; if (global_expression.is_valid()) { String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; @@ -1071,8 +1191,8 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port code += "\nvoid fragment() {\n"; - Set<int> processed; - Error err = _write_node(p_type, global_code, global_code_per_node, global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes); + HashSet<int> processed; + Error err = _write_node(p_type, &global_code, &global_code_per_node, &global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes); ERR_FAIL_COND_V(err != OK, String()); switch (node->get_output_port_type(p_port)) { @@ -1091,6 +1211,9 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port case VisualShaderNode::PORT_TYPE_VECTOR_3D: { code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ".xyz;\n"; + } break; default: { code += " COLOR.rgb = vec3(0.0);\n"; } break; @@ -1253,6 +1376,16 @@ bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { } _queue_update(); return true; + } else if (name.begins_with("varyings/")) { + String var_name = name.get_slicec('/', 1); + Varying value = Varying(); + value.name = var_name; + if (value.from_string(p_value) && !varyings.has(var_name)) { + varyings[var_name] = value; + varyings_list.push_back(value); + } + _queue_update(); + return true; } else if (name.begins_with("nodes/")) { String typestr = name.get_slicec('/', 1); Type type = TYPE_VERTEX; @@ -1317,6 +1450,14 @@ bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { r_ret = 0; } return true; + } else if (name.begins_with("varyings/")) { + String var_name = name.get_slicec('/', 1); + if (varyings.has(var_name)) { + r_ret = varyings[var_name].to_string(); + } else { + r_ret = String(); + } + return true; } else if (name.begins_with("nodes/")) { String typestr = name.get_slicec('/', 1); Type type = TYPE_VERTEX; @@ -1375,11 +1516,11 @@ void VisualShader::reset_state() { } void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { //mode - p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); //render modes - Map<String, String> blend_mode_enums; - Set<String> toggles; + HashMap<String, String> blend_mode_enums; + HashSet<String> toggles; const Vector<ShaderLanguage::ModeInfo> &rmodes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)); @@ -1390,7 +1531,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { const String begin = String(info.name); for (int j = 0; j < info.options.size(); j++) { - const String option = String(info.options[j]); + const String option = String(info.options[j]).capitalize(); if (!blend_mode_enums.has(begin)) { blend_mode_enums[begin] = option; @@ -1404,11 +1545,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } for (const KeyValue<String, String> &E : blend_mode_enums) { - p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("modes"), E.key), PROPERTY_HINT_ENUM, E.value)); } - for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get())); + for (const String &E : toggles) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E))); + } + + for (const KeyValue<String, Varying> &E : varyings) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key))); } for (int i = 0; i < TYPE_MAX; i++) { @@ -1435,7 +1580,7 @@ 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 { +Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<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, HashSet<int> &processed, bool for_preview, HashSet<StringName> &r_classes) const { const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node; if (vsnode->is_disabled()) { @@ -1477,7 +1622,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui if (!skip_global) { Ref<VisualShaderNodeUniform> uniform = vsnode; if (!uniform.is_valid() || !uniform->is_global_code_generated()) { - global_code += vsnode->generate_global(get_mode(), type, node); + if (global_code) { + *global_code += vsnode->generate_global(get_mode(), type, node); + } } String class_name = vsnode->get_class_name(); @@ -1485,9 +1632,13 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui class_name = vsnode->get_script_instance()->get_script()->get_path(); } if (!r_classes.has(class_name)) { - global_code_per_node += vsnode->generate_global_per_node(get_mode(), node); + if (global_code_per_node) { + *global_code_per_node += vsnode->generate_global_per_node(get_mode(), node); + } for (int i = 0; i < TYPE_MAX; i++) { - global_code_per_func[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node); + if (global_code_per_func) { + (*global_code_per_func)[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node); + } } r_classes.insert(class_name); } @@ -1547,11 +1698,14 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui inputs[i] = "(" + src_var + " ? 1.0 : 0.0)"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_2D: { - inputs[i] = "dot(" + src_var + ", vec2(0.333333, 0.333333))"; + inputs[i] = "dot(" + src_var + ", vec2(0.5, 0.5))"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "dot(" + src_var + ", vec4(0.25, 0.25, 0.25, 0.25))"; + } break; default: break; } @@ -1565,11 +1719,14 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui inputs[i] = "(" + src_var + " ? 1 : 0)"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_2D: { - inputs[i] = "dot(float(" + src_var + "), vec2(0.333333, 0.333333))"; + inputs[i] = "dot(float(" + src_var + "), vec2(0.5, 0.5))"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "dot(float(" + src_var + "), vec4(0.25, 0.25, 0.25, 0.25))"; + } break; default: break; } @@ -1588,6 +1745,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "all(bvec3(" + src_var + "))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "all(bvec4(" + src_var + "))"; + } break; default: break; } @@ -1603,7 +1763,8 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_BOOLEAN: { inputs[i] = "vec2(" + src_var + " ? 1.0 : 0.0)"; } break; - case VisualShaderNode::PORT_TYPE_VECTOR_3D: { + case VisualShaderNode::PORT_TYPE_VECTOR_3D: + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { inputs[i] = "vec2(" + src_var + ".xy)"; } break; default: @@ -1625,6 +1786,30 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_2D: { inputs[i] = "vec3(" + src_var + ", 0.0)"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "vec3(" + src_var + ".xyz)"; + } break; + default: + break; + } + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + switch (out_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: { + inputs[i] = "vec4(" + src_var + ")"; + } break; + case VisualShaderNode::PORT_TYPE_SCALAR_INT: { + inputs[i] = "vec4(float(" + src_var + "))"; + } break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: { + inputs[i] = "vec4(" + src_var + " ? 1.0 : 0.0)"; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_2D: { + inputs[i] = "vec4(" + src_var + ", 0.0, 0.0)"; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_3D: { + inputs[i] = "vec4(" + src_var + ", 0.0)"; + } break; default: break; } @@ -1659,6 +1844,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui Vector3 val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::QUATERNION) { + Quaternion val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + node_code += " vec4 " + inputs[i] + " = " + vformat("vec4(%.5f, %.5f, %.5f, %.5f);\n", val.x, val.y, val.z, val.w); } else if (defval.get_type() == Variant::TRANSFORM3D) { Transform3D val = defval; val.basis.transpose(); @@ -1683,7 +1872,7 @@ 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; + HashMap<int, bool> expanded_output_ports; for (int i = 0; i < initial_output_count; i++) { bool expanded = false; @@ -1698,6 +1887,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { output_count += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + output_count += 4; + } break; default: break; } @@ -1725,6 +1917,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: outputs[i] = "vec3 " + var_name; break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + outputs[i] = "vec4 " + var_name; + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: outputs[i] = "bool " + var_name; break; @@ -1742,6 +1937,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { j += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + j += 4; + } break; default: break; } @@ -1764,6 +1962,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: code += " vec3 " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + code += " vec4 " + outputs[i] + ";\n"; + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: code += " bool " + outputs[i] + ";\n"; break; @@ -1781,6 +1982,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { j += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + j += 4; + } break; default: break; } @@ -1792,29 +1996,19 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui if (!node_code.is_empty()) { 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]) { switch (vsnode->get_output_port_type(i)) { case VisualShaderNode::PORT_TYPE_VECTOR_2D: { 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; @@ -1824,30 +2018,18 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { 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; @@ -1855,12 +2037,43 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui i += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component + 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 + 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 + 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; + } + + if (vsnode->is_output_port_connected(i + 4) || (for_preview && vsnode->get_output_port_for_preview() == (i + 4))) { // alpha-component + String a = "n_out" + itos(node) + "p" + itos(i + 4); + code += " float " + a + " = n_out" + itos(node) + "p" + itos(i) + ".a;\n"; + outputs[i + 4] = a; + } + + i += 4; + } break; default: break; } } } + if (!node_code.is_empty()) { + code += "\n"; + } + code += "\n"; // processed.insert(node); @@ -1889,11 +2102,11 @@ void VisualShader::_update_shader() const { StringBuilder global_code; StringBuilder global_code_per_node; - Map<Type, StringBuilder> global_code_per_func; + HashMap<Type, StringBuilder> global_code_per_func; StringBuilder code; Vector<VisualShader::DefaultTextureParam> default_tex_params; - Set<StringName> classes; - Map<int, int> insertion_pos; + HashSet<StringName> classes; + HashMap<int, int> insertion_pos; static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" }; global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n"; @@ -1937,17 +2150,18 @@ void VisualShader::_update_shader() const { static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; - Set<String> used_uniform_names; + HashSet<String> used_uniform_names; List<VisualShaderNodeUniform *> uniforms; - Map<int, List<int>> emitters; + HashMap<int, List<int>> emitters; + HashMap<int, List<int>> varying_setters; for (int i = 0, index = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { continue; } - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr()); + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node; if (global_expression.is_valid()) { String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; @@ -1956,26 +2170,27 @@ void VisualShader::_update_shader() const { expr += "\n"; global_expressions += expr; } - Ref<VisualShaderNodeUniformRef> uniform_ref = Object::cast_to<VisualShaderNodeUniformRef>(E->get().node.ptr()); + Ref<VisualShaderNodeUniformRef> uniform_ref = E.value.node; if (uniform_ref.is_valid()) { used_uniform_names.insert(uniform_ref->get_uniform_name()); } - Ref<VisualShaderNodeUniform> uniform = Object::cast_to<VisualShaderNodeUniform>(E->get().node.ptr()); + Ref<VisualShaderNodeUniform> uniform = E.value.node; if (uniform.is_valid()) { uniforms.push_back(uniform.ptr()); } - Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr()); + Ref<VisualShaderNodeVaryingSetter> varying_setter = E.value.node; + if (varying_setter.is_valid() && varying_setter->is_input_port_connected(0)) { + if (!varying_setters.has(i)) { + varying_setters.insert(i, List<int>()); + } + varying_setters[i].push_back(E.key); + } + Ref<VisualShaderNodeParticleEmit> emit_particle = E.value.node; if (emit_particle.is_valid()) { if (!emitters.has(i)) { emitters.insert(i, List<int>()); } - - for (const KeyValue<int, Node> &M : graph[i].nodes) { - if (M.value.node == emit_particle.ptr()) { - emitters[i].push_back(M.key); - break; - } - } + emitters[i].push_back(E.key); } } } @@ -1990,8 +2205,41 @@ void VisualShader::_update_shader() const { } } - Map<int, String> code_map; - Set<int> empty_funcs; + if (!varyings.is_empty()) { + global_code += "\n// Varyings\n"; + + for (const KeyValue<String, Varying> &E : varyings) { + global_code += "varying "; + switch (E.value.type) { + case VaryingType::VARYING_TYPE_FLOAT: + global_code += "float "; + break; + case VaryingType::VARYING_TYPE_VECTOR_2D: + global_code += "vec2 "; + break; + case VaryingType::VARYING_TYPE_VECTOR_3D: + global_code += "vec3 "; + break; + case VaryingType::VARYING_TYPE_VECTOR_4D: + global_code += "vec4 "; + break; + case VaryingType::VARYING_TYPE_COLOR: + global_code += "vec4 "; + break; + case VaryingType::VARYING_TYPE_TRANSFORM: + global_code += "mat4 "; + break; + default: + break; + } + global_code += E.key + ";\n"; + } + + global_code += "\n"; + } + + HashMap<int, String> code_map; + HashSet<int> empty_funcs; for (int i = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { @@ -2003,12 +2251,57 @@ void VisualShader::_update_shader() const { VMap<ConnectionKey, const List<Connection>::Element *> output_connections; StringBuilder func_code; + HashSet<int> processed; bool is_empty_func = false; if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) { is_empty_func = true; } + String varying_code; + if (shader_mode == Shader::MODE_SPATIAL || shader_mode == Shader::MODE_CANVAS_ITEM) { + for (const KeyValue<String, Varying> &E : varyings) { + if ((E.value.mode == VARYING_MODE_VERTEX_TO_FRAG_LIGHT && i == TYPE_VERTEX) || (E.value.mode == VARYING_MODE_FRAG_TO_LIGHT && i == TYPE_FRAGMENT)) { + bool found = false; + for (int key : varying_setters[i]) { + Ref<VisualShaderNodeVaryingSetter> setter = Object::cast_to<VisualShaderNodeVaryingSetter>(const_cast<VisualShaderNode *>(graph[i].nodes[key].node.ptr())); + if (setter.is_valid() && E.value.name == setter->get_varying_name()) { + found = true; + break; + } + } + + if (!found) { + String code2; + switch (E.value.type) { + case VaryingType::VARYING_TYPE_FLOAT: + code2 += "0.0"; + break; + case VaryingType::VARYING_TYPE_VECTOR_2D: + code2 += "vec2(0.0)"; + break; + case VaryingType::VARYING_TYPE_VECTOR_3D: + code2 += "vec3(0.0)"; + break; + case VaryingType::VARYING_TYPE_VECTOR_4D: + code2 += "vec4(0.0)"; + break; + case VaryingType::VARYING_TYPE_COLOR: + code2 += "vec4(0.0)"; + break; + case VaryingType::VARYING_TYPE_TRANSFORM: + code2 += "mat4(1.0)"; + break; + default: + break; + } + varying_code += vformat(" %s = %s;\n", E.key, code2); + } + is_empty_func = false; + } + } + } + for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) { ConnectionKey from_key; from_key.node = E->get().from_node; @@ -2037,13 +2330,19 @@ void VisualShader::_update_shader() const { } insertion_pos.insert(i, code.get_string_length() + func_code.get_string_length()); - Set<int> processed; - 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); + 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 (varying_setters.has(i)) { + for (int &E : varying_setters[i]) { + err = _write_node(Type(i), nullptr, nullptr, nullptr, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes); + ERR_FAIL_COND(err != OK); + } + } + if (emitters.has(i)) { for (int &E : emitters[i]) { - 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, processed, false, classes); + 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, processed, false, classes); ERR_FAIL_COND(err != OK); } } @@ -2051,6 +2350,7 @@ void VisualShader::_update_shader() const { if (shader_mode == Shader::MODE_PARTICLES) { code_map.insert(i, func_code); } else { + func_code += varying_code; func_code += "}\n"; code += func_code; } @@ -2226,21 +2526,6 @@ void VisualShader::_queue_update() { call_deferred(SNAME("_update_shader")); } -void VisualShader::_input_type_changed(Type p_type, int p_id) { - ERR_FAIL_INDEX(p_type, TYPE_MAX); - //erase connections using this input, as type changed - Graph *g = &graph[p_type]; - - for (List<Connection>::Element *E = g->connections.front(); E;) { - List<Connection>::Element *N = E->next(); - if (E->get().from_node == p_id) { - g->connections.erase(E); - g->nodes[E->get().to_node].prev_connected_nodes.erase(p_id); - } - E = N; - } -} - void VisualShader::rebuild() { dirty.set(); _update_shader(); @@ -2270,16 +2555,16 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); - ClassDB::bind_method(D_METHOD("set_engine_version", "version"), &VisualShader::set_engine_version); - ClassDB::bind_method(D_METHOD("get_engine_version"), &VisualShader::get_engine_version); - ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset); + ClassDB::bind_method(D_METHOD("add_varying", "name", "mode", "type"), &VisualShader::add_varying); + ClassDB::bind_method(D_METHOD("remove_varying", "name"), &VisualShader::remove_varying); + ClassDB::bind_method(D_METHOD("has_varying", "name"), &VisualShader::has_varying); + ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version"); ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs. @@ -2295,6 +2580,18 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_FOG); BIND_ENUM_CONSTANT(TYPE_MAX); + BIND_ENUM_CONSTANT(VARYING_MODE_VERTEX_TO_FRAG_LIGHT); + BIND_ENUM_CONSTANT(VARYING_MODE_FRAG_TO_LIGHT); + BIND_ENUM_CONSTANT(VARYING_MODE_MAX); + + BIND_ENUM_CONSTANT(VARYING_TYPE_FLOAT); + BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_2D); + BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(VARYING_TYPE_COLOR); + BIND_ENUM_CONSTANT(VARYING_TYPE_TRANSFORM); + BIND_ENUM_CONSTANT(VARYING_TYPE_MAX); + BIND_CONSTANT(NODE_ID_INVALID); BIND_CONSTANT(NODE_ID_OUTPUT); } @@ -2327,30 +2624,32 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Node3D, Vertex { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview_matrix", "MODELVIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, // Node3D, Fragment - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" }, @@ -2358,15 +2657,14 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, @@ -2374,9 +2672,12 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_roughness_texture", "NORMAL_ROUGHNESS_TEXTURE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_index", "VIEW_INDEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_mono_left", "VIEW_MONO_LEFT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, // Node3D, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, @@ -2390,11 +2691,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, @@ -2404,23 +2705,22 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas_matrix", "CANVAS_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen_matrix", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" }, // Canvas Item, Fragment - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_pixel_size", "SCREEN_PIXEL_SIZE" }, @@ -2430,42 +2730,34 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, // Canvas Item, Light - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light", "LIGHT" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light_color", "LIGHT_COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_position", "LIGHT_POSITION" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "shadow", "SHADOW_MODULATE.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "shadow", "SHADOW_MODULATE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" }, // Particles, Start { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_3D, "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_VECTOR_4D, "custom", "CUSTOM" }, { 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" }, @@ -2475,13 +2767,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Start (Custom) { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_3D, "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_VECTOR_4D, "custom", "CUSTOM" }, { 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" }, @@ -2491,13 +2781,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Process { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { 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_3D, "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_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2507,13 +2795,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Process (Custom) { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_3D, "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_VECTOR_4D, "custom", "CUSTOM" }, { 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" }, @@ -2525,13 +2811,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_3D, "collision_normal", "COLLISION_NORMAL" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_3D, "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_VECTOR_4D, "custom", "CUSTOM" }, { 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" }, @@ -2544,8 +2828,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "eyedir", "EYEDIR" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "half_res_color", "HALF_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "half_res_color", "HALF_RES_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_color", "LIGHT0_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_direction", "LIGHT0_DIRECTION" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, @@ -2563,10 +2846,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "position", "POSITION" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "quarter_res_color", "QUARTER_RES_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "sky_coords", "SKY_COORDS" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, @@ -2590,20 +2873,20 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Spatial, Fragment - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" }, @@ -2611,7 +2894,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { // Spatial, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, @@ -2622,25 +2905,25 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Canvas Item, Fragment - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Canvas Item, Light - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, @@ -2682,7 +2965,7 @@ int VisualShaderNodeInput::get_output_port_count() const { } VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const { - return get_input_type_by_name(input_name); + return p_port == 0 ? get_input_type_by_name(input_name) : PORT_TYPE_SCALAR; } String VisualShaderNodeInput::get_output_port_name(int p_port) const { @@ -2693,6 +2976,22 @@ String VisualShaderNodeInput::get_caption() const { return "Input"; } +bool VisualShaderNodeInput::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + switch (get_input_type_by_name(input_name)) { + case PORT_TYPE_VECTOR_2D: + return true; + case PORT_TYPE_VECTOR_3D: + return true; + case PORT_TYPE_VECTOR_4D: + return true; + default: + return false; + } + } + return false; +} + String VisualShaderNodeInput::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 (get_output_port_type(0) == PORT_TYPE_SAMPLER) { return ""; @@ -2725,6 +3024,9 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T case PORT_TYPE_VECTOR_3D: { code = " " + p_output_vars[0] + " = vec3(0.0);\n"; } break; + case PORT_TYPE_VECTOR_4D: { + code = " " + p_output_vars[0] + " = vec4(0.0);\n"; + } break; case PORT_TYPE_BOOLEAN: { code = " " + p_output_vars[0] + " = false;\n"; } break; @@ -2860,7 +3162,7 @@ void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const { } if (port_list.is_empty()) { - port_list = TTR("None"); + port_list = RTR("None"); } property.hint_string = port_list; } @@ -2894,18 +3196,18 @@ VisualShaderNodeInput::VisualShaderNodeInput() { ////////////// UniformRef -List<VisualShaderNodeUniformRef::Uniform> uniforms; +RBMap<RID, List<VisualShaderNodeUniformRef::Uniform>> uniforms; -void VisualShaderNodeUniformRef::add_uniform(const String &p_name, UniformType p_type) { - uniforms.push_back({ p_name, p_type }); +void VisualShaderNodeUniformRef::add_uniform(RID p_shader_rid, const String &p_name, UniformType p_type) { + uniforms[p_shader_rid].push_back({ p_name, p_type }); } -void VisualShaderNodeUniformRef::clear_uniforms() { - uniforms.clear(); +void VisualShaderNodeUniformRef::clear_uniforms(RID p_shader_rid) { + uniforms[p_shader_rid].clear(); } -bool VisualShaderNodeUniformRef::has_uniform(const String &p_name) { - for (const VisualShaderNodeUniformRef::Uniform &E : uniforms) { +bool VisualShaderNodeUniformRef::has_uniform(RID p_shader_rid, const String &p_name) { + for (const VisualShaderNodeUniformRef::Uniform &E : uniforms[p_shader_rid]) { if (E.name == p_name) { return true; } @@ -2941,6 +3243,8 @@ int VisualShaderNodeUniformRef::get_output_port_count() const { return 1; case UniformType::UNIFORM_TYPE_VECTOR3: return 1; + case UniformType::UNIFORM_TYPE_VECTOR4: + return 1; case UniformType::UNIFORM_TYPE_TRANSFORM: return 1; case UniformType::UNIFORM_TYPE_COLOR: @@ -2965,6 +3269,8 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port return PortType::PORT_TYPE_VECTOR_2D; case UniformType::UNIFORM_TYPE_VECTOR3: return PortType::PORT_TYPE_VECTOR_3D; + case UniformType::UNIFORM_TYPE_VECTOR4: + return PortType::PORT_TYPE_VECTOR_4D; case UniformType::UNIFORM_TYPE_TRANSFORM: return PortType::PORT_TYPE_TRANSFORM; case UniformType::UNIFORM_TYPE_COLOR: @@ -2994,6 +3300,8 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { return ""; case UniformType::UNIFORM_TYPE_VECTOR3: return ""; + case UniformType::UNIFORM_TYPE_VECTOR4: + return ""; case UniformType::UNIFORM_TYPE_TRANSFORM: return ""; case UniformType::UNIFORM_TYPE_COLOR: @@ -3012,14 +3320,24 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { return ""; } +void VisualShaderNodeUniformRef::set_shader_rid(const RID &p_shader_rid) { + shader_rid = p_shader_rid; +} + void VisualShaderNodeUniformRef::set_uniform_name(const String &p_name) { uniform_name = p_name; + if (shader_rid.is_valid()) { + update_uniform_type(); + } + emit_changed(); +} + +void VisualShaderNodeUniformRef::update_uniform_type() { if (uniform_name != "[None]") { uniform_type = get_uniform_type_by_name(uniform_name); } else { uniform_type = UniformType::UNIFORM_TYPE_FLOAT; } - emit_changed(); } String VisualShaderNodeUniformRef::get_uniform_name() const { @@ -3027,35 +3345,45 @@ String VisualShaderNodeUniformRef::get_uniform_name() const { } int VisualShaderNodeUniformRef::get_uniforms_count() const { - return uniforms.size(); + ERR_FAIL_COND_V(!shader_rid.is_valid(), 0); + + return uniforms[shader_rid].size(); } String VisualShaderNodeUniformRef::get_uniform_name_by_index(int p_idx) const { - if (p_idx >= 0 && p_idx < uniforms.size()) { - return uniforms[p_idx].name; + ERR_FAIL_COND_V(!shader_rid.is_valid(), String()); + + if (p_idx >= 0 && p_idx < uniforms[shader_rid].size()) { + return uniforms[shader_rid][p_idx].name; } return ""; } VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_name(const String &p_name) const { - for (int i = 0; i < uniforms.size(); i++) { - if (uniforms[i].name == p_name) { - return uniforms[i].type; + ERR_FAIL_COND_V(!shader_rid.is_valid(), UNIFORM_TYPE_FLOAT); + + for (int i = 0; i < uniforms[shader_rid].size(); i++) { + if (uniforms[shader_rid][i].name == p_name) { + return uniforms[shader_rid][i].type; } } return UniformType::UNIFORM_TYPE_FLOAT; } VisualShaderNodeUniformRef::UniformType VisualShaderNodeUniformRef::get_uniform_type_by_index(int p_idx) const { - if (p_idx >= 0 && p_idx < uniforms.size()) { - return uniforms[p_idx].type; + ERR_FAIL_COND_V(!shader_rid.is_valid(), UNIFORM_TYPE_FLOAT); + + if (p_idx >= 0 && p_idx < uniforms[shader_rid].size()) { + return uniforms[shader_rid][p_idx].type; } 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) { + ERR_FAIL_COND_V(!shader_rid.is_valid(), PORT_TYPE_SCALAR); + + if (p_idx >= 0 && p_idx < uniforms[shader_rid].size()) { + switch (uniforms[shader_rid][p_idx].type) { case UniformType::UNIFORM_TYPE_FLOAT: return PORT_TYPE_SCALAR; case UniformType::UNIFORM_TYPE_INT: @@ -3066,6 +3394,8 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b return PORT_TYPE_VECTOR_2D; case UniformType::UNIFORM_TYPE_VECTOR3: return PORT_TYPE_VECTOR_3D; + case UniformType::UNIFORM_TYPE_VECTOR4: + return PORT_TYPE_VECTOR_4D; case UniformType::UNIFORM_TYPE_TRANSFORM: return PORT_TYPE_TRANSFORM; case UniformType::UNIFORM_TYPE_COLOR: @@ -3135,89 +3465,94 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { //////////////////////////////////////////////////////////////////////// // Node3D, Vertex. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV2", "UV2" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "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_SCALAR, "Point Size", "POINT_SIZE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" }, //////////////////////////////////////////////////////////////////////// // Node3D, Fragment. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Metallic", "METALLIC" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Specular", "SPECULAR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO", "AO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO Light Affect", "AO_LIGHT_AFFECT" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat Roughness", "CLEARCOAT_ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Anisotropy", "ANISOTROPY" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Anisotropy Flow", "ANISOTROPY_FLOW" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Subsurf Scatter", "SSS_STRENGTH" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Backlight", "BACKLIGHT" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Scissor Threshold", "ALPHA_SCISSOR_THRESHOLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Hash Scale", "ALPHA_HASH_SCALE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha AA Edge", "ALPHA_ANTIALIASING_EDGE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Alpha UV", "ALPHA_TEXTURE_COORDINATE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, //////////////////////////////////////////////////////////////////////// // Node3D, Light. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, //////////////////////////////////////////////////////////////////////// // Canvas Item. //////////////////////////////////////////////////////////////////////// // Canvas Item, Vertex. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Vertex", "VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" }, //////////////////////////////////////////////////////////////////////// // Canvas Item, Fragment. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "shadow_vertex", "SHADOW_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light Vertex", "LIGHT_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Shadow Vertex", "SHADOW_VERTEX" }, //////////////////////////////////////////////////////////////////////// // Canvas Item, Light. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light", "LIGHT.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Light Alpha", "LIGHT.a" }, //////////////////////////////////////////////////////////////////////// // Sky, Sky. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fog", "FOG.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Fog", "FOG.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "Fog Alpha", "FOG.a" }, //////////////////////////////////////////////////////////////////////// // Fog, Fog. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" }, - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" }, - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "Density", "DENSITY" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" }, //////////////////////////////////////////////////////////////////////// { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, @@ -3261,7 +3596,7 @@ String VisualShaderNodeOutput::get_input_port_name(int p_port) const { while (ports[idx].mode != Shader::MODE_MAX) { if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { if (count == p_port) { - return String(ports[idx].name).capitalize(); + return String(ports[idx].name); } count++; } @@ -3294,7 +3629,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const { } if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { String name = get_input_port_name(p_index); - return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold"); + return bool(name == "AO" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold"); } return false; } @@ -3398,7 +3733,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T List<String> keyword_list; ShaderLanguage::get_keyword_list(&keyword_list); if (keyword_list.find(uniform_name)) { - return TTR("Shader keywords cannot be used as uniform names.\nChoose another name."); + return RTR("Shader keywords cannot be used as uniform names.\nChoose another name."); } if (!is_qualifier_supported(qualifier)) { String qualifier_str; @@ -3414,11 +3749,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T default: break; } - return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str); + return vformat(RTR("This uniform type does not support the '%s' qualifier."), qualifier_str); } else if (qualifier == Qualifier::QUAL_GLOBAL) { RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name); if (gvt == RS::GLOBAL_VAR_TYPE_MAX) { - return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name); + return vformat(RTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name); } bool incompatible_type = false; switch (gvt) { @@ -3447,6 +3782,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T incompatible_type = true; } } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + if (!Object::cast_to<VisualShaderNodeVec4Uniform>(this)) { + incompatible_type = true; + } + } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) { incompatible_type = true; @@ -3476,7 +3816,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T break; } if (incompatible_type) { - return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name); + return vformat(RTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name); } } @@ -4154,6 +4494,9 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad case PORT_TYPE_VECTOR_3D: tk = "vec3(0.0, 0.0, 0.0)"; break; + case PORT_TYPE_VECTOR_4D: + tk = "vec4(0.0, 0.0, 0.0, 0.0)"; + break; case PORT_TYPE_BOOLEAN: tk = "false"; break; @@ -4199,3 +4542,306 @@ String VisualShaderNodeGlobalExpression::generate_global(Shader::Mode p_mode, Vi VisualShaderNodeGlobalExpression::VisualShaderNodeGlobalExpression() { set_editable(false); } + +////////////// Varying + +List<VisualShaderNodeVarying::Varying> varyings; + +void VisualShaderNodeVarying::add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type) { // static + varyings.push_back({ p_name, p_mode, p_type }); +} + +void VisualShaderNodeVarying::clear_varyings() { // static + varyings.clear(); +} + +bool VisualShaderNodeVarying::has_varying(const String &p_name) { // static + for (const VisualShaderNodeVarying::Varying &E : varyings) { + if (E.name == p_name) { + return true; + } + } + return false; +} + +int VisualShaderNodeVarying::get_varyings_count() const { + return varyings.size(); +} + +String VisualShaderNodeVarying::get_varying_name_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < varyings.size()) { + return varyings[p_idx].name; + } + return ""; +} + +VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type_by_name(const String &p_name) const { + for (int i = 0; i < varyings.size(); i++) { + if (varyings[i].name == p_name) { + return varyings[i].type; + } + } + return VisualShader::VARYING_TYPE_FLOAT; +} + +VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < varyings.size()) { + return varyings[p_idx].type; + } + return VisualShader::VARYING_TYPE_FLOAT; +} + +VisualShader::VaryingMode VisualShaderNodeVarying::get_varying_mode_by_name(const String &p_name) const { + for (int i = 0; i < varyings.size(); i++) { + if (varyings[i].name == p_name) { + return varyings[i].mode; + } + } + return VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT; +} + +VisualShader::VaryingMode VisualShaderNodeVarying::get_varying_mode_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < varyings.size()) { + return varyings[p_idx].mode; + } + return VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT; +} + +VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type_by_index(int p_idx) const { + if (p_idx >= 0 && p_idx < varyings.size()) { + return get_port_type(varyings[p_idx].type, 0); + } + return PORT_TYPE_SCALAR; +} + +////////////// + +void VisualShaderNodeVarying::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_varying_name", "name"), &VisualShaderNodeVarying::set_varying_name); + ClassDB::bind_method(D_METHOD("get_varying_name"), &VisualShaderNodeVarying::get_varying_name); + + ClassDB::bind_method(D_METHOD("set_varying_type", "type"), &VisualShaderNodeVarying::set_varying_type); + ClassDB::bind_method(D_METHOD("get_varying_type"), &VisualShaderNodeVarying::get_varying_type); + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "varying_name"), "set_varying_name", "get_varying_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "varying_type", PROPERTY_HINT_ENUM, "Float,Vector,Transform"), "set_varying_type", "get_varying_type"); +} + +String VisualShaderNodeVarying::get_type_str() const { + switch (varying_type) { + case VisualShader::VARYING_TYPE_FLOAT: + return "float"; + case VisualShader::VARYING_TYPE_VECTOR_2D: + return "vec2"; + case VisualShader::VARYING_TYPE_VECTOR_3D: + return "vec3"; + case VisualShader::VARYING_TYPE_VECTOR_4D: + return "vec4"; + case VisualShader::VARYING_TYPE_COLOR: + return "vec4"; + case VisualShader::VARYING_TYPE_TRANSFORM: + return "mat4"; + default: + break; + } + return ""; +} + +VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type(VisualShader::VaryingType p_type, int p_port) const { + switch (p_type) { + case VisualShader::VARYING_TYPE_VECTOR_2D: + return PORT_TYPE_VECTOR_2D; + case VisualShader::VARYING_TYPE_VECTOR_3D: + return PORT_TYPE_VECTOR_3D; + case VisualShader::VARYING_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case VisualShader::VARYING_TYPE_COLOR: + if (p_port == 1) { + break; // scalar + } + return PORT_TYPE_VECTOR_3D; + case VisualShader::VARYING_TYPE_TRANSFORM: + return PORT_TYPE_TRANSFORM; + default: + break; + } + return PORT_TYPE_SCALAR; +} + +void VisualShaderNodeVarying::set_varying_name(String p_varying_name) { + if (varying_name == p_varying_name) { + return; + } + varying_name = p_varying_name; + emit_changed(); +} + +String VisualShaderNodeVarying::get_varying_name() const { + return varying_name; +} + +void VisualShaderNodeVarying::set_varying_type(VisualShader::VaryingType p_varying_type) { + ERR_FAIL_INDEX(p_varying_type, VisualShader::VARYING_TYPE_MAX); + if (varying_type == p_varying_type) { + return; + } + varying_type = p_varying_type; + emit_changed(); +} + +VisualShader::VaryingType VisualShaderNodeVarying::get_varying_type() const { + return varying_type; +} + +VisualShaderNodeVarying::VisualShaderNodeVarying() { +} + +////////////// Varying Setter + +String VisualShaderNodeVaryingSetter::get_caption() const { + return vformat("VaryingSetter"); +} + +int VisualShaderNodeVaryingSetter::get_input_port_count() const { + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + return 2; + } + return 1; +} + +VisualShaderNodeVaryingSetter::PortType VisualShaderNodeVaryingSetter::get_input_port_type(int p_port) const { + return get_port_type(varying_type, p_port); +} + +String VisualShaderNodeVaryingSetter::get_input_port_name(int p_port) const { + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + if (p_port == 0) { + return "color"; + } else { + return "alpha"; + } + } + return ""; +} + +int VisualShaderNodeVaryingSetter::get_output_port_count() const { + return 0; +} + +VisualShaderNodeVaryingSetter::PortType VisualShaderNodeVaryingSetter::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeVaryingSetter::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeVaryingSetter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return vformat("varying %s %s;\n", get_type_str(), varying_name); +} + +String VisualShaderNodeVaryingSetter::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 (varying_name == "[None]") { + return code; + } + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + code += vformat(" %s = vec4(%s, %s);\n", varying_name, p_input_vars[0], p_input_vars[1]); + } else { + code += vformat(" %s = %s;\n", varying_name, p_input_vars[0]); + } + return code; +} + +VisualShaderNodeVaryingSetter::VisualShaderNodeVaryingSetter() { +} + +////////////// Varying Getter + +String VisualShaderNodeVaryingGetter::get_caption() const { + return vformat("VaryingGetter"); +} + +int VisualShaderNodeVaryingGetter::get_input_port_count() const { + return 0; +} + +VisualShaderNodeVaryingGetter::PortType VisualShaderNodeVaryingGetter::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeVaryingGetter::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVaryingGetter::get_output_port_count() const { + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + return 2; + } + return 1; +} + +VisualShaderNodeVaryingGetter::PortType VisualShaderNodeVaryingGetter::get_output_port_type(int p_port) const { + return get_port_type(varying_type, p_port); +} + +String VisualShaderNodeVaryingGetter::get_output_port_name(int p_port) const { + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + if (p_port == 0) { + return "color"; + } else { + return "alpha"; + } + } + return ""; +} + +bool VisualShaderNodeVaryingGetter::has_output_port_preview(int p_port) const { + return false; +} + +String VisualShaderNodeVaryingGetter::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 from = varying_name; + String from2; + + if (varying_name == "[None]") { + switch (varying_type) { + case VisualShader::VARYING_TYPE_FLOAT: + from = "0.0"; + break; + case VisualShader::VARYING_TYPE_VECTOR_2D: + from = "vec2(0.0)"; + break; + case VisualShader::VARYING_TYPE_VECTOR_3D: + from = "vec3(0.0)"; + break; + case VisualShader::VARYING_TYPE_VECTOR_4D: + from = "vec4(0.0)"; + break; + case VisualShader::VARYING_TYPE_COLOR: + from = "vec3(0.0)"; + from2 = "0.0"; + break; + case VisualShader::VARYING_TYPE_TRANSFORM: + from = "mat4(1.0)"; + break; + default: + break; + } + } else if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + from = varying_name + ".rgb"; + from2 = varying_name + ".a"; + } + + if (varying_type == VisualShader::VARYING_TYPE_COLOR) { + String code; + code += vformat(" %s = %s;\n", p_output_vars[0], from); + code += vformat(" %s = %s;\n", p_output_vars[1], from2); + return code; + } + return vformat(" %s = %s;\n", p_output_vars[0], from); +} + +VisualShaderNodeVaryingGetter::VisualShaderNodeVaryingGetter() { + varying_name = "[None]"; +} diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index d3b5365893..7ca4e5fc4a 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -44,8 +44,6 @@ class VisualShader : public Shader { friend class VisualShaderNodeVersionChecker; - Dictionary engine_version; - public: enum Type { TYPE_VERTEX, @@ -73,6 +71,50 @@ public: List<Ref<Texture2D>> params; }; + enum VaryingMode { + VARYING_MODE_VERTEX_TO_FRAG_LIGHT, + VARYING_MODE_FRAG_TO_LIGHT, + VARYING_MODE_MAX, + }; + + enum VaryingType { + VARYING_TYPE_FLOAT, + VARYING_TYPE_VECTOR_2D, + VARYING_TYPE_VECTOR_3D, + VARYING_TYPE_VECTOR_4D, + VARYING_TYPE_COLOR, + VARYING_TYPE_TRANSFORM, + VARYING_TYPE_MAX, + }; + + struct Varying { + String name; + VaryingMode mode; + VaryingType type; + + Varying() { + } + + Varying(String p_name, VaryingMode p_mode, VaryingType p_type) : + name(p_name), mode(p_mode), type(p_type) {} + + bool from_string(const String &p_str) { + Vector<String> arr = p_str.split(","); + if (arr.size() != 2) { + return false; + } + + mode = (VaryingMode)arr[0].to_int(); + type = (VaryingType)arr[1].to_int(); + + return true; + } + + String to_string() const { + return vformat("%s,%s", itos((int)mode), itos((int)type)); + } + }; + private: Type current_type; @@ -83,7 +125,7 @@ private: }; struct Graph { - Map<int, Node> nodes; + RBMap<int, Node> nodes; List<Connection> connections; } graph[TYPE_MAX]; @@ -94,13 +136,11 @@ private: Vector2 graph_offset; - struct RenderModeEnums { - Shader::Mode mode = Shader::Mode::MODE_MAX; - const char *string; - }; - HashMap<String, int> modes; - Set<StringName> flags; + HashSet<StringName> flags; + + HashMap<String, Varying> varyings; + List<Varying> varyings_list; mutable SafeFlag dirty; void _queue_update(); @@ -116,7 +156,7 @@ private: } }; - Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<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; + Error _write_node(Type p_type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, HashSet<int> &processed, bool for_preview, HashSet<StringName> &r_classes) const; void _input_type_changed(Type p_type, int p_id); bool has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const; @@ -135,14 +175,6 @@ public: // internal methods void set_shader_type(Type p_type); Type get_shader_type() const; -public: - void set_engine_version(const Dictionary &p_version); - Dictionary get_engine_version() const; - -#ifndef DISABLE_DEPRECATED - void update_engine_version(const Dictionary &p_new_version); -#endif /* DISABLE_DEPRECATED */ - enum { NODE_ID_INVALID = -1, NODE_ID_OUTPUT = 0, @@ -151,6 +183,18 @@ public: void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id); void set_node_position(Type p_type, int p_id, const Vector2 &p_position); + void add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type); + void remove_varying(const String &p_name); + bool has_varying(const String &p_name) const; + int get_varyings_count() const; + const Varying *get_varying_by_index(int p_idx) const; + + void set_varying_mode(const String &p_name, VaryingMode p_mode); + VaryingMode get_varying_mode(const String &p_name); + + void set_varying_type(const String &p_name, VaryingType p_type); + VaryingType get_varying_type(const String &p_name); + Vector2 get_node_position(Type p_type, int p_id) const; Ref<VisualShaderNode> get_node(Type p_type, int p_id) const; @@ -190,6 +234,8 @@ public: }; VARIANT_ENUM_CAST(VisualShader::Type) +VARIANT_ENUM_CAST(VisualShader::VaryingMode) +VARIANT_ENUM_CAST(VisualShader::VaryingType) /// /// /// @@ -199,10 +245,10 @@ class VisualShaderNode : public Resource { int port_preview = -1; - Map<int, Variant> default_input_values; - Map<int, bool> connected_input_ports; - Map<int, int> connected_output_ports; - Map<int, bool> expanded_output_ports; + HashMap<int, Variant> default_input_values; + HashMap<int, bool> connected_input_ports; + HashMap<int, int> connected_output_ports; + HashMap<int, bool> expanded_output_ports; protected: bool simple_decl = true; @@ -216,6 +262,7 @@ public: PORT_TYPE_SCALAR_INT, PORT_TYPE_VECTOR_2D, PORT_TYPE_VECTOR_3D, + PORT_TYPE_VECTOR_4D, PORT_TYPE_BOOLEAN, PORT_TYPE_TRANSFORM, PORT_TYPE_SAMPLER, @@ -271,7 +318,7 @@ public: void set_disabled(bool p_disabled = true); virtual Vector<StringName> get_editable_properties() const; - virtual Map<StringName, String> get_editable_properties_names() const; + virtual HashMap<StringName, String> get_editable_properties_names() const; virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; @@ -328,14 +375,20 @@ protected: GDVIRTUAL1RC(int, _get_output_port_type, int) GDVIRTUAL1RC(String, _get_output_port_name, int) GDVIRTUAL4RC(String, _get_code, TypedArray<String>, TypedArray<String>, Shader::Mode, VisualShader::Type) + GDVIRTUAL2RC(String, _get_func_code, Shader::Mode, VisualShader::Type) GDVIRTUAL1RC(String, _get_global_code, Shader::Mode) GDVIRTUAL0RC(bool, _is_highend) + GDVIRTUAL2RC(bool, _is_available, Shader::Mode, VisualShader::Type) + + bool _is_valid_code(const String &p_code) const; protected: void _set_input_port_default_value(int p_port, const Variant &p_value); + bool is_available(Shader::Mode p_mode, VisualShader::Type p_type) const; 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 String generate_global_per_node(Shader::Mode p_mode, int p_id) const override; + virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; static void _bind_methods(); @@ -385,6 +438,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_caption() const override; @@ -495,6 +549,7 @@ public: UNIFORM_TYPE_BOOLEAN, UNIFORM_TYPE_VECTOR2, UNIFORM_TYPE_VECTOR3, + UNIFORM_TYPE_VECTOR4, UNIFORM_TYPE_TRANSFORM, UNIFORM_TYPE_COLOR, UNIFORM_TYPE_SAMPLER, @@ -506,6 +561,7 @@ public: }; private: + RID shader_rid; String uniform_name = "[None]"; UniformType uniform_type = UniformType::UNIFORM_TYPE_FLOAT; @@ -513,9 +569,9 @@ protected: static void _bind_methods(); public: - static void add_uniform(const String &p_name, UniformType p_type); - static void clear_uniforms(); - static bool has_uniform(const String &p_name); + static void add_uniform(RID p_shader_rid, const String &p_name, UniformType p_type); + static void clear_uniforms(RID p_shader_rid); + static bool has_uniform(RID p_shader_rid, const String &p_name); public: virtual String get_caption() const override; @@ -528,9 +584,13 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + void set_shader_rid(const RID &p_shader); + void set_uniform_name(const String &p_name); String get_uniform_name() const; + void update_uniform_type(); + void _set_uniform_type(int p_uniform_type); int _get_uniform_type() const; @@ -615,9 +675,9 @@ protected: String name; }; - Map<int, Port> input_ports; - Map<int, Port> output_ports; - Map<int, Control *> controls; + HashMap<int, Port> input_ports; + HashMap<int, Port> output_ports; + HashMap<int, Control *> controls; protected: static void _bind_methods(); @@ -697,6 +757,103 @@ public: VisualShaderNodeGlobalExpression(); }; +class VisualShaderNodeVarying : public VisualShaderNode { + GDCLASS(VisualShaderNodeVarying, VisualShaderNode); + +public: + struct Varying { + String name; + VisualShader::VaryingMode mode; + VisualShader::VaryingType type; + bool assigned = false; + }; + +protected: + VisualShader::VaryingType varying_type = VisualShader::VARYING_TYPE_FLOAT; + String varying_name = "[None]"; + +public: // internal + static void add_varying(const String &p_name, VisualShader::VaryingMode p_mode, VisualShader::VaryingType p_type); + static void clear_varyings(); + static bool has_varying(const String &p_name); + + int get_varyings_count() const; + String get_varying_name_by_index(int p_idx) const; + VisualShader::VaryingMode get_varying_mode_by_name(const String &p_name) const; + VisualShader::VaryingMode get_varying_mode_by_index(int p_idx) const; + VisualShader::VaryingType get_varying_type_by_name(const String &p_name) const; + VisualShader::VaryingType get_varying_type_by_index(int p_idx) const; + PortType get_port_type_by_index(int p_idx) const; + +protected: + static void _bind_methods(); + +protected: + String get_type_str() const; + PortType get_port_type(VisualShader::VaryingType p_type, int p_port) const; + +public: + virtual String get_caption() const override = 0; + + virtual int get_input_port_count() const override = 0; + virtual PortType get_input_port_type(int p_port) const override = 0; + virtual String get_input_port_name(int p_port) const override = 0; + + virtual int get_output_port_count() const override = 0; + virtual PortType get_output_port_type(int p_port) const override = 0; + virtual String get_output_port_name(int p_port) const override = 0; + + 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 = 0; + + void set_varying_name(String p_varying_name); + String get_varying_name() const; + + void set_varying_type(VisualShader::VaryingType p_varying_type); + VisualShader::VaryingType get_varying_type() const; + + VisualShaderNodeVarying(); +}; + +class VisualShaderNodeVaryingSetter : public VisualShaderNodeVarying { + GDCLASS(VisualShaderNodeVaryingSetter, VisualShaderNodeVarying); + +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_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; + + VisualShaderNodeVaryingSetter(); +}; + +class VisualShaderNodeVaryingGetter : public VisualShaderNodeVarying { + GDCLASS(VisualShaderNodeVaryingGetter, VisualShaderNodeVarying); + +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 bool has_output_port_preview(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; + + VisualShaderNodeVaryingGetter(); +}; + extern String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name); #endif // VISUAL_SHADER_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index f2479199ee..b8667f07fe 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -38,6 +38,8 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_ return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -50,6 +52,8 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_output_port return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -73,10 +77,11 @@ void VisualShaderNodeVectorBase::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeVectorBase::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeVectorBase::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -291,7 +296,7 @@ int VisualShaderNodeColorConstant::get_input_port_count() const { } VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; + return PORT_TYPE_VECTOR_4D; } String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { @@ -299,15 +304,15 @@ String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { } int VisualShaderNodeColorConstant::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } 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 + return ""; } bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const { @@ -318,11 +323,7 @@ bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const } 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 += " " + 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; + return " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.r, constant.g, constant.b, constant.a) + ";\n"; } void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) { @@ -477,6 +478,68 @@ void VisualShaderNodeVec3Constant::_bind_methods() { VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() { } +////////////// Vector4 + +String VisualShaderNodeVec4Constant::get_caption() const { + return "Vector4Constant"; +} + +int VisualShaderNodeVec4Constant::get_input_port_count() const { + return 0; +} + +VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Constant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec4Constant::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Constant::get_output_port_name(int p_port) const { + return ""; // No output port means the editor will be used as port. +} + +String VisualShaderNodeVec4Constant::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 " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.x, constant.y, constant.z, constant.w) + ";\n"; +} + +void VisualShaderNodeVec4Constant::set_constant(const Quaternion &p_constant) { + if (constant.is_equal_approx(p_constant)) { + return; + } + constant = p_constant; + emit_changed(); +} + +Quaternion VisualShaderNodeVec4Constant::get_constant() const { + return constant; +} + +Vector<StringName> VisualShaderNodeVec4Constant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeVec4Constant::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec4Constant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec4Constant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeVec4Constant::VisualShaderNodeVec4Constant() { +} + ////////////// Transform3D String VisualShaderNodeTransformConstant::get_caption() const { @@ -584,21 +647,25 @@ String VisualShaderNodeTexture::get_input_port_name(int p_port) const { } int VisualShaderNodeTexture::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const { - if (p_port == 0 && source == SOURCE_DEPTH) { - return PORT_TYPE_SCALAR; + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_4D; + default: + return PORT_TYPE_SCALAR; } - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; } String VisualShaderNodeTexture::get_output_port_name(int p_port) const { - if (p_port == 0 && source == SOURCE_DEPTH) { - return "depth"; + switch (p_port) { + case 0: + return "color"; + default: + return ""; } - return p_port == 0 ? "rgb" : "alpha"; } bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const { @@ -633,7 +700,7 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade case TYPE_DATA: break; case TYPE_COLOR: - u += " : hint_albedo"; + u += " : source_color"; break; case TYPE_NORMAL_MAP: u += " : hint_normal"; @@ -655,170 +722,116 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: default_uv = "vec2(0.0)"; } + String code; if (source == SOURCE_TEXTURE) { String id = make_unique_id(p_type, p_id, "tex"); - String code; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; return code; } if (source == SOURCE_PORT) { String id = p_input_vars[2]; - - String code; - code += " {\n"; if (id.is_empty()) { - code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; } else { if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\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; } if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - String code = " {\n"; if (p_input_vars[0].is_empty() || p_for_preview) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0);\n"; } else { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n"; } else { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\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 = " {\n"; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(TEXTURE, " + default_uv + ");\n"; } else { - code += " vec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(TEXTURE, " + p_input_vars[0] + ");\n"; } else { - code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\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 = " {\n"; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; } else { - code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; + code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; } else { - code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = _tex_read.a;\n"; - code += " }\n"; return code; } - if (p_for_preview) // DEPTH_TEXTURE is not supported in preview(canvas_item) shader - { - if (source == SOURCE_DEPTH) { - String code; - 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 = " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. - - if (p_input_vars[1].is_empty()) { - code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; + if (source == SOURCE_DEPTH) { + if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { + code += " {\n"; + if (p_input_vars[0].is_empty()) { // Use UV by default. + if (p_input_vars[1].is_empty()) { + code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; + } else { + code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + } + } else if (p_input_vars[1].is_empty()) { + //no lod + code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; } else { - code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; - } else { - code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; + code += " " + p_output_vars[0] + " = vec4(_depth, _depth, _depth, 1.0);\n"; + code += " }\n"; + return code; } - - 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 += " " + p_output_vars[0] + " = 0.0;\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; - return code; } - //none - String code; - code += " " + p_output_vars[0] + " = vec3(0.0);\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } @@ -892,7 +905,7 @@ Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const { String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { if (is_input_port_connected(2) && source != SOURCE_PORT) { - return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); + return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); } if (source == SOURCE_TEXTURE) { @@ -917,12 +930,12 @@ String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::T if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { if (get_output_port_for_preview() == 0) { // DEPTH_TEXTURE is not supported in preview(canvas_item) shader - return TTR("Invalid source for preview."); + return RTR("Invalid source for preview."); } return String(); // all good } - return TTR("Invalid source for shader."); + return RTR("Invalid source for shader."); } void VisualShaderNodeTexture::_bind_methods() { @@ -1157,15 +1170,15 @@ String VisualShaderNodeSample3D::get_input_port_name(int p_port) const { } int VisualShaderNodeSample3D::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeSample3D::get_output_port_name(int p_port) const { - return p_port == 0 ? "rgb" : "alpha"; + return "color"; } bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const { @@ -1195,7 +1208,6 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader String code; if (source == SOURCE_TEXTURE || source == SOURCE_PORT) { String id; - code += " {\n"; if (source == SOURCE_TEXTURE) { id = make_unique_id(p_type, p_id, "tex3d"); } else { @@ -1204,27 +1216,22 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader if (!id.is_empty()) { if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } } else { - code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\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 += " " + p_output_vars[0] + " = vec3(0.0);\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } @@ -1255,7 +1262,7 @@ void VisualShaderNodeSample3D::_bind_methods() { String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { if (is_input_port_connected(2) && source != SOURCE_PORT) { - return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); + return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); } if (source == SOURCE_TEXTURE) { @@ -1264,7 +1271,7 @@ String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader:: if (source == SOURCE_PORT) { return String(); // all good } - return TTR("Invalid source for shader."); + return RTR("Invalid source for shader."); } VisualShaderNodeSample3D::VisualShaderNodeSample3D() { @@ -1422,15 +1429,15 @@ String VisualShaderNodeCubemap::get_input_port_name(int p_port) const { } int VisualShaderNodeCubemap::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return PORT_TYPE_VECTOR_4D; } String VisualShaderNodeCubemap::get_output_port_name(int p_port) const { - return p_port == 0 ? "rgb" : "alpha"; + return "color"; } bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const { @@ -1456,7 +1463,7 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade case TYPE_DATA: break; case TYPE_COLOR: - u += " : hint_albedo"; + u += " : source_color"; break; case TYPE_NORMAL_MAP: u += " : hint_normal"; @@ -1484,37 +1491,28 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader: } else if (source == SOURCE_PORT) { id = p_input_vars[2]; } else { - return String(); + return code; } - code += " {\n"; - if (id.is_empty()) { - 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"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; - code += " }\n"; - return code; } @@ -1575,7 +1573,7 @@ Vector<StringName> VisualShaderNodeCubemap::get_editable_properties() const { String VisualShaderNodeCubemap::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { if (is_input_port_connected(2) && source != SOURCE_PORT) { - return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); + return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'."); } return String(); } @@ -1891,8 +1889,10 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_CROSS: - if (op_type == OP_TYPE_VECTOR_2D) { // not supported + if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. code += "vec2(0.0);\n"; + } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. + code += "vec4(0.0);\n"; } else { code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } @@ -1901,8 +1901,10 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_REFLECT: - if (op_type == OP_TYPE_VECTOR_2D) { // not supported + if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. code += "vec2(0.0);\n"; + } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. + code += "vec4(0.0);\n"; } else { code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } @@ -1931,6 +1933,10 @@ void VisualShaderNodeVectorOp::set_op_type(OpType p_op_type) { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; default: break; } @@ -1960,14 +1966,14 @@ Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const { String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { bool invalid_type = false; - if (op_type == OP_TYPE_VECTOR_2D) { + if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) { if (op == OP_CROSS || op == OP_REFLECT) { invalid_type = true; } } if (invalid_type) { - return TTR("Invalid operator for that type."); + return RTR("Invalid operator for that type."); } return String(); @@ -2004,6 +2010,10 @@ VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() { set_input_port_default_value(0, Vector3()); set_input_port_default_value(1, Vector3()); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion()); + set_input_port_default_value(1, Quaternion()); + } break; default: break; } @@ -2452,7 +2462,7 @@ void VisualShaderNodeFloatFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeFloatFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeFloatFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc,OneMinus"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Fract,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_SIN); BIND_ENUM_CONSTANT(FUNC_COS); @@ -2471,7 +2481,7 @@ void VisualShaderNodeFloatFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_FLOOR); BIND_ENUM_CONSTANT(FUNC_ROUND); BIND_ENUM_CONSTANT(FUNC_CEIL); - BIND_ENUM_CONSTANT(FUNC_FRAC); + BIND_ENUM_CONSTANT(FUNC_FRACT); BIND_ENUM_CONSTANT(FUNC_SATURATE); BIND_ENUM_CONSTANT(FUNC_NEGATE); BIND_ENUM_CONSTANT(FUNC_ACOSH); @@ -2598,8 +2608,6 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad "", // FUNC_SATURATE "-($)", "1.0 / ($)", - "", // FUNC_RGB2HSV - "", // FUNC_HSV2RGB "abs($)", "acos($)", "acosh($)", @@ -2636,8 +2644,10 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad if (op_type == OP_TYPE_VECTOR_2D) { code = "max(min($, vec2(1.0)), vec2(0.0))"; - } else { + } else if (op_type == OP_TYPE_VECTOR_3D) { code = "max(min($, vec3(1.0)), vec3(0.0))"; + } else { + code = "max(min($, vec4(1.0)), vec4(0.0))"; } return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n"; } @@ -2646,44 +2656,16 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad String code; if (op_type == OP_TYPE_VECTOR_2D) { - code = "vec2(1.0, 1.0) - $"; + code = "vec2(1.0) - $"; + } else if (op_type == OP_TYPE_VECTOR_3D) { + code = "vec3(1.0) - $"; } else { - code = "vec3(1.0, 1.0, 1.0) - $"; + code = "vec4(1.0) - $"; } return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n"; } - String code; - - if (func == FUNC_RGB2HSV) { - if (op_type == OP_TYPE_VECTOR_2D) { // not supported - return " " + p_output_vars[0] + " = vec2(0.0);\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) { - if (op_type == OP_TYPE_VECTOR_2D) { // not supported - return " " + p_output_vars[0] + " = vec2(0.0);\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 += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; - } - - return code; + return " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; } void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) { @@ -2698,6 +2680,9 @@ void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -2710,13 +2695,6 @@ void VisualShaderNodeVectorFunc::set_function(Function p_func) { if (func == p_func) { return; } - if (p_func == FUNC_RGB2HSV) { - simple_decl = false; - } else if (p_func == FUNC_HSV2RGB) { - simple_decl = false; - } else { - simple_decl = true; - } func = p_func; emit_changed(); } @@ -2731,34 +2709,16 @@ Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const { return props; } -String VisualShaderNodeVectorFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { - bool invalid_type = false; - - if (op_type == OP_TYPE_VECTOR_2D) { - if (func == FUNC_RGB2HSV || func == FUNC_HSV2RGB) { - invalid_type = true; - } - } - - if (invalid_type) { - return TTR("Invalid function for that type."); - } - - return String(); -} - void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Fract,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); BIND_ENUM_CONSTANT(FUNC_NEGATE); BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); - BIND_ENUM_CONSTANT(FUNC_RGB2HSV); - BIND_ENUM_CONSTANT(FUNC_HSV2RGB); BIND_ENUM_CONSTANT(FUNC_ABS); BIND_ENUM_CONSTANT(FUNC_ACOS); BIND_ENUM_CONSTANT(FUNC_ACOSH); @@ -2773,7 +2733,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_EXP); BIND_ENUM_CONSTANT(FUNC_EXP2); BIND_ENUM_CONSTANT(FUNC_FLOOR); - BIND_ENUM_CONSTANT(FUNC_FRAC); + BIND_ENUM_CONSTANT(FUNC_FRACT); BIND_ENUM_CONSTANT(FUNC_INVERSE_SQRT); BIND_ENUM_CONSTANT(FUNC_LOG); BIND_ENUM_CONSTANT(FUNC_LOG2); @@ -2799,6 +2759,9 @@ VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3()); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion()); + } break; default: break; } @@ -2843,8 +2806,26 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade 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 += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n"; + code += " }\n"; + break; + case FUNC_HSV2RGB: + 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"; + break; + case FUNC_RGB2HSV: + 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"; break; case FUNC_SEPIA: @@ -2886,9 +2867,11 @@ void VisualShaderNodeColorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_GRAYSCALE); + BIND_ENUM_CONSTANT(FUNC_HSV2RGB); + BIND_ENUM_CONSTANT(FUNC_RGB2HSV); BIND_ENUM_CONSTANT(FUNC_SEPIA); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -3190,6 +3173,9 @@ void VisualShaderNodeVectorLen::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -3259,6 +3245,8 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_inp return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3279,6 +3267,8 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_out return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3316,6 +3306,9 @@ void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -3354,12 +3347,13 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); BIND_ENUM_CONSTANT(FUNC_SUM); @@ -3390,6 +3384,8 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3419,6 +3415,8 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3459,6 +3457,11 @@ void VisualShaderNodeClamp::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + break; default: break; } @@ -3480,12 +3483,13 @@ void VisualShaderNodeClamp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_FLOAT); BIND_ENUM_CONSTANT(OP_TYPE_INT); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3542,6 +3546,11 @@ void VisualShaderNodeFaceForward::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + } break; default: break; } @@ -3631,6 +3640,13 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_p return PORT_TYPE_VECTOR_3D; } break; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 1) { + return PORT_TYPE_VECTOR_4D; + } + break; default: break; } @@ -3661,6 +3677,10 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_ return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3697,6 +3717,14 @@ void VisualShaderNodeStep::set_op_type(OpType p_op_type) { set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; default: break; } @@ -3722,13 +3750,15 @@ void VisualShaderNodeStep::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3763,6 +3793,13 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_ return PORT_TYPE_VECTOR_3D; // x } break; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 2) { + return PORT_TYPE_VECTOR_4D; // x + } + break; default: break; } @@ -3795,6 +3832,10 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3836,6 +3877,16 @@ void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) { set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1 set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // edge0 + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // edge1 + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x + break; + case OP_TYPE_VECTOR_4D_SCALAR: + set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0 + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1 + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x + break; default: break; } @@ -3861,13 +3912,15 @@ void VisualShaderNodeSmoothStep::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3923,6 +3976,10 @@ void VisualShaderNodeVectorDistance::set_op_type(OpType p_op_type) { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + } break; default: break; } @@ -4017,6 +4074,13 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_por break; } return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 2) { + break; + } + return PORT_TYPE_VECTOR_4D; default: break; } @@ -4047,6 +4111,10 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_po return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -4088,6 +4156,16 @@ void VisualShaderNodeMix::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // weight + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight + } break; default: break; } @@ -4113,13 +4191,15 @@ void VisualShaderNodeMix::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -4141,6 +4221,8 @@ int VisualShaderNodeVectorCompose::get_input_port_count() const { return 2; case OP_TYPE_VECTOR_3D: return 3; + case OP_TYPE_VECTOR_4D: + return 4; default: break; } @@ -4171,6 +4253,18 @@ String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const { return "z"; } } break; + case OP_TYPE_VECTOR_4D: { + switch (p_port) { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + } + } break; default: break; } @@ -4206,6 +4300,15 @@ void VisualShaderNodeVectorCompose::set_op_type(OpType p_op_type) { set_input_port_default_value(1, p2); set_input_port_default_value(2, 0.0); } break; + case OP_TYPE_VECTOR_4D: { + float p1 = get_input_port_default_value(0); + float p2 = get_input_port_default_value(1); + + set_input_port_default_value(0, p1); + set_input_port_default_value(1, p2); + set_input_port_default_value(2, 0.0); + set_input_port_default_value(3, 0.0); + } break; default: break; } @@ -4222,6 +4325,9 @@ String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualS case OP_TYPE_VECTOR_3D: { code += " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } break; + case OP_TYPE_VECTOR_4D: { + code += " " + p_output_vars[0] + " = vec4(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ", " + p_input_vars[3] + ");\n"; + } break; default: break; } @@ -4302,6 +4408,8 @@ int VisualShaderNodeVectorDecompose::get_output_port_count() const { return 2; case OP_TYPE_VECTOR_3D: return 3; + case OP_TYPE_VECTOR_4D: + return 4; default: break; } @@ -4332,6 +4440,18 @@ String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const { return "z"; } } break; + case OP_TYPE_VECTOR_4D: { + switch (p_port) { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + } + } break; default: break; } @@ -4350,6 +4470,9 @@ void VisualShaderNodeVectorDecompose::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -4369,6 +4492,12 @@ String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, Visua code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; } break; + case OP_TYPE_VECTOR_4D: { + 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"; + code += " " + p_output_vars[3] + " = " + p_input_vars[0] + ".w;\n"; + } break; default: break; } @@ -4933,7 +5062,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const { } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; + return PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { @@ -4941,15 +5070,22 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { } int VisualShaderNodeColorUniform::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { - return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port + return "color"; +} + +bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; } void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) { @@ -4977,7 +5113,7 @@ Color VisualShaderNodeColorUniform::get_default_value() const { } String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String code = _get_qual_str() + "uniform vec4 " + get_uniform_name() + " : hint_color"; + String code = _get_qual_str() + "uniform vec4 " + get_uniform_name() + " : source_color"; if (default_value_enabled) { code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.r, default_value.g, default_value.b, default_value.a); } @@ -4986,9 +5122,7 @@ 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 = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; - return code; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeColorUniform::is_show_prop_names() const { @@ -5226,6 +5360,106 @@ Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() { } +////////////// Vector4 Uniform + +String VisualShaderNodeVec4Uniform::get_caption() const { + return "Vector4Uniform"; +} + +int VisualShaderNodeVec4Uniform::get_input_port_count() const { + return 0; +} + +VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Uniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec4Uniform::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Uniform::get_output_port_name(int p_port) const { + return ""; // No output port means the editor will be used as port. +} + +void VisualShaderNodeVec4Uniform::set_default_value_enabled(bool p_enabled) { + default_value_enabled = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeVec4Uniform::is_default_value_enabled() const { + return default_value_enabled; +} + +void VisualShaderNodeVec4Uniform::set_default_value(const Quaternion &p_value) { + default_value = p_value; + emit_changed(); +} + +Quaternion VisualShaderNodeVec4Uniform::get_default_value() const { + return default_value; +} + +String VisualShaderNodeVec4Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec4 " + get_uniform_name(); + if (default_value_enabled) { + code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.x, default_value.y, default_value.z, default_value.w); + } + code += ";\n"; + return code; +} + +String VisualShaderNodeVec4Uniform::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 " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +void VisualShaderNodeVec4Uniform::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec4Uniform::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec4Uniform::is_default_value_enabled); + + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec4Uniform::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec4Uniform::get_default_value); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "default_value"), "set_default_value", "get_default_value"); +} + +bool VisualShaderNodeVec4Uniform::is_show_prop_names() const { + return true; +} + +bool VisualShaderNodeVec4Uniform::is_use_prop_slots() const { + return true; +} + +bool VisualShaderNodeVec4Uniform::is_qualifier_supported(Qualifier p_qual) const { + return true; // All qualifiers are supported. +} + +bool VisualShaderNodeVec4Uniform::is_convertible_to_constant() const { + return true; // Conversion is allowed. +} + +Vector<StringName> VisualShaderNodeVec4Uniform::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); + props.push_back("default_value_enabled"); + if (default_value_enabled) { + props.push_back("default_value"); + } + return props; +} + +VisualShaderNodeVec4Uniform::VisualShaderNodeVec4Uniform() { +} + ////////////// Transform Uniform String VisualShaderNodeTransformUniform::get_caption() const { @@ -5277,9 +5511,9 @@ Transform3D VisualShaderNodeTransformUniform::get_default_value() const { String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform mat4 " + get_uniform_name(); if (default_value_enabled) { - Vector3 row0 = default_value.basis.get_row(0); - Vector3 row1 = default_value.basis.get_row(1); - Vector3 row2 = default_value.basis.get_row(2); + Vector3 row0 = default_value.basis.rows[0]; + Vector3 row1 = default_value.basis.rows[1]; + Vector3 row2 = default_value.basis.rows[2]; Vector3 origin = default_value.origin; code += " = mat4(" + vformat("vec4(%.6f, %.6f, %.6f, 0.0)", row0.x, row0.y, row0.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row1.x, row1.y, row1.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row2.x, row2.y, row2.z) + vformat(", vec4(%.6f, %.6f, %.6f, 1.0)", origin.x, origin.y, origin.z) + ")"; } @@ -5333,79 +5567,32 @@ Vector<StringName> VisualShaderNodeTransformUniform::get_editable_properties() c VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() { } -////////////// Texture Uniform - -String VisualShaderNodeTextureUniform::get_caption() const { - return "TextureUniform"; -} - -int VisualShaderNodeTextureUniform::get_input_port_count() const { - return 2; -} - -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_2D : PORT_TYPE_SCALAR; -} - -String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { - return p_port == 0 ? "uv" : "lod"; -} - -int VisualShaderNodeTextureUniform::get_output_port_count() const { - return 3; -} - -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { - switch (p_port) { - case 0: - return PORT_TYPE_VECTOR_3D; - case 1: - return PORT_TYPE_SCALAR; - case 2: - return PORT_TYPE_SAMPLER; - default: - return PORT_TYPE_SCALAR; - } -} - -String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { - switch (p_port) { - case 0: - return "rgb"; - case 1: - return "alpha"; - case 2: - return "sampler2D"; - default: - return ""; - } -} +////////////// -String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { +String get_sampler_hint(VisualShaderNodeTextureUniform::TextureType p_texture_type, VisualShaderNodeTextureUniform::ColorDefault p_color_default, VisualShaderNodeTextureUniform::TextureFilter p_texture_filter, VisualShaderNodeTextureUniform::TextureRepeat p_texture_repeat) { + String code; bool has_colon = false; - String code = _get_qual_str() + "uniform sampler2D " + get_uniform_name(); // type { String type_code; - switch (texture_type) { - case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) { - type_code = "hint_black"; + switch (p_texture_type) { + case VisualShaderNodeTextureUniform::TYPE_DATA: + if (p_color_default == VisualShaderNodeTextureUniform::COLOR_DEFAULT_BLACK) { + type_code = "hint_default_black"; } break; - case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) { - type_code = "hint_black_albedo"; - } else { - type_code = "hint_albedo"; + case VisualShaderNodeTextureUniform::TYPE_COLOR: + type_code = "source_color"; + if (p_color_default == VisualShaderNodeTextureUniform::COLOR_DEFAULT_BLACK) { + type_code += ", hint_default_black"; } break; - case TYPE_NORMAL_MAP: + case VisualShaderNodeTextureUniform::TYPE_NORMAL_MAP: type_code = "hint_normal"; break; - case TYPE_ANISOTROPY: + case VisualShaderNodeTextureUniform::TYPE_ANISOTROPY: type_code = "hint_anisotropy"; break; default: @@ -5422,23 +5609,23 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu { String filter_code; - switch (texture_filter) { - case FILTER_NEAREST: + switch (p_texture_filter) { + case VisualShaderNodeTextureUniform::FILTER_NEAREST: filter_code = "filter_nearest"; break; - case FILTER_LINEAR: + case VisualShaderNodeTextureUniform::FILTER_LINEAR: filter_code = "filter_linear"; break; - case FILTER_NEAREST_MIPMAP: + case VisualShaderNodeTextureUniform::FILTER_NEAREST_MIPMAP: filter_code = "filter_nearest_mipmap"; break; - case FILTER_LINEAR_MIPMAP: + case VisualShaderNodeTextureUniform::FILTER_LINEAR_MIPMAP: filter_code = "filter_linear_mipmap"; break; - case FILTER_NEAREST_MIPMAP_ANISOTROPIC: + case VisualShaderNodeTextureUniform::FILTER_NEAREST_MIPMAP_ANISOTROPIC: filter_code = "filter_nearest_mipmap_anisotropic"; break; - case FILTER_LINEAR_MIPMAP_ANISOTROPIC: + case VisualShaderNodeTextureUniform::FILTER_LINEAR_MIPMAP_ANISOTROPIC: filter_code = "filter_linear_mipmap_anisotropic"; break; default: @@ -5460,11 +5647,11 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu { String repeat_code; - switch (texture_repeat) { - case REPEAT_ENABLED: + switch (p_texture_repeat) { + case VisualShaderNodeTextureUniform::REPEAT_ENABLED: repeat_code = "repeat_enable"; break; - case REPEAT_DISABLED: + case VisualShaderNodeTextureUniform::REPEAT_DISABLED: repeat_code = "repeat_disable"; break; default: @@ -5481,43 +5668,60 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu } } - code += ";\n"; return code; } -bool VisualShaderNodeTextureUniform::is_code_generated() const { - return is_output_port_connected(0) || is_output_port_connected(1); // rgb or alpha +////////////// Texture Uniform + +String VisualShaderNodeTextureUniform::get_caption() const { + return "TextureUniform"; } -String VisualShaderNodeTextureUniform::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 default_uv; - if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - default_uv = "UV"; - } else { - default_uv = "vec2(0.0)"; +int VisualShaderNodeTextureUniform::get_input_port_count() const { + return 0; +} + +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeTextureUniform::get_output_port_count() const { + return 1; +} + +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_SAMPLER; + default: + return PORT_TYPE_SCALAR; } +} - String id = get_uniform_name(); - String code = " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " vec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; - } else { - code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; +String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { + switch (p_port) { + case 0: + return "sampler2D"; + default: + return ""; } +} - code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = n_tex_read.a;\n"; - code += " }\n"; +String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform sampler2D " + get_uniform_name(); + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; return code; } +String VisualShaderNodeTextureUniform::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 ""; +} + void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) { ERR_FAIL_INDEX(int(p_texture_type), int(TYPE_MAX)); if (texture_type == p_texture_type) { @@ -5585,12 +5789,12 @@ bool VisualShaderNodeTextureUniform::is_show_prop_names() const { return true; } -Map<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { - Map<StringName, String> names; - names.insert("texture_type", TTR("Type")); - names.insert("color_default", TTR("Default Color")); - names.insert("texture_filter", TTR("Filter")); - names.insert("texture_repeat", TTR("Repeat")); +HashMap<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { + HashMap<StringName, String> names; + names.insert("texture_type", RTR("Type")); + names.insert("color_default", RTR("Default Color")); + names.insert("texture_filter", RTR("Filter")); + names.insert("texture_repeat", RTR("Repeat")); return names; } @@ -5637,15 +5841,6 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(REPEAT_MAX); } -bool VisualShaderNodeTextureUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - if (p_port == 0) { - return true; - } - } - return false; -} - bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) const { switch (p_qual) { case Qualifier::QUAL_NONE: @@ -5665,7 +5860,6 @@ bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const { } VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { - simple_decl = false; } ////////////// Texture Uniform (Triplanar) @@ -5678,7 +5872,7 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const { return 2; } -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const { +VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const { if (p_port == 0 || p_port == 1) { return PORT_TYPE_VECTOR_3D; } @@ -5694,10 +5888,36 @@ String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port) return ""; } +int VisualShaderNodeTextureUniformTriplanar::get_output_port_count() const { + return 2; +} + +VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_output_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_4D; + case 1: + return PORT_TYPE_SAMPLER; + default: + return PORT_TYPE_SCALAR; + } +} + +String VisualShaderNodeTextureUniformTriplanar::get_output_port_name(int p_port) const { + switch (p_port) { + case 0: + return "color"; + case 1: + return "sampler2D"; + default: + return ""; + } +} + String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const { String code; - code += "// TRIPLANAR FUNCTION GLOBAL CODE\n"; + code += "// " + get_caption() + "\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"; @@ -5720,11 +5940,13 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader: String code; if (p_type == VisualShader::TYPE_VERTEX) { - code += " // TRIPLANAR FUNCTION VERTEX CODE\n"; + code += "// " + get_caption() + "\n"; + 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"; + code += " }\n"; } return code; @@ -5732,22 +5954,18 @@ 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 = " {\n"; + String code; if (p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; } else if (!p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; } else if (p_input_vars[0].is_empty() && !p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; } else { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\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; } @@ -5769,63 +5987,14 @@ String VisualShaderNodeTexture2DArrayUniform::get_caption() const { return "Texture2DArrayUniform"; } -int VisualShaderNodeTexture2DArrayUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeTexture2DArrayUniform::get_output_port_name(int p_port) const { return "sampler2DArray"; } -int VisualShaderNodeTexture2DArrayUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeTexture2DArrayUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeTexture2DArrayUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform sampler2DArray " + get_uniform_name(); - - switch (texture_type) { - case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black;\n"; - } else { - code += ";\n"; - } - break; - case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black_albedo;\n"; - } else { - code += " : hint_albedo;\n"; - } - break; - case TYPE_NORMAL_MAP: - code += " : hint_normal;\n"; - break; - case TYPE_ANISOTROPY: - code += " : hint_anisotropy;\n"; - break; - default: - code += ";\n"; - break; - } - + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; return code; } @@ -5842,63 +6011,14 @@ String VisualShaderNodeTexture3DUniform::get_caption() const { return "Texture3DUniform"; } -int VisualShaderNodeTexture3DUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeTexture3DUniform::get_output_port_name(int p_port) const { return "sampler3D"; } -int VisualShaderNodeTexture3DUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeTexture3DUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeTexture3DUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform sampler3D " + get_uniform_name(); - - switch (texture_type) { - case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black;\n"; - } else { - code += ";\n"; - } - break; - case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black_albedo;\n"; - } else { - code += " : hint_albedo;\n"; - } - break; - case TYPE_NORMAL_MAP: - code += " : hint_normal;\n"; - break; - case TYPE_ANISOTROPY: - code += " : hint_anisotropy;\n"; - break; - default: - code += ";\n"; - break; - } - + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; return code; } @@ -5915,63 +6035,14 @@ String VisualShaderNodeCubemapUniform::get_caption() const { return "CubemapUniform"; } -int VisualShaderNodeCubemapUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeCubemapUniform::get_output_port_name(int p_port) const { return "samplerCube"; } -int VisualShaderNodeCubemapUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeCubemapUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeCubemapUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform samplerCube " + get_uniform_name(); - - switch (texture_type) { - case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black;\n"; - } else { - code += ";\n"; - } - break; - case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black_albedo;\n"; - } else { - code += " : hint_albedo;\n"; - } - break; - case TYPE_NORMAL_MAP: - code += " : hint_normal;\n"; - break; - case TYPE_ANISOTROPY: - code += " : hint_anisotropy;\n"; - break; - default: - code += ";\n"; - break; - } - + code += get_sampler_hint(texture_type, color_default, texture_filter, texture_repeat); + code += ";\n"; return code; } @@ -6079,6 +6150,8 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case OP_TYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case OP_TYPE_TRANSFORM: @@ -6115,6 +6188,8 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(in return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case OP_TYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case OP_TYPE_TRANSFORM: @@ -6151,6 +6226,10 @@ void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0), get_input_port_default_value(2)); break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(1, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(0.0, 0.0, 0.0, 0.0), get_input_port_default_value(2)); + break; case OP_TYPE_BOOLEAN: set_input_port_default_value(1, true); set_input_port_default_value(2, false); @@ -6180,12 +6259,13 @@ void VisualShaderNodeSwitch::_bind_methods() { // static ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSwitch::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSwitch::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_FLOAT); BIND_ENUM_CONSTANT(OP_TYPE_INT); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_BOOLEAN); BIND_ENUM_CONSTANT(OP_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(OP_TYPE_MAX); @@ -6419,6 +6499,8 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i return PORT_TYPE_VECTOR_2D; case CTYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case CTYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case CTYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case CTYPE_TRANSFORM: @@ -6457,7 +6539,7 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const { String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) { if (func > FUNC_NOT_EQUAL) { - return TTR("Invalid comparison function for that type."); + return RTR("Invalid comparison function for that type."); } } return ""; @@ -6513,6 +6595,12 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader: code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n"; code += " }\n"; } break; + case CTYPE_VECTOR_4D: { + code += " {\n"; + code += " bvec4 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n"; + code += " }\n"; + } break; case CTYPE_BOOLEAN: { if (func > FUNC_NOT_EQUAL) { return " " + p_output_vars[0] + " = false;\n"; @@ -6557,6 +6645,11 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_ty set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); simple_decl = false; break; + case CTYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + simple_decl = false; + break; case CTYPE_BOOLEAN: set_input_port_default_value(0, false); set_input_port_default_value(1, false); @@ -6608,7 +6701,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const { Vector<StringName> props; props.push_back("type"); props.push_back("function"); - if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D) { + if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D || comparison_type == CTYPE_VECTOR_4D) { props.push_back("condition"); } return props; @@ -6624,7 +6717,7 @@ void VisualShaderNodeCompare::_bind_methods() { ClassDB::bind_method(D_METHOD("set_condition", "condition"), &VisualShaderNodeCompare::set_condition); ClassDB::bind_method(D_METHOD("get_condition"), &VisualShaderNodeCompare::get_condition); - ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_comparison_type", "get_comparison_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_comparison_type", "get_comparison_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "a == b,a != b,a > b,a >= b,a < b,a <= b"), "set_function", "get_function"); ADD_PROPERTY(PropertyInfo(Variant::INT, "condition", PROPERTY_HINT_ENUM, "All,Any"), "set_condition", "get_condition"); @@ -6632,6 +6725,7 @@ void VisualShaderNodeCompare::_bind_methods() { BIND_ENUM_CONSTANT(CTYPE_SCALAR_INT); BIND_ENUM_CONSTANT(CTYPE_VECTOR_2D); BIND_ENUM_CONSTANT(CTYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(CTYPE_VECTOR_4D); BIND_ENUM_CONSTANT(CTYPE_BOOLEAN); BIND_ENUM_CONSTANT(CTYPE_TRANSFORM); BIND_ENUM_CONSTANT(CTYPE_MAX); @@ -6671,6 +6765,8 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_por return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -6698,6 +6794,8 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_po return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -6733,6 +6831,11 @@ void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + } break; default: break; } @@ -6754,11 +6857,12 @@ void VisualShaderNodeMultiplyAdd::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMultiplyAdd::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMultiplyAdd::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -6804,29 +6908,29 @@ String VisualShaderNodeBillboard::generate_code(Shader::Mode p_mode, VisualShade 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"; + code += " mat4 __mvm = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_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 += " __mvm = __mvm * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_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"; + code += " mat4 __mvm = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], MODEL_MATRIX[1], vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, MODEL_MATRIX[1].xyz)), 0.0), MODEL_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"; + code += " __mvm = __mvm * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_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 += " __mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(MODEL_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 += " mat4 __wm = mat4(normalize(INV_VIEW_MATRIX[0]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[1]) * length(MODEL_MATRIX[0]), normalize(INV_VIEW_MATRIX[2]) * length(MODEL_MATRIX[2]), MODEL_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 += " " + p_output_vars[0] + " = VIEW_MATRIX * __wm;\n"; code += " }\n"; break; default: diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index eeeb91a3ee..1eb7b7240f 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -44,6 +44,7 @@ public: enum OpType { OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -280,6 +281,36 @@ public: /////////////////////////////////////// +class VisualShaderNodeVec4Constant : public VisualShaderNodeConstant { + GDCLASS(VisualShaderNodeVec4Constant, VisualShaderNodeConstant); + Quaternion constant; + +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; + + void set_constant(const Quaternion &p_constant); + Quaternion get_constant() const; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeVec4Constant(); +}; + +/////////////////////////////////////// + class VisualShaderNodeTransformConstant : public VisualShaderNodeConstant { GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNodeConstant); Transform3D constant; @@ -903,7 +934,7 @@ public: FUNC_FLOOR, FUNC_ROUND, FUNC_CEIL, - FUNC_FRAC, + FUNC_FRACT, FUNC_SATURATE, FUNC_NEGATE, FUNC_ACOSH, @@ -1008,8 +1039,6 @@ public: FUNC_SATURATE, FUNC_NEGATE, FUNC_RECIPROCAL, - FUNC_RGB2HSV, - FUNC_HSV2RGB, FUNC_ABS, FUNC_ACOS, FUNC_ACOSH, @@ -1024,7 +1053,7 @@ public: FUNC_EXP, FUNC_EXP2, FUNC_FLOOR, - FUNC_FRAC, + FUNC_FRACT, FUNC_INVERSE_SQRT, FUNC_LOG, FUNC_LOG2, @@ -1064,7 +1093,6 @@ public: Function get_function() const; virtual Vector<StringName> get_editable_properties() const override; - String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; VisualShaderNodeVectorFunc(); }; @@ -1081,6 +1109,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode { public: enum Function { FUNC_GRAYSCALE, + FUNC_HSV2RGB, + FUNC_RGB2HSV, FUNC_SEPIA, FUNC_MAX, }; @@ -1282,6 +1312,7 @@ public: OP_TYPE_INT, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -1324,6 +1355,7 @@ public: OP_TYPE_SCALAR, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -1427,6 +1459,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1471,6 +1505,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1561,6 +1597,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1883,6 +1921,8 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + bool is_output_port_expandable(int p_port) 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; @@ -1990,6 +2030,49 @@ public: /////////////////////////////////////// +class VisualShaderNodeVec4Uniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeVec4Uniform, VisualShaderNodeUniform); + +private: + bool default_value_enabled = false; + Quaternion default_value; + +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_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; + + virtual bool is_show_prop_names() const override; + virtual bool is_use_prop_slots() const override; + + void set_default_value_enabled(bool p_enabled); + bool is_default_value_enabled() const; + + void set_default_value(const Quaternion &p_value); + Quaternion get_default_value() const; + + bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeVec4Uniform(); +}; + +/////////////////////////////////////// + class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform); @@ -2084,7 +2167,6 @@ public: 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_input_port_default(int p_port, Shader::Mode p_mode) const override; virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; @@ -2093,9 +2175,8 @@ public: virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - virtual Map<StringName, String> get_editable_properties_names() const override; + virtual HashMap<StringName, String> get_editable_properties_names() const override; virtual bool is_show_prop_names() const override; - virtual bool is_code_generated() const override; Vector<StringName> get_editable_properties() const override; @@ -2134,6 +2215,10 @@ public: 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 bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override; @@ -2150,16 +2235,8 @@ class VisualShaderNodeTexture2DArrayUniform : public VisualShaderNodeTextureUnif 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 bool is_input_port_default(int p_port, Shader::Mode p_mode) 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; @@ -2173,16 +2250,8 @@ class VisualShaderNodeTexture3DUniform : public VisualShaderNodeTextureUniform { 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 bool is_input_port_default(int p_port, Shader::Mode p_mode) 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; @@ -2196,16 +2265,8 @@ class VisualShaderNodeCubemapUniform : public VisualShaderNodeTextureUniform { 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 bool is_input_port_default(int p_port, Shader::Mode p_mode) 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; @@ -2248,6 +2309,7 @@ public: OP_TYPE_INT, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_BOOLEAN, OP_TYPE_TRANSFORM, OP_TYPE_MAX, @@ -2362,6 +2424,7 @@ public: CTYPE_SCALAR_INT, CTYPE_VECTOR_2D, CTYPE_VECTOR_3D, + CTYPE_VECTOR_4D, CTYPE_BOOLEAN, CTYPE_TRANSFORM, CTYPE_MAX, @@ -2431,6 +2494,7 @@ public: OP_TYPE_SCALAR, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 398c33c452..bdfbb59fa6 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -74,9 +74,9 @@ Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() co return props; } -Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const { - Map<StringName, String> names; - names.insert("mode_2d", TTR("2D Mode")); +HashMap<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const { + HashMap<StringName, String> names; + names.insert("mode_2d", RTR("2D Mode")); return names; } @@ -470,7 +470,7 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector2> image->set_pixel(i, 0, Color(v.x, v.y, 0)); } if (r_texture->get_width() != p_array.size() || p_array.size() == 0) { - r_texture->create_from_image(image); + r_texture->set_image(image); } else { r_texture->update(image); } @@ -491,7 +491,7 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector3> image->set_pixel(i, 0, Color(v.x, v.y, v.z)); } if (r_texture->get_width() != p_array.size() || p_array.size() == 0) { - r_texture->create_from_image(image); + r_texture->set_image(image); } else { r_texture->update(image); } @@ -511,7 +511,7 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Color> &p image->set_pixel(i, 0, p_array[i]); } if (r_texture->get_width() != p_array.size() || p_array.size() == 0) { - r_texture->create_from_image(image); + r_texture->set_image(image); } else { r_texture->update(image); } @@ -704,13 +704,13 @@ Vector<StringName> VisualShaderNodeParticleMeshEmitter::get_editable_properties( return props; } -Map<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const { - Map<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names(); +HashMap<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const { + HashMap<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names(); - names.insert("mesh", TTR("Mesh")); - names.insert("use_all_surfaces", TTR("Use All Surfaces")); + names.insert("mesh", RTR("Mesh")); + names.insert("use_all_surfaces", RTR("Use All Surfaces")); if (!use_all_surfaces) { - names.insert("surface_index", TTR("Surface Index")); + names.insert("surface_index", RTR("Surface Index")); } return names; diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index 0b91cba5e0..64acb6d18f 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -52,7 +52,7 @@ public: bool is_mode_2d() const; Vector<StringName> get_editable_properties() const override; - virtual Map<StringName, String> get_editable_properties_names() const override; + virtual HashMap<StringName, String> get_editable_properties_names() const override; bool is_show_prop_names() const override; VisualShaderNodeParticleEmitter(); @@ -153,7 +153,7 @@ public: int get_surface_index() const; Vector<StringName> get_editable_properties() const override; - Map<StringName, String> get_editable_properties_names() const override; + HashMap<StringName, String> get_editable_properties_names() const override; Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override; VisualShaderNodeParticleMeshEmitter(); @@ -352,4 +352,4 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeParticleEmit::EmitFlags) -#endif +#endif // VISUAL_SHADER_PARTICLE_NODES_H diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 9d8e0f7547..4dfbe5f079 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -73,8 +73,8 @@ World2D::World2D() { // Create and configure space2D to be more friendly with pixels than meters space = PhysicsServer2D::get_singleton()->space_create(); PhysicsServer2D::get_singleton()->space_set_active(space, true); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 980.0)); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/2d/default_gravity_vector", Vector2(0, 1))); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0)); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1))); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/2d/default_linear_damp", 0.1)); ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0)); diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 4a277c3d84..c04b8f6461 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -46,7 +46,7 @@ class World2D : public Resource { RID space; RID navigation_map; - Set<Viewport *> viewports; + HashSet<Viewport *> viewports; protected: static void _bind_methods(); @@ -62,7 +62,7 @@ public: PhysicsDirectSpaceState2D *get_direct_space_state(); - _FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; } + _FORCE_INLINE_ const HashSet<Viewport *> &get_viewports() { return viewports; } World2D(); ~World2D(); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index 0088236112..fb6dcd3d57 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -31,7 +31,6 @@ #include "world_3d.h" #include "core/config/project_settings.h" -#include "core/math/octree.h" #include "scene/3d/camera_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" #include "scene/scene_string_names.h" @@ -141,8 +140,8 @@ World3D::World3D() { scenario = RenderingServer::get_singleton()->scenario_create(); PhysicsServer3D::get_singleton()->space_set_active(space, true); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8)); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8)); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); @@ -150,8 +149,8 @@ World3D::World3D() { navigation_map = NavigationServer3D::get_singleton()->map_create(); 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)); + NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25)); + NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25)); } World3D::~World3D() { diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index b34b7a2bfb..08bc050349 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -53,7 +53,7 @@ private: Ref<Environment> fallback_environment; Ref<CameraEffects> camera_effects; - Set<Camera3D *> cameras; + HashSet<Camera3D *> cameras; protected: static void _bind_methods(); @@ -77,7 +77,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; } + _FORCE_INLINE_ const HashSet<Camera3D *> &get_cameras() const { return cameras; } PhysicsDirectSpaceState3D *get_direct_space_state(); diff --git a/scene/resources/world_boundary_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp index 9789388c6a..013ffb9e24 100644 --- a/scene/resources/world_boundary_shape_2d.cpp +++ b/scene/resources/world_boundary_shape_2d.cpp @@ -108,7 +108,7 @@ void WorldBoundaryShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_distance"), &WorldBoundaryShape2D::get_distance); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:px"), "set_distance", "get_distance"); } WorldBoundaryShape2D::WorldBoundaryShape2D() : diff --git a/scene/resources/world_boundary_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp index 09d41e8291..400cbae217 100644 --- a/scene/resources/world_boundary_shape_3d.cpp +++ b/scene/resources/world_boundary_shape_3d.cpp @@ -80,7 +80,7 @@ void WorldBoundaryShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldBoundaryShape3D::set_plane); ClassDB::bind_method(D_METHOD("get_plane"), &WorldBoundaryShape3D::get_plane); - ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane"), "set_plane", "get_plane"); + ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane", PROPERTY_HINT_NONE, "suffix:m"), "set_plane", "get_plane"); } WorldBoundaryShape3D::WorldBoundaryShape3D() : diff --git a/scene/resources/world_boundary_shape_3d.h b/scene/resources/world_boundary_shape_3d.h index 5378bc52c7..5c34767106 100644 --- a/scene/resources/world_boundary_shape_3d.h +++ b/scene/resources/world_boundary_shape_3d.h @@ -53,4 +53,5 @@ public: WorldBoundaryShape3D(); }; -#endif // WORLD_BOUNDARY_SHAPE_H + +#endif // WORLD_BOUNDARY_SHAPE_3D_H |