diff options
97 files changed, 2936 insertions, 679 deletions
diff --git a/.github/workflows/javascript_builds.yml b/.github/workflows/javascript_builds.yml index ced2c36a91..7f7ebf680f 100644 --- a/.github/workflows/javascript_builds.yml +++ b/.github/workflows/javascript_builds.yml @@ -4,7 +4,7 @@ on: [push, pull_request] # Global Settings env: GODOT_BASE_BRANCH: master - SCONSFLAGS: platform=javascript verbose=yes warnings=extra debug_symbols=no --jobs=2 + SCONSFLAGS: platform=javascript verbose=yes warnings=extra werror=yes debug_symbols=no --jobs=2 SCONS_CACHE_LIMIT: 4096 EM_VERSION: 2.0.25 EM_CACHE_FOLDER: 'emsdk-cache' diff --git a/SConstruct b/SConstruct index 5dec3f2020..45108721ad 100644 --- a/SConstruct +++ b/SConstruct @@ -506,13 +506,17 @@ if selected_platform in platform_list: if env["werror"]: env.Append(CCFLAGS=["/WX"]) else: # GCC, Clang - gcc_common_warnings = [] + common_warnings = [] if methods.using_gcc(env): - gcc_common_warnings += ["-Wshadow-local", "-Wno-misleading-indentation"] + common_warnings += ["-Wshadow-local", "-Wno-misleading-indentation"] + elif methods.using_clang(env) or methods.using_emcc(env): + # We often implement `operator<` for structs of pointers as a requirement + # for putting them in `Set` or `Map`. We don't mind about unreliable ordering. + common_warnings += ["-Wno-ordered-compare-function-pointers"] if env["warnings"] == "extra": - env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + gcc_common_warnings) + env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) if methods.using_gcc(env): env.Append( @@ -528,12 +532,12 @@ if selected_platform in platform_list: env.Append(CXXFLAGS=["-Wplacement-new=1"]) if cc_version_major >= 9: env.Append(CCFLAGS=["-Wattribute-alias=2"]) - elif methods.using_clang(env): + elif methods.using_clang(env) or methods.using_emcc(env): env.Append(CCFLAGS=["-Wimplicit-fallthrough"]) elif env["warnings"] == "all": - env.Append(CCFLAGS=["-Wall"] + gcc_common_warnings) + env.Append(CCFLAGS=["-Wall"] + common_warnings) elif env["warnings"] == "moderate": - env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + gcc_common_warnings) + env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings) else: # 'no' env.Append(CCFLAGS=["-w"]) @@ -544,7 +548,7 @@ if selected_platform in platform_list: env.Append(CXXFLAGS=["-Wno-error=cpp"]) if cc_version_major == 7: # Bogus warning fixed in 8+. env.Append(CCFLAGS=["-Wno-error=strict-overflow"]) - else: + elif methods.using_clang(env) or methods.using_emcc(env): env.Append(CXXFLAGS=["-Wno-error=#warnings"]) else: # always enable those errors env.Append(CCFLAGS=["-Werror=return-type"]) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index d34ed29691..05fc309a28 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -356,7 +356,7 @@ void _OS::print_all_textures_by_size() { ResourceCache::get_cached_resources(&rsrc); for (Ref<Resource> &res : rsrc) { - if (!res->is_class("ImageTexture")) { + if (!res->is_class("Texture")) { continue; } @@ -376,14 +376,30 @@ void _OS::print_all_textures_by_size() { imgs.sort(); - for (_OSCoreBindImg &E : imgs) { - total -= E.vram; + if (imgs.size() == 0) { + print_line("No textures seem used in this project."); + } else { + print_line("Textures currently in use, sorted by VRAM usage:\n" + "Path - VRAM usage (Dimensions)"); + } + + for (const _OSCoreBindImg &img : imgs) { + print_line(vformat("%s - %s %s", + img.path, + String::humanize_size(img.vram), + img.size)); } + + print_line(vformat("Total VRAM usage: %s.", String::humanize_size(total))); } void _OS::print_resources_by_type(const Vector<String> &p_types) { - Map<String, int> type_count; + ERR_FAIL_COND_MSG(p_types.size() == 0, + "At least one type should be provided to print resources by type."); + + print_line(vformat("Resources currently in use for the following types: %s", p_types)); + Map<String, int> type_count; List<Ref<Resource>> resources; ResourceCache::get_cached_resources(&resources); @@ -404,6 +420,18 @@ void _OS::print_resources_by_type(const Vector<String> &p_types) { } type_count[r->get_class()]++; + + print_line(vformat("%s: %s", r->get_class(), r->get_path())); + + List<StringName> metas; + r->get_meta_list(&metas); + for (const StringName &meta : metas) { + print_line(vformat(" %s: %s", meta, r->get_meta(meta))); + } + } + + for (const KeyValue<String, int> &E : type_count) { + print_line(vformat("%s count: %d", E.key, E.value)); } } @@ -1759,7 +1787,7 @@ void _Thread::_start_func(void *ud) { target_param_count = method->get_argument_count(); target_default_arg_count = method->get_default_argument_count(); } - if (target_param_count >= 1 && target_default_arg_count == target_param_count) { + if (target_param_count >= 1 && target_default_arg_count < target_param_count) { argc = 1; } } diff --git a/core/core_constants.cpp b/core/core_constants.cpp index de15cfd14a..cd8096c610 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -133,6 +133,19 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(VALIGN_CENTER); BIND_CORE_ENUM_CONSTANT(VALIGN_BOTTOM); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP_TO); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER_TO); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM_TO); + + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_TOP); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_CENTER); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BASELINE); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BOTTOM); + + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM); + // huge list of keys BIND_CORE_CONSTANT(SPKEY); diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 6714705bb5..15be0f1e36 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -196,7 +196,7 @@ Array InputMap::_action_get_events(const StringName &p_action) { const List<Ref<InputEvent>> *al = action_get_events(p_action); if (al) { for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) { - ret.push_back(E); + ret.push_back(E->get()); } } diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 727611a573..0262655927 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -552,5 +552,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) { } lock.read_unlock(); +#else + WARN_PRINT("ResourceCache::dump only with in debug builds."); #endif } diff --git a/core/math/math_defs.h b/core/math/math_defs.h index 7692e1be47..c3a8f910c0 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -81,6 +81,26 @@ enum VAlign { VALIGN_BOTTOM }; +enum InlineAlign { + // Image alignment points. + INLINE_ALIGN_TOP_TO = 0b0000, + INLINE_ALIGN_CENTER_TO = 0b0001, + INLINE_ALIGN_BOTTOM_TO = 0b0010, + INLINE_ALIGN_IMAGE_MASK = 0b0011, + + // Text alignment points. + INLINE_ALIGN_TO_TOP = 0b0000, + INLINE_ALIGN_TO_CENTER = 0b0100, + INLINE_ALIGN_TO_BASELINE = 0b1000, + INLINE_ALIGN_TO_BOTTOM = 0b1100, + INLINE_ALIGN_TEXT_MASK = 0b1100, + + // Presets. + INLINE_ALIGN_TOP = INLINE_ALIGN_TOP_TO | INLINE_ALIGN_TO_TOP, + INLINE_ALIGN_CENTER = INLINE_ALIGN_CENTER_TO | INLINE_ALIGN_TO_CENTER, + INLINE_ALIGN_BOTTOM = INLINE_ALIGN_BOTTOM_TO | INLINE_ALIGN_TO_BOTTOM +}; + enum Side { SIDE_LEFT, SIDE_TOP, diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index 3d8e70cec7..cadfdc13d1 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -75,16 +75,24 @@ public: bool operator!=(const Transform3D &p_transform) const; _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const; + _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; + _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const; + + // NOTE: These are UNSAFE with non-uniform scaling, and will produce incorrect results. + // They use the transpose. + // For safe inverse transforms, xform by the affine_inverse. _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const; + _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; + _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const; + // Safe with non-uniform scaling (uses affine_inverse). _FORCE_INLINE_ Plane xform(const Plane &p_plane) const; _FORCE_INLINE_ Plane xform_inv(const Plane &p_plane) const; - _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; - _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; - - _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const; - _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const; + // These fast versions use precomputed affine inverse, and should be used in bottleneck areas where + // multiple planes are to be transformed. + _FORCE_INLINE_ Plane xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const; + static _FORCE_INLINE_ Plane xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose); void operator*=(const Transform3D &p_transform); Transform3D operator*(const Transform3D &p_transform) const; @@ -130,30 +138,20 @@ _FORCE_INLINE_ Vector3 Transform3D::xform_inv(const Vector3 &p_vector) const { (basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (basis.elements[2][2] * v.z)); } +// Neither the plane regular xform or xform_inv are particularly efficient, +// as they do a basis inverse. For xforming a large number +// of planes it is better to pre-calculate the inverse transpose basis once +// and reuse it for each plane, by using the 'fast' version of the functions. _FORCE_INLINE_ Plane Transform3D::xform(const Plane &p_plane) const { - Vector3 point = p_plane.normal * p_plane.d; - Vector3 point_dir = point + p_plane.normal; - point = xform(point); - point_dir = xform(point_dir); - - Vector3 normal = point_dir - point; - normal.normalize(); - real_t d = normal.dot(point); - - return Plane(normal, d); + Basis b = basis.inverse(); + b.transpose(); + return xform_fast(p_plane, b); } _FORCE_INLINE_ Plane Transform3D::xform_inv(const Plane &p_plane) const { - Vector3 point = p_plane.normal * p_plane.d; - Vector3 point_dir = point + p_plane.normal; - point = xform_inv(point); - point_dir = xform_inv(point_dir); - - Vector3 normal = point_dir - point; - normal.normalize(); - real_t d = normal.dot(point); - - return Plane(normal, d); + Transform3D inv = affine_inverse(); + Basis basis_transpose = basis.transposed(); + return xform_inv_fast(p_plane, inv, basis_transpose); } _FORCE_INLINE_ AABB Transform3D::xform(const AABB &p_aabb) const { @@ -231,4 +229,37 @@ Vector<Vector3> Transform3D::xform_inv(const Vector<Vector3> &p_array) const { return array; } +_FORCE_INLINE_ Plane Transform3D::xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = xform(point); + + // Use inverse transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_inverse_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + +_FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose) { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = p_inverse.xform(point); + + // Note that instead of precalculating the transpose, an alternative + // would be to use the transpose for the basis transform. + // However that would be less SIMD friendly (requiring a swizzle). + // So the cost is one extra precalced value in the calling code. + // This is probably worth it, as this could be used in bottleneck areas. And + // where it is not a bottleneck, the non-fast method is fine. + + // Use transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + #endif // TRANSFORM_H diff --git a/core/os/os.cpp b/core/os/os.cpp index cdb570bb73..76a6da51e1 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -178,7 +178,7 @@ static void _OS_printres(Object *p_obj) { return; } - String str = itos(res->get_instance_id()) + String(res->get_class()) + ":" + String(res->get_name()) + " - " + res->get_path(); + String str = vformat("%s - %s - %s", res->to_string(), res->get_name(), res->get_path()); if (_OSPRF) { _OSPRF->store_line(str); } else { diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 19d23fd375..cb7d924556 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -929,6 +929,66 @@ void Translation::_bind_methods() { /////////////////////////////////////////////// +struct _character_accent_pair { + const char32_t character; + const char32_t *accented_character; +}; + +static _character_accent_pair _character_to_accented[] = { + { 'A', U"Å" }, + { 'B', U"ß" }, + { 'C', U"Ç" }, + { 'D', U"Ð" }, + { 'E', U"É" }, + { 'F', U"F́" }, + { 'G', U"Ĝ" }, + { 'H', U"Ĥ" }, + { 'I', U"Ĩ" }, + { 'J', U"Ĵ" }, + { 'K', U"ĸ" }, + { 'L', U"Ł" }, + { 'M', U"Ḿ" }, + { 'N', U"й" }, + { 'O', U"Ö" }, + { 'P', U"Ṕ" }, + { 'Q', U"Q́" }, + { 'R', U"Ř" }, + { 'S', U"Ŝ" }, + { 'T', U"Ŧ" }, + { 'U', U"Ũ" }, + { 'V', U"Ṽ" }, + { 'W', U"Ŵ" }, + { 'X', U"X́" }, + { 'Y', U"Ÿ" }, + { 'Z', U"Ž" }, + { 'a', U"á" }, + { 'b', U"ḅ" }, + { 'c', U"ć" }, + { 'd', U"d́" }, + { 'e', U"é" }, + { 'f', U"f́" }, + { 'g', U"ǵ" }, + { 'h', U"h̀" }, + { 'i', U"í" }, + { 'j', U"ǰ" }, + { 'k', U"ḱ" }, + { 'l', U"ł" }, + { 'm', U"m̀" }, + { 'n', U"ή" }, + { 'o', U"ô" }, + { 'p', U"ṕ" }, + { 'q', U"q́" }, + { 'r', U"ŕ" }, + { 's', U"š" }, + { 't', U"ŧ" }, + { 'u', U"ü" }, + { 'v', U"ṽ" }, + { 'w', U"ŵ" }, + { 'x', U"x́" }, + { 'y', U"ý" }, + { 'z', U"ź" }, +}; + bool TranslationServer::is_locale_valid(const String &p_locale) { const char **ptr = locale_list; @@ -1101,10 +1161,10 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin } if (!res) { - return p_message; + return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message; } - return res; + return pseudolocalization_enabled ? pseudolocalize(res) : res; } StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1217,7 +1277,18 @@ void TranslationServer::setup() { } else { set_locale(OS::get_singleton()->get_locale()); } + fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); + pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); + pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); + pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); + pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); + pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); + expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); + pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); + pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); + #ifdef TOOLS_ENABLED { String options = ""; @@ -1258,10 +1329,10 @@ StringName TranslationServer::tool_translate(const StringName &p_message, const if (tool_translation.is_valid()) { StringName r = tool_translation->get_message(p_message, p_context); if (r) { - return r; + return editor_pseudolocalization ? tool_pseudolocalize(r) : r; } } - return p_message; + return editor_pseudolocalization ? tool_pseudolocalize(p_message) : p_message; } StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1306,6 +1377,181 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message, return p_message_plural; } +bool TranslationServer::is_pseudolocalization_enabled() const { + return pseudolocalization_enabled; +} + +void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { + pseudolocalization_enabled = p_enabled; + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } + ResourceLoader::reload_translation_remaps(); +} + +void TranslationServer::set_editor_pseudolocalization(bool p_enabled) { + editor_pseudolocalization = p_enabled; +} + +void TranslationServer::reload_pseudolocalization() { + pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); + pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); + pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); + pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); + expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); + pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); + pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); + } + ResourceLoader::reload_translation_remaps(); +} + +StringName TranslationServer::pseudolocalize(const StringName &p_message) const { + String message = p_message; + int length = message.length(); + if (pseudolocalization_override_enabled) { + message = get_override_string(message); + } + + if (pseudolocalization_double_vowels_enabled) { + message = double_vowels(message); + } + + if (pseudolocalization_accents_enabled) { + message = replace_with_accented_string(message); + } + + if (pseudolocalization_fake_bidi_enabled) { + message = wrap_with_fakebidi_characters(message); + } + + StringName res = add_padding(message, length); + return res; +} + +StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { + String message = p_message; + message = double_vowels(message); + message = replace_with_accented_string(message); + StringName res = "[!!! " + message + " !!!]"; + return res; +} + +String TranslationServer::get_override_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += '*'; + } + return res; +} + +String TranslationServer::double_vowels(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += p_message[i]; + if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || + p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { + res += p_message[i]; + } + } + return res; +}; + +String TranslationServer::replace_with_accented_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + const char32_t *accented = get_accented_version(p_message[i]); + if (accented) { + res += accented; + } else { + res += p_message[i]; + } + } + return res; +} + +String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { + String res; + char32_t fakebidiprefix = U'\u202e'; + char32_t fakebidisuffix = U'\u202c'; + res += fakebidiprefix; + // The fake bidi unicode gets popped at every newline so pushing it back at every newline. + for (int i = 0; i < p_message.size(); i++) { + if (p_message[i] == '\n') { + res += fakebidisuffix; + res += p_message[i]; + res += fakebidiprefix; + } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += fakebidisuffix; + res += p_message[i]; + res += p_message[i + 1]; + res += fakebidiprefix; + i++; + } else { + res += p_message[i]; + } + } + res += fakebidisuffix; + return res; +} + +String TranslationServer::add_padding(String &p_message, int p_length) const { + String res; + String prefix = pseudolocalization_prefix; + String suffix; + for (int i = 0; i < p_length * expansion_ratio / 2; i++) { + prefix += "_"; + suffix += "_"; + } + suffix += pseudolocalization_suffix; + res += prefix; + res += p_message; + res += suffix; + return res; +} + +const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { + if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) { + return nullptr; + } + + for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { + if (_character_to_accented[i].character == p_character) { + return _character_to_accented[i].accented_character; + } + } + + return nullptr; +} + +bool TranslationServer::is_placeholder(String &p_message, int p_index) const { + return p_message[p_index] == '%' && p_index < p_message.size() - 1 && + (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || + p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); +} + void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); @@ -1322,6 +1568,12 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); + + ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization); + ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); } void TranslationServer::load_translations() { diff --git a/core/string/translation.h b/core/string/translation.h index 72a828227e..4f179ac0fe 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -77,6 +77,26 @@ class TranslationServer : public Object { bool enabled = true; + bool pseudolocalization_enabled = false; + bool pseudolocalization_accents_enabled = false; + bool pseudolocalization_double_vowels_enabled = false; + bool pseudolocalization_fake_bidi_enabled = false; + bool pseudolocalization_override_enabled = false; + bool pseudolocalization_skip_placeholders_enabled = false; + bool editor_pseudolocalization = false; + float expansion_ratio = 0.0; + String pseudolocalization_prefix; + String pseudolocalization_suffix; + + StringName tool_pseudolocalize(const StringName &p_message) const; + String get_override_string(String &p_message) const; + String double_vowels(String &p_message) const; + String replace_with_accented_string(String &p_message) const; + String wrap_with_fakebidi_characters(String &p_message) const; + String add_padding(String &p_message, int p_length) const; + const char32_t *get_accented_version(char32_t p_character) const; + bool is_placeholder(String &p_message, int p_index) const; + static TranslationServer *singleton; bool _load_translations(const String &p_from); @@ -104,6 +124,13 @@ public: StringName translate(const StringName &p_message, const StringName &p_context = "") const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + StringName pseudolocalize(const StringName &p_message) const; + + bool is_pseudolocalization_enabled() const; + void set_pseudolocalization_enabled(bool p_enabled); + void set_editor_pseudolocalization(bool p_enabled); + void reload_pseudolocalization(); + static Vector<String> get_all_locales(); static Vector<String> get_all_locale_names(); static bool is_locale_valid(const String &p_locale); diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 3cb2a6bfb5..001da3ddcb 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -101,6 +101,7 @@ VARIANT_ENUM_CAST(MouseButton); VARIANT_ENUM_CAST(Orientation); VARIANT_ENUM_CAST(HAlign); VARIANT_ENUM_CAST(VAlign); +VARIANT_ENUM_CAST(InlineAlign); VARIANT_ENUM_CAST(PropertyHint); VARIANT_ENUM_CAST(PropertyUsageFlags); VARIANT_ENUM_CAST(Variant::Type); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index c86812742c..7edcf6da82 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1128,6 +1128,36 @@ <constant name="VALIGN_BOTTOM" value="2" enum="VAlign"> Vertical bottom alignment, usually for text-derived classes. </constant> + <constant name="INLINE_ALIGN_TOP_TO" value="0" enum="InlineAlign"> + Aligns the top of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGN_TO_*[/code] constant. + </constant> + <constant name="INLINE_ALIGN_CENTER_TO" value="1" enum="InlineAlign"> + Aligns the center of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGN_TO_*[/code] constant. + </constant> + <constant name="INLINE_ALIGN_BOTTOM_TO" value="2" enum="InlineAlign"> + Aligns the bottom of the inline object (e.g. image, table) to the position of the text specified by [code]INLINE_ALIGN_TO_*[/code] constant. + </constant> + <constant name="INLINE_ALIGN_TO_TOP" value="0" enum="InlineAlign"> + Aligns the position of the inline object (e.g. image, table) specified by [code]INLINE_ALIGN_*_TO[/code] constant to the top of the text. + </constant> + <constant name="INLINE_ALIGN_TO_CENTER" value="4" enum="InlineAlign"> + Aligns the position of the inline object (e.g. image, table) specified by [code]INLINE_ALIGN_*_TO[/code] constant to the center of the text. + </constant> + <constant name="INLINE_ALIGN_TO_BASELINE" value="8" enum="InlineAlign"> + Aligns the position of the inline object (e.g. image, table) specified by [code]INLINE_ALIGN_*_TO[/code] constant to the baseline of the text. + </constant> + <constant name="INLINE_ALIGN_TO_BOTTOM" value="12" enum="InlineAlign"> + Aligns inline object (e.g. image, table) to the bottom of the text. + </constant> + <constant name="INLINE_ALIGN_TOP" value="0" enum="InlineAlign"> + Aligns top of the inline object (e.g. image, table) to the top of the text. Equvalent to [code]INLINE_ALIGN_TOP_TO | INLINE_ALIGN_TO_TOP[/code]. + </constant> + <constant name="INLINE_ALIGN_CENTER" value="5" enum="InlineAlign"> + Aligns center of the inline object (e.g. image, table) to the center of the text. Equvalent to [code]INLINE_ALIGN_CENTER_TO | INLINE_ALIGN_TO_CENTER[/code]. + </constant> + <constant name="INLINE_ALIGN_BOTTOM" value="14" enum="InlineAlign"> + Aligns bottom of the inline object (e.g. image, table) to the bottom of the text. Equvalent to [code]INLINE_ALIGN_BOTTOM_TO | INLINE_ALIGN_TO_BOTTOM[/code]. + </constant> <constant name="SPKEY" value="16777216"> Keycodes with this bit applied are non-printable. </constant> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 12a13ecfcc..66fa57cb52 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -311,8 +311,8 @@ <member name="device" type="String" setter="set_device" getter="get_device" default=""Default""> Name of the current device for audio output (see [method get_device_list]). </member> - <member name="global_rate_scale" type="float" setter="set_global_rate_scale" getter="get_global_rate_scale" default="1.0"> - Scales the rate at which audio is played (i.e. setting it to [code]0.5[/code] will make the audio be played twice as fast). + <member name="playback_speed_scale" type="float" setter="set_playback_speed_scale" getter="get_playback_speed_scale" default="1.0"> + Scales the rate at which audio is played (i.e. setting it to [code]0.5[/code] will make the audio be played at half its speed). </member> </members> <signals> diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml index 085568466a..62fd7a1d6e 100644 --- a/doc/classes/EditorInspectorPlugin.xml +++ b/doc/classes/EditorInspectorPlugin.xml @@ -13,6 +13,7 @@ On each of these calls, the "add" functions can be called. </description> <tutorials> + <link title="Inspector plugins">https://docs.godotengine.org/en/latest/tutorials/plugins/editor/inspector_plugins.html</link> </tutorials> <methods> <method name="_can_handle" qualifiers="virtual"> @@ -56,7 +57,7 @@ <return type="void" /> <argument index="0" name="control" type="Control" /> <description> - Adds a custom control, not necessarily a property editor. + Adds a custom control, which is not necessarily a property editor. </description> </method> <method name="add_property_editor"> @@ -64,7 +65,7 @@ <argument index="0" name="property" type="String" /> <argument index="1" name="editor" type="Control" /> <description> - Adds a property editor, this must inherit [EditorProperty]. + Adds a property editor for an individual property. The [code]editor[/code] control must extend [EditorProperty]. </description> </method> <method name="add_property_editor_for_multiple_properties"> @@ -73,7 +74,7 @@ <argument index="1" name="properties" type="PackedStringArray" /> <argument index="2" name="editor" type="Control" /> <description> - Adds an editor that allows modifying multiple properties, this must inherit [EditorProperty]. + Adds an editor that allows modifying multiple properties. The [code]editor[/code] control must extend [EditorProperty]. </description> </method> </methods> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 41f42568ad..725b0ba8ff 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -47,14 +47,14 @@ <method name="get_tooltip_text" qualifiers="const"> <return type="String" /> <description> - Override if you want to allow a custom tooltip over your property. + Must be implemented to provide a custom tooltip to the property editor. </description> </method> <method name="set_bottom_editor"> <return type="void" /> <argument index="0" name="editor" type="Control" /> <description> - Adds controls with this function if you want them on the bottom (below the label). + Puts the [code]editor[/code] control below the property label. The control must be previously added using [method Node.add_child]. </description> </method> </methods> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 383049fb3e..6eba469e54 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -741,6 +741,34 @@ <member name="internationalization/locale/test" type="String" setter="" getter="" default=""""> If non-empty, this locale will be used when running the project from the editor. </member> + <member name="internationalization/pseudolocalization/double_vowels" type="bool" setter="" getter="" default="false"> + Double vowels in strings during pseudolocalization to simulate the lengthening of text due to localization. + </member> + <member name="internationalization/pseudolocalization/expansion_ratio" type="float" setter="" getter="" default="0.0"> + The expansion ratio to use during pseudolocalization. A value of [code]0.3[/code] is sufficient for most practical purposes, and will increase the length of each string by 30%. + </member> + <member name="internationalization/pseudolocalization/fake_bidi" type="bool" setter="" getter="" default="false"> + If [code]true[/code], emulate bidirectional (right-to-left) text when pseudolocalization is enabled. This can be used to spot issues with RTL layout and UI mirroring that will crop up if the project is localized to RTL languages such as Arabic or Hebrew. + </member> + <member name="internationalization/pseudolocalization/override" type="bool" setter="" getter="" default="false"> + Replace all characters in the string with [code]*[/code]. Useful for finding non-localizable strings. + </member> + <member name="internationalization/pseudolocalization/prefix" type="String" setter="" getter="" default=""[""> + Prefix that will be prepended to the pseudolocalized string. + </member> + <member name="internationalization/pseudolocalization/replace_with_accents" type="bool" setter="" getter="" default="true"> + Replace all characters with their accented variants during pseudolocalization. + </member> + <member name="internationalization/pseudolocalization/skip_placeholders" type="bool" setter="" getter="" default="true"> + Skip placeholders for string formatting like [code]%s[/code] or [code]%f[/code] during pseudolocalization. Useful to identify strings which need additional control characters to display correctly. + </member> + <member name="internationalization/pseudolocalization/suffix" type="String" setter="" getter="" default=""]""> + Suffix that will be appended to the pseudolocalized string. + </member> + <member name="internationalization/pseudolocalization/use_pseudolocalization" type="bool" setter="" getter="" default="false"> + If [code]true[/code], enables pseudolocalization for the project. This can be used to spot untranslatable strings or layout issues that may occur once the project is localized to languages that have longer strings than the source language. + [b]Note:[/b] This property is only read when the project starts. To toggle pseudolocalization at run-time, use [member TranslationServer.pseudolocalization_enabled] instead. + </member> <member name="internationalization/rendering/force_right_to_left_layout_direction" type="bool" setter="" getter="" default="false"> Force layout direction and text writing direction to RTL for all locales. </member> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index c017bcaa1a..43778df195 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -1148,7 +1148,7 @@ </constant> <constant name="TEXTURE_USAGE_CAN_COPY_TO_BIT" value="256" enum="TextureUsageBits"> </constant> - <constant name="TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT" value="512" enum="TextureUsageBits"> + <constant name="TEXTURE_USAGE_INPUT_ATTACHMENT_BIT" value="512" enum="TextureUsageBits"> </constant> <constant name="TEXTURE_SWIZZLE_IDENTITY" value="0" enum="TextureSwizzle"> </constant> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 538e93d505..7bbcc5e0b5 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -21,7 +21,7 @@ <argument index="1" name="width" type="int" default="0" /> <argument index="2" name="height" type="int" default="0" /> <argument index="3" name="color" type="Color" default="Color(1, 1, 1, 1)" /> - <argument index="4" name="inline_align" type="int" enum="VAlign" default="0" /> + <argument index="4" name="inline_align" type="int" enum="InlineAlign" default="5" /> <description> Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image and a [code]color[/code] to tint the image. If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio. @@ -288,7 +288,7 @@ <method name="push_table"> <return type="void" /> <argument index="0" name="columns" type="int" /> - <argument index="1" name="inline_align" type="int" enum="VAlign" default="0" /> + <argument index="1" name="inline_align" type="int" enum="InlineAlign" default="0" /> <description> Adds a [code][table=columns,inline_align][/code] tag to the tag stack. </description> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 027ae2000a..0376a3f96e 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -89,7 +89,7 @@ <return type="int" /> <argument index="0" name="to" type="String" /> <description> - Performs a case-sensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/code] of each string, which roughly matches the alphabetical order. + Performs a case-sensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. [b]Behavior with different string lengths:[/b] Returns [code]1[/code] if the "base" string is longer than the [code]to[/code] string or [code]-1[/code] if the "base" string is shorter than the [code]to[/code] string. Keep in mind this length is determined by the number of Unicode codepoints, [i]not[/i] the actual visible characters. [b]Behavior with empty strings:[/b] Returns [code]-1[/code] if the "base" string is empty, [code]1[/code] if the [code]to[/code] string is empty or [code]0[/code] if both strings are empty. To get a boolean result from a string comparison, use the [code]==[/code] operator instead. See also [method nocasecmp_to] and [method naturalnocasecmp_to]. @@ -388,7 +388,7 @@ <return type="int" /> <argument index="0" name="to" type="String" /> <description> - Performs a case-insensitive [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/code] of each string, which roughly matches the alphabetical order. Internally, lowercase characters will be converted to uppercase during the comparison. + Performs a case-insensitive [i]natural order[/i] comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters will be converted to uppercase during the comparison. When used for sorting, natural order comparison will order suites of numbers as expected by most people. If you sort the numbers from 1 to 10 using natural order, you will get [code][1, 2, 3, ...][/code] instead of [code][1, 10, 2, 3, ...][/code]. [b]Behavior with different string lengths:[/b] Returns [code]1[/code] if the "base" string is longer than the [code]to[/code] string or [code]-1[/code] if the "base" string is shorter than the [code]to[/code] string. Keep in mind this length is determined by the number of Unicode codepoints, [i]not[/i] the actual visible characters. [b]Behavior with empty strings:[/b] Returns [code]-1[/code] if the "base" string is empty, [code]1[/code] if the [code]to[/code] string is empty or [code]0[/code] if both strings are empty. @@ -399,7 +399,7 @@ <return type="int" /> <argument index="0" name="to" type="String" /> <description> - Performs a case-insensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/code] of each string, which roughly matches the alphabetical order. Internally, lowercase characters will be converted to uppercase during the comparison. + Performs a case-insensitive comparison to another string. Returns [code]-1[/code] if less than, [code]1[/code] if greater than, or [code]0[/code] if equal. "less than" or "greater than" are determined by the [url=https://en.wikipedia.org/wiki/List_of_Unicode_characters]Unicode code points[/url] of each string, which roughly matches the alphabetical order. Internally, lowercase characters will be converted to uppercase during the comparison. [b]Behavior with different string lengths:[/b] Returns [code]1[/code] if the "base" string is longer than the [code]to[/code] string or [code]-1[/code] if the "base" string is shorter than the [code]to[/code] string. Keep in mind this length is determined by the number of Unicode codepoints, [i]not[/i] the actual visible characters. [b]Behavior with empty strings:[/b] Returns [code]-1[/code] if the "base" string is empty, [code]1[/code] if the [code]to[/code] string is empty or [code]0[/code] if both strings are empty. To get a boolean result from a string comparison, use the [code]==[/code] operator instead. See also [method casecmp_to] and [method naturalnocasecmp_to]. diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index cc66f56d65..daa7298418 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -13,7 +13,7 @@ <return type="bool" /> <argument index="0" name="key" type="Variant" /> <argument index="1" name="size" type="Vector2" /> - <argument index="2" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="2" name="inline_align" type="int" enum="InlineAlign" default="5" /> <argument index="3" name="length" type="int" default="1" /> <description> Adds inline object to the text buffer, [code]key[/code] must be unique. In the text, object is represented as [code]length[/code] object replacement characters. @@ -122,7 +122,7 @@ <return type="bool" /> <argument index="0" name="key" type="Variant" /> <argument index="1" name="size" type="Vector2" /> - <argument index="2" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="2" name="inline_align" type="int" enum="InlineAlign" default="5" /> <description> Sets new size and alignment of embedded object. </description> diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index 9050f9246a..811fe32eb2 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -13,7 +13,7 @@ <return type="bool" /> <argument index="0" name="key" type="Variant" /> <argument index="1" name="size" type="Vector2" /> - <argument index="2" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="2" name="inline_align" type="int" enum="InlineAlign" default="5" /> <argument index="3" name="length" type="int" default="1" /> <description> Adds inline object to the text buffer, [code]key[/code] must be unique. In the text, object is represented as [code]length[/code] object replacement characters. @@ -240,7 +240,7 @@ <return type="bool" /> <argument index="0" name="key" type="Variant" /> <argument index="1" name="size" type="Vector2" /> - <argument index="2" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="2" name="inline_align" type="int" enum="InlineAlign" default="5" /> <description> Sets new size and alignment of embedded object. </description> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index dbf7ae93aa..ac56be4392 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -541,7 +541,7 @@ <argument index="0" name="shaped" type="RID" /> <argument index="1" name="key" type="Variant" /> <argument index="2" name="size" type="Vector2" /> - <argument index="3" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="3" name="inline_align" type="int" enum="InlineAlign" default="5" /> <argument index="4" name="length" type="int" default="1" /> <description> Adds inline object to the text buffer, [code]key[/code] must be unique. In the text, object is represented as [code]length[/code] object replacement characters. @@ -817,7 +817,7 @@ <argument index="0" name="shaped" type="RID" /> <argument index="1" name="key" type="Variant" /> <argument index="2" name="size" type="Vector2" /> - <argument index="3" name="inline_align" type="int" enum="VAlign" default="1" /> + <argument index="3" name="inline_align" type="int" enum="InlineAlign" default="5" /> <description> Sets new size and alignment of embedded object. </description> diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml index 655c16b0cd..029848be33 100644 --- a/doc/classes/TranslationServer.xml +++ b/doc/classes/TranslationServer.xml @@ -51,6 +51,19 @@ It will return a [code]nullptr[/code] if there is no [Translation] instance that matches the [code]locale[/code]. </description> </method> + <method name="pseudolocalize" qualifiers="const"> + <return type="StringName" /> + <argument index="0" name="message" type="StringName" /> + <description> + Returns the pseudolocalized string based on the [code]p_message[/code] passed in. + </description> + </method> + <method name="reload_pseudolocalization"> + <return type="void" /> + <description> + Reparses the pseudolocalization options and reloads the translation. + </description> + </method> <method name="remove_translation"> <return type="void" /> <argument index="0" name="translation" type="Translation" /> @@ -85,6 +98,11 @@ </description> </method> </methods> + <members> + <member name="pseudolocalization_enabled" type="bool" setter="set_pseudolocalization_enabled" getter="is_pseudolocalization_enabled" default="false"> + If [code]true[/code], enables the use of pseudolocalization. See [member ProjectSettings.internationalization/pseudolocalization/use_pseudolocalization] for details. + </member> + </members> <constants> </constants> </class> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 9ee2a28240..b5bd6cef38 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1804,6 +1804,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + if (p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + } + if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } @@ -2134,6 +2138,10 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID } } + if (texture.usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) { + usage_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + } + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { usage_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } @@ -3275,8 +3283,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF for (int i = 0; i < p_attachments.size(); i++) { ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)), - VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set."); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)), + VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set."); VkAttachmentDescription description = {}; description.flags = 0; @@ -3473,7 +3481,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; } else { ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), input attachment (" + itos(j) + ")."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as input attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture."); ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); reference.attachment = attachment; reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -3497,12 +3505,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } else { ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ")."); ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused."); - ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as resolve attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color texture."); ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); reference.attachment = attachment; - reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } resolve_references.push_back(reference); @@ -3632,8 +3640,10 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF render_pass_create_info.pDependencies = nullptr; } + // These are only used if we use multiview but we need to define them in scope. const uint32_t view_mask = (1 << p_view_count) - 1; const uint32_t correlation_mask = (1 << p_view_count) - 1; + Vector<uint32_t> view_masks; VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; if (p_view_count > 1) { @@ -3645,10 +3655,15 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF // Make sure we limit this to the number of views we support. ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + // Set view masks for each subpass + for (uint32_t i = 0; i < subpasses.size(); i++) { + view_masks.push_back(view_mask); + }; + render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; render_pass_multiview_create_info.pNext = nullptr; - render_pass_multiview_create_info.subpassCount = 1; - render_pass_multiview_create_info.pViewMasks = &view_mask; + render_pass_multiview_create_info.subpassCount = subpasses.size(); + render_pass_multiview_create_info.pViewMasks = view_masks.ptr(); render_pass_multiview_create_info.dependencyCount = 0; render_pass_multiview_create_info.pViewOffsets = nullptr; render_pass_multiview_create_info.correlationMaskCount = 1; @@ -4506,6 +4521,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve } break; case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { info.type = UNIFORM_TYPE_INPUT_ATTACHMENT; + need_array_dimensions = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: { ERR_PRINT("Acceleration structure not supported."); @@ -5490,7 +5506,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.sampler = *sampler; img_info.imageView = texture->view; - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j + 1]; @@ -5543,7 +5559,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.sampler = VK_NULL_HANDLE; img_info.imageView = texture->view; - if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) { + if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j]; @@ -7416,6 +7432,10 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) { vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor); } +uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() { + return draw_list_current_subpass; +} + RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() { ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID); ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index f9ff61310a..dc4583d837 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1149,6 +1149,7 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect); virtual void draw_list_disable_scissor(DrawListID p_list); + virtual uint32_t draw_list_get_current_pass(); virtual DrawListID draw_list_switch_to_next_pass(); virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index c9d3df9ce2..f40dc3c64a 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3418,6 +3418,7 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) { if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) { //potential new key, does not exist if (num_tracks == 1) { + // TRANSLATORS: %s will be replaced by a phrase describing the target of track. insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?"), p_id.query)); } else { insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?"), num_tracks)); @@ -3525,7 +3526,8 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_ id.track_idx = track_idx; id.value = p_xform; id.type = Animation::TYPE_TRANSFORM3D; - id.query = "node '" + p_node->get_name() + "'"; + // TRANSLATORS: This describes the target of new animation track, will be inserted into another string. + id.query = vformat(TTR("node '%s'"), p_node->get_name()); id.advance = false; //dialog insert @@ -3547,7 +3549,8 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant id.track_idx = i; id.value = p_value; id.type = Animation::TYPE_ANIMATION; - id.query = "animation"; + // TRANSLATORS: This describes the target of new animation track, will be inserted into another string. + id.query = TTR("animation"); id.advance = false; //dialog insert _query_insert(id); @@ -3560,7 +3563,7 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant id.track_idx = -1; id.value = p_value; id.type = Animation::TYPE_ANIMATION; - id.query = "animation"; + id.query = TTR("animation"); id.advance = false; //dialog insert _query_insert(id); @@ -3609,7 +3612,8 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p id.track_idx = i; id.value = p_value; id.type = Animation::TYPE_VALUE; - id.query = "property '" + p_property + "'"; + // TRANSLATORS: This describes the target of new animation track, will be inserted into another string. + id.query = vformat(TTR("property '%s'"), p_property); id.advance = false; //dialog insert _query_insert(id); @@ -3639,7 +3643,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p id.track_idx = i; id.value = value; id.type = Animation::TYPE_BEZIER; - id.query = "property '" + p_property + "'"; + id.query = vformat(TTR("property '%s'"), p_property); id.advance = false; //dialog insert _query_insert(id); @@ -3655,7 +3659,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p id.track_idx = -1; id.value = p_value; id.type = Animation::TYPE_VALUE; - id.query = "property '" + p_property + "'"; + id.query = vformat(TTR("property '%s'"), p_property); id.advance = false; //dialog insert _query_insert(id); @@ -3708,7 +3712,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari id.track_idx = i; id.value = p_value; id.type = Animation::TYPE_VALUE; - id.query = "property '" + p_property + "'"; + id.query = vformat(TTR("property '%s'"), p_property); id.advance = p_advance; //dialog insert _query_insert(id); @@ -3733,7 +3737,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari id.track_idx = i; id.value = value; id.type = Animation::TYPE_BEZIER; - id.query = "property '" + p_property + "'"; + id.query = vformat(TTR("property '%s'"), p_property); id.advance = p_advance; //dialog insert _query_insert(id); @@ -3747,7 +3751,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari id.track_idx = -1; id.value = p_value; id.type = Animation::TYPE_VALUE; - id.query = "property '" + p_property + "'"; + id.query = vformat(TTR("property '%s'"), p_property); id.advance = p_advance; //dialog insert _query_insert(id); diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index 72a0c353e8..84a9237a96 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -563,7 +563,7 @@ void EditorFeatureProfileManager::_class_list_item_selected() { int feature_id = md; String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature(feature_id)); - description_bit->set_text(feature_description); + description_bit->set_text(TTRGET(feature_description)); return; } else { return; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index d7b7e8e52d..679f2e8ce4 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1907,8 +1907,7 @@ void EditorInspector::update_tree() { } Vector<String> slices = propname.operator String().split("/"); - if (slices.size() == 2 && slices[0].begins_with("custom_")) { - // Likely a theme property. + if (slices.size() == 2 && slices[0].begins_with("theme_override_")) { for (int i = 0; i < F->get().theme_properties.size(); i++) { if (F->get().theme_properties[i].name == slices[1]) { descr = DTR(F->get().theme_properties[i].description); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 78500ab16c..2126a7b8be 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4944,6 +4944,16 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { scene_tabs_context_menu->set_position(mb->get_global_position()); scene_tabs_context_menu->popup(); } + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) { + int previous_tab = editor_data.get_edited_scene() - 1; + previous_tab = previous_tab >= 0 ? previous_tab : editor_data.get_edited_scene_count() - 1; + _scene_tab_changed(previous_tab); + } + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) { + int next_tab = editor_data.get_edited_scene() + 1; + next_tab %= editor_data.get_edited_scene_count(); + _scene_tab_changed(next_tab); + } } } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index fa03d281f2..789cabea9a 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2854,6 +2854,10 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const EditorScriptPicker *script_picker = memnew(EditorScriptPicker); script_picker->set_script_owner(Object::cast_to<Node>(p_object)); resource_picker = script_picker; + } else if (p_path == "shader" && p_base_type == "Shader" && Object::cast_to<ShaderMaterial>(p_object)) { + EditorShaderPicker *shader_picker = memnew(EditorShaderPicker); + shader_picker->set_edited_material(Object::cast_to<ShaderMaterial>(p_object)); + resource_picker = shader_picker; } else { resource_picker = memnew(EditorResourcePicker); } diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index f3965fe7de..a4ab749db4 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -847,6 +847,8 @@ EditorResourcePicker::EditorResourcePicker() { edit_button->connect("gui_input", callable_mp(this, &EditorResourcePicker::_button_input)); } +// EditorScriptPicker + void EditorScriptPicker::set_create_options(Object *p_menu_node) { PopupMenu *menu_node = Object::cast_to<PopupMenu>(p_menu_node); if (!menu_node) { @@ -895,3 +897,42 @@ void EditorScriptPicker::_bind_methods() { EditorScriptPicker::EditorScriptPicker() { } + +// EditorShaderPicker + +void EditorShaderPicker::set_create_options(Object *p_menu_node) { + PopupMenu *menu_node = Object::cast_to<PopupMenu>(p_menu_node); + if (!menu_node) { + return; + } + + menu_node->add_icon_item(get_theme_icon("Shader", "EditorIcons"), TTR("New Shader"), OBJ_MENU_NEW_SHADER); + menu_node->add_separator(); +} + +bool EditorShaderPicker::handle_menu_selected(int p_which) { + Ref<ShaderMaterial> material = Ref<ShaderMaterial>(get_edited_material()); + + switch (p_which) { + case OBJ_MENU_NEW_SHADER: { + if (material.is_valid()) { + EditorNode::get_singleton()->get_scene_tree_dock()->open_shader_dialog(material); + return true; + } + } break; + default: + break; + } + return false; +} + +void EditorShaderPicker::set_edited_material(ShaderMaterial *p_material) { + edited_material = p_material; +} + +ShaderMaterial *EditorShaderPicker::get_edited_material() const { + return edited_material; +} + +EditorShaderPicker::EditorShaderPicker() { +} diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index a4c3006c02..d77c31f831 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -144,4 +144,23 @@ public: EditorScriptPicker(); }; +class EditorShaderPicker : public EditorResourcePicker { + GDCLASS(EditorShaderPicker, EditorResourcePicker); + + enum ExtraMenuOption { + OBJ_MENU_NEW_SHADER = 10, + }; + + ShaderMaterial *edited_material = nullptr; + +public: + virtual void set_create_options(Object *p_menu_node) override; + virtual bool handle_menu_selected(int p_which) override; + + void set_edited_material(ShaderMaterial *p_material); + ShaderMaterial *get_edited_material() const; + + EditorShaderPicker(); +}; + #endif // EDITOR_RESOURCE_PICKER_H diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 29ac836834..b1cd53092d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -374,6 +374,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/editor/display_scale", 0); // Display what the Auto display scale setting effectively corresponds to. float scale = get_auto_display_scale(); + + _initial_set("interface/editor/enable_debugging_pseudolocalization", false); + set_restart_if_changed("interface/editor/enable_debugging_pseudolocalization", true); + // Use pseudolocalization in editor. + hints["interface/editor/display_scale"] = PropertyInfo(Variant::INT, "interface/editor/display_scale", PROPERTY_HINT_ENUM, vformat("Auto (%d%%),75%%,100%%,125%%,150%%,175%%,200%%,Custom", Math::round(scale * 100)), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/custom_display_scale", 1.0f); hints["interface/editor/custom_display_scale"] = PropertyInfo(Variant::FLOAT, "interface/editor/custom_display_scale", PROPERTY_HINT_RANGE, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); @@ -953,11 +958,11 @@ fail: } void EditorSettings::setup_language() { + TranslationServer::get_singleton()->set_editor_pseudolocalization(get("interface/editor/enable_debugging_pseudolocalization")); String lang = get("interface/editor/editor_language"); if (lang == "en") { return; // Default, nothing to do. } - // Load editor translation for configured/detected locale. EditorTranslationList *etl = _editor_translations; while (etl->data) { diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 699957897f..b646b3361d 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -889,8 +889,8 @@ ExportTemplateManager::ExportTemplateManager() { request_mirrors->connect("request_completed", callable_mp(this, &ExportTemplateManager::_refresh_mirrors_completed)); mirror_options_button = memnew(MenuButton); - mirror_options_button->get_popup()->add_item("Open in Web Browser", VISIT_WEB_MIRROR); - mirror_options_button->get_popup()->add_item("Copy Mirror URL", COPY_MIRROR_URL); + mirror_options_button->get_popup()->add_item(TTR("Open in Web Browser"), VISIT_WEB_MIRROR); + mirror_options_button->get_popup()->add_item(TTR("Copy Mirror URL"), COPY_MIRROR_URL); download_install_hb->add_child(mirror_options_button); mirror_options_button->get_popup()->connect("id_pressed", callable_mp(this, &ExportTemplateManager::_mirror_options_button_cbk)); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 46e15d97bf..b8cbaaf7c1 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -145,7 +145,7 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id) const { return gizmo_plugin->get_handle_value(this, p_id); } -void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) { if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { get_script_instance()->call("_set_handle", p_id, p_camera, p_point); return; @@ -155,7 +155,7 @@ void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p gizmo_plugin->set_handle(this, p_id, p_camera, p_point); } -void EditorNode3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) const { +void EditorNode3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) { if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { get_script_instance()->call("_commit_handle", p_id, p_restore, p_cancel); return; @@ -196,7 +196,7 @@ Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const { return gizmo_plugin->get_subgizmo_transform(this, p_id); } -void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) const { +void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) { if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) { get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform); return; @@ -206,7 +206,7 @@ void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform gizmo_plugin->set_subgizmo_transform(this, p_id, p_transform); } -void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) const { +void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) { if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) { Array ids; for (int i = 0; i < p_ids.size(); i++) { @@ -1145,13 +1145,13 @@ Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_giz return Variant(); } -void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { get_script_instance()->call("_set_handle", p_gizmo, p_id, p_camera, p_point); } } -void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { get_script_instance()->call("_commit_handle", p_gizmo, p_id, p_restore, p_cancel); } @@ -1184,13 +1184,13 @@ Transform3D EditorNode3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGi return Transform3D(); } -void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) const { +void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) { if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) { get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform); } } -void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) const { +void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) { if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) { Array ids; for (int i = 0; i < p_ids.size(); i++) { @@ -1310,7 +1310,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec return Math::rad2deg(a); } -void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node()); Transform3D gt = light->get_global_transform(); Transform3D gi = gt.affine_inverse(); @@ -1354,7 +1354,7 @@ void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, } } -void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node()); if (p_cancel) { light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); @@ -1538,7 +1538,7 @@ Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo return player->get_emission_angle(); } -void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); Transform3D gt = player->get_global_transform(); @@ -1575,7 +1575,7 @@ void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo } } -void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); if (p_cancel) { @@ -1684,7 +1684,7 @@ Variant Camera3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, } } -void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node()); Transform3D gt = camera->get_global_transform(); @@ -1713,7 +1713,7 @@ void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, } } -void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node()); if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { @@ -2572,7 +2572,7 @@ Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo return Variant(soft_body->is_point_pinned(p_id)); } -void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); soft_body->pin_point_toggle(p_id); } @@ -2628,7 +2628,7 @@ Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(const EditorNode3 return notifier->get_aabb(); } -void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_spatial_node()); Transform3D gt = notifier->get_global_transform(); @@ -2680,7 +2680,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p } } -void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_spatial_node()); if (p_cancel) { @@ -2820,7 +2820,7 @@ Variant GPUParticles3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_g return particles->get_visibility_aabb(); } -void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_spatial_node()); Transform3D gt = particles->get_global_transform(); @@ -2871,7 +2871,7 @@ void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int } } -void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_spatial_node()); if (p_cancel) { @@ -2985,7 +2985,7 @@ Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(const EditorNode3DG return Variant(); } -void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { Node3D *sn = p_gizmo->get_spatial_node(); Transform3D gt = sn->get_global_transform(); @@ -3031,7 +3031,7 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_g } } -void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { Node3D *sn = p_gizmo->get_spatial_node(); if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) { @@ -3245,7 +3245,7 @@ Variant ReflectionProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_ return AABB(probe->get_extents(), probe->get_origin_offset()); } -void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); Transform3D gt = probe->get_global_transform(); @@ -3302,7 +3302,7 @@ void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, in } } -void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); AABB restore = p_restore; @@ -3424,7 +3424,7 @@ Variant DecalGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int return decal->get_extents(); } -void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node()); Transform3D gt = decal->get_global_transform(); @@ -3455,7 +3455,7 @@ void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Ca decal->set_extents(extents); } -void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node()); Vector3 restore = p_restore; @@ -3564,7 +3564,7 @@ Variant VoxelGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, i return probe->get_extents(); } -void VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_spatial_node()); Transform3D gt = probe->get_global_transform(); @@ -3595,7 +3595,7 @@ void VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, probe->set_extents(extents); } -void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_spatial_node()); Vector3 restore = p_restore; @@ -3723,10 +3723,10 @@ Variant LightmapGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo return Variant(); } -void LightmapGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void LightmapGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { } -void LightmapGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void LightmapGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { } bool LightmapGIGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -3905,10 +3905,10 @@ Variant LightmapProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gi return Variant(); } -void LightmapProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void LightmapProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { } -void LightmapProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void LightmapProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { } bool LightmapProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -4124,7 +4124,7 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p return Variant(); } -void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node()); Ref<Shape3D> s = cs->get_shape(); @@ -4241,7 +4241,7 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i } } -void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node()); Ref<Shape3D> s = cs->get_shape(); diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index f303a61607..53f602460c 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -91,14 +91,14 @@ public: virtual bool is_handle_highlighted(int p_id) const; virtual String get_handle_name(int p_id) const; virtual Variant get_handle_value(int p_id) const; - virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const; - virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false) const; + virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point); + virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false); virtual int subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const; virtual Vector<int> subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const; virtual Transform3D get_subgizmo_transform(int p_id) const; - virtual void set_subgizmo_transform(int p_id, Transform3D p_transform) const; - virtual void commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) const; + virtual void set_subgizmo_transform(int p_id, Transform3D p_transform); + virtual void commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false); void set_selected(bool p_selected) { selected = p_selected; } bool is_selected() const { return selected; } @@ -161,14 +161,14 @@ public: virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const; virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const; virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const; - virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const; - virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const; + virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point); + virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false); virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const; virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const; virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const; - virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) const; - virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) const; + virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform); + virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false); Ref<EditorNode3DGizmo> get_gizmo(Node3D *p_spatial); void set_state(int p_state); @@ -189,8 +189,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; void redraw(EditorNode3DGizmo *p_gizmo) override; Light3DGizmoPlugin(); @@ -206,8 +206,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; void redraw(EditorNode3DGizmo *p_gizmo) override; AudioStreamPlayer3DGizmoPlugin(); @@ -223,8 +223,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; void redraw(EditorNode3DGizmo *p_gizmo) override; Camera3DGizmoPlugin(); @@ -355,7 +355,7 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const override; SoftBody3DGizmoPlugin(); @@ -372,8 +372,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; VisibleOnScreenNotifier3DGizmoPlugin(); }; @@ -402,8 +402,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; GPUParticles3DGizmoPlugin(); }; @@ -419,8 +419,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; GPUParticlesCollision3DGizmoPlugin(); }; @@ -436,8 +436,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; ReflectionProbeGizmoPlugin(); }; @@ -453,8 +453,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; DecalGizmoPlugin(); }; @@ -470,8 +470,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; VoxelGIGizmoPlugin(); }; @@ -487,8 +487,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; LightmapGIGizmoPlugin(); }; @@ -504,8 +504,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; LightmapProbeGizmoPlugin(); }; @@ -533,8 +533,8 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override; CollisionShape3DGizmoPlugin(); }; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 5ed8205475..931c50fc44 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -122,31 +122,28 @@ void ViewportRotationControl::_draw() { } void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) { - bool focused = focused_axis == p_axis.axis; - bool positive = p_axis.axis < 3; - bool front = (Math::abs(p_axis.z_axis) <= 0.001 && positive) || p_axis.z_axis > 0.001; - int direction = p_axis.axis % 3; + const bool focused = focused_axis == p_axis.axis; + const bool positive = p_axis.axis < 3; + const int direction = p_axis.axis % 3; - Color axis_color = axis_colors[direction]; - - if (!front) { - axis_color = axis_color.darkened(0.4); - } - Color c = focused ? Color(0.9, 0.9, 0.9) : axis_color; + const Color axis_color = axis_colors[direction]; + const double alpha = focused ? 1.0 : ((p_axis.z_axis + 1.0) / 2.0) * 0.5 + 0.5; + const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color.r, axis_color.g, axis_color.b, alpha); if (positive) { - Vector2i center = get_size() / 2.0; + // Draw axis lines for the positive axes. + const Vector2i center = get_size() / 2.0; draw_line(center, p_axis.screen_point, c, 1.5 * EDSCALE); - } - if (front) { draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c); - if (positive) { - String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z"); - draw_char(get_theme_font(SNAME("rotation_control"), SNAME("EditorFonts")), p_axis.screen_point + Vector2i(-4, 5) * EDSCALE, axis_name, "", get_theme_font_size(SNAME("rotation_control_size"), SNAME("EditorFonts")), Color(0.0, 0.0, 0.0)); - } + + // Draw the axis letter for the positive axes. + const String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z"); + draw_char(get_theme_font(SNAME("rotation_control"), SNAME("EditorFonts")), p_axis.screen_point + Vector2i(-4, 5) * EDSCALE, axis_name, "", get_theme_font_size(SNAME("rotation_control_size"), SNAME("EditorFonts")), Color(0.0, 0.0, 0.0, alpha)); } else { - draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * (0.55 + (0.2 * (1.0 + p_axis.z_axis))), c); + // Draw an outline around the negative axes. + draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c); + draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * 0.8, c.darkened(0.4)); } } diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 63b89aea35..13f7908170 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -88,7 +88,7 @@ Variant Path3DGizmo::get_handle_value(int p_id) const { return ofs; } -void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) { Ref<Curve3D> c = path->get_curve(); if (c.is_null()) { return; @@ -157,7 +157,7 @@ void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point } } -void Path3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) const { +void Path3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) { Ref<Curve3D> c = path->get_curve(); if (c.is_null()) { return; diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h index 5902500526..b74d7cc59e 100644 --- a/editor/plugins/path_3d_editor_plugin.h +++ b/editor/plugins/path_3d_editor_plugin.h @@ -47,8 +47,8 @@ class Path3DGizmo : public EditorNode3DGizmo { public: virtual String get_handle_name(int p_idx) const override; virtual Variant get_handle_value(int p_id) const override; - virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false) const override; + virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) override; + virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false) override; virtual void redraw() override; Path3DGizmo(Path3D *p_path = nullptr); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 452ad126b3..9a41a100bd 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1007,6 +1007,7 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { const Dictionary vs_version = visual_shader->get_engine_version(); if (!vs_version.has_all(components)) { visual_shader->update_engine_version(engine_version); + print_line(vformat(TTR("The shader (\"%s\") has been updated to correspond Godot %s.%s version."), visual_shader->get_path(), engine_version["major"], engine_version["minor"])); } else { for (int i = 0; i < components.size(); i++) { if (vs_version[components[i]] != engine_version[components[i]]) { diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index a1deae92a4..96fcb4fe29 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -351,51 +351,64 @@ void PropertySelector::_item_selected() { String class_type; if (type != Variant::NIL) { class_type = Variant::get_type_name(type); - - } else { + } else if (base_type != String()) { class_type = base_type; + } else if (instance) { + class_type = instance->get_class(); } DocTools *dd = EditorHelp::get_doc_data(); String text; - if (properties) { - String at_class = class_type; - - while (at_class != String()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + while (class_type != String()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); if (E) { for (int i = 0; i < E->get().properties.size(); i++) { if (E->get().properties[i].name == name) { text = DTR(E->get().properties[i].description); + break; } } } - at_class = ClassDB::get_parent_class(at_class); + if (text != String()) { + break; + } + + // The property may be from a parent class, keep looking. + class_type = ClassDB::get_parent_class(class_type); } } else { - String at_class = class_type; - - while (at_class != String()) { - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + while (class_type != String()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); if (E) { for (int i = 0; i < E->get().methods.size(); i++) { if (E->get().methods[i].name == name) { text = DTR(E->get().methods[i].description); + break; } } } - at_class = ClassDB::get_parent_class(at_class); + if (text != String()) { + break; + } + + // The method may be from a parent class, keep looking. + class_type = ClassDB::get_parent_class(class_type); } } - if (text == String()) { - return; + if (text != String()) { + // Display both property name and description, since the help bit may be displayed + // far away from the location (especially if the dialog was resized to be taller). + help_bit->set_text(vformat("[b]%s[/b]: %s", name, text)); + help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 1)); + } else { + // Use nested `vformat()` as translators shouldn't interfere with BBCode tags. + help_bit->set_text(vformat(TTR("No description available for %s."), vformat("[b]%s[/b]", name))); + help_bit->get_rich_text()->set_self_modulate(Color(1, 1, 1, 0.5)); } - - help_bit->set_text(text); } void PropertySelector::_hide_requested() { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index c4d47c7594..8994adf112 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -45,6 +45,7 @@ #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" +#include "editor/shader_create_dialog.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" #include "servers/display_server.h" @@ -1939,12 +1940,31 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { _update_script_button(); } +void SceneTreeDock::_shader_created(Ref<Shader> p_shader) { + if (selected_shader_material.is_null()) { + return; + } + + Ref<Shader> existing = selected_shader_material->get_shader(); + + editor_data->get_undo_redo().create_action(TTR("Set Shader")); + editor_data->get_undo_redo().add_do_method(selected_shader_material.ptr(), "set_shader", p_shader); + editor_data->get_undo_redo().add_undo_method(selected_shader_material.ptr(), "set_shader", existing); + editor_data->get_undo_redo().commit_action(); +} + void SceneTreeDock::_script_creation_closed() { script_create_dialog->disconnect("script_created", callable_mp(this, &SceneTreeDock::_script_created)); script_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed)); script_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed)); } +void SceneTreeDock::_shader_creation_closed() { + shader_create_dialog->disconnect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created)); + shader_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); + shader_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); +} + void SceneTreeDock::_toggle_editable_children_from_selection() { List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); @@ -2896,6 +2916,42 @@ void SceneTreeDock::open_script_dialog(Node *p_for_node, bool p_extend) { } } +void SceneTreeDock::attach_shader_to_selected() { + if (selected_shader_material.is_null()) { + return; + } + + String path = selected_shader_material->get_path(); + if (path == "") { + String root_path; + if (editor_data->get_edited_scene_root()) { + root_path = editor_data->get_edited_scene_root()->get_filename(); + } + String shader_name; + if (selected_shader_material->get_name().is_empty()) { + shader_name = root_path.get_file(); + } else { + shader_name = selected_shader_material->get_name(); + } + if (root_path == "") { + path = String("res://").plus_file(shader_name); + } else { + path = root_path.get_base_dir().plus_file(shader_name); + } + } + + shader_create_dialog->connect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created)); + shader_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); + shader_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); + shader_create_dialog->config(path); + shader_create_dialog->popup_centered(); +} + +void SceneTreeDock::open_shader_dialog(Ref<ShaderMaterial> &p_for_material) { + selected_shader_material = p_for_material; + attach_shader_to_selected(); +} + void SceneTreeDock::open_add_child_dialog() { create_dialog->set_base_type("CanvasItem"); _tool_selected(TOOL_NEW, true); @@ -3267,6 +3323,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel script_create_dialog->set_inheritance_base_type("Node"); add_child(script_create_dialog); + shader_create_dialog = memnew(ShaderCreateDialog); + add_child(shader_create_dialog); + reparent_dialog = memnew(ReparentDialog); add_child(reparent_dialog); reparent_dialog->connect("reparent", callable_mp(this, &SceneTreeDock::_node_reparent)); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 4952122cb7..ccdc0a3786 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -49,6 +49,7 @@ #include "scene_tree_editor.h" class EditorNode; +class ShaderCreateDialog; class SceneTreeDock : public VBoxContainer { GDCLASS(SceneTreeDock, VBoxContainer); @@ -138,6 +139,7 @@ class SceneTreeDock : public VBoxContainer { HashMap<String, Map<RES, RES>> clipboard_resource_remap; ScriptCreateDialog *script_create_dialog; + ShaderCreateDialog *shader_create_dialog; AcceptDialog *accept; ConfirmationDialog *delete_dialog; ConfirmationDialog *editable_instance_remove_dialog; @@ -166,6 +168,8 @@ class SceneTreeDock : public VBoxContainer { VBoxContainer *create_root_dialog; String selected_favorite_root; + Ref<ShaderMaterial> selected_shader_material; + void _add_children_to_popup(Object *p_obj, int p_depth); void _node_reparent(NodePath p_path, bool p_keep_global_xform); @@ -192,7 +196,9 @@ class SceneTreeDock : public VBoxContainer { void _node_selected(); void _node_renamed(); void _script_created(Ref<Script> p_script); + void _shader_created(Ref<Shader> p_shader); void _script_creation_closed(); + void _shader_creation_closed(); void _delete_confirm(bool p_cut = false); @@ -288,6 +294,9 @@ public: void attach_script_to_selected(bool p_extend); void open_script_dialog(Node *p_for_node, bool p_extend); + void attach_shader_to_selected(); + void open_shader_dialog(Ref<ShaderMaterial> &p_for_material); + void open_add_child_dialog(); void open_instance_child_dialog(); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp new file mode 100644 index 0000000000..beae0c9c91 --- /dev/null +++ b/editor/shader_create_dialog.cpp @@ -0,0 +1,643 @@ +/*************************************************************************/ +/* shader_create_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_create_dialog.h" +#include "editor/editor_scale.h" +#include "scene/resources/visual_shader.h" +#include "servers/rendering/shader_types.h" + +void ShaderCreateDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _update_theme(); + + String last_lang = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_language", ""); + if (!last_lang.is_empty()) { + for (int i = 0; i < language_menu->get_item_count(); i++) { + if (language_menu->get_item_text(i) == last_lang) { + language_menu->select(i); + current_language = i; + break; + } + } + } else { + language_menu->select(default_language); + } + + current_mode = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_mode", 0); + mode_menu->select(current_mode); + } break; + case NOTIFICATION_THEME_CHANGED: { + _update_theme(); + } break; + } +} + +void ShaderCreateDialog::_update_theme() { + Ref<Texture2D> shader_icon = gc->get_theme_icon(SNAME("Shader"), SNAME("EditorIcons")); + if (shader_icon.is_valid()) { + language_menu->set_item_icon(0, shader_icon); + } + + Ref<Texture2D> visual_shader_icon = gc->get_theme_icon(SNAME("VisualShader"), SNAME("EditorIcons")); + if (visual_shader_icon.is_valid()) { + language_menu->set_item_icon(1, visual_shader_icon); + } + + path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); + status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); +} + +void ShaderCreateDialog::_update_language_info() { + language_data.clear(); + + List<StringName> classes; + classes.push_front(SNAME("Shader")); + classes.push_front(SNAME("VisualShader")); + + for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { + language_data.push_back(ShaderTypeData()); + } + + int idx = 0; + for (List<ShaderTypeData>::Element *E = language_data.front(); E; E = E->next()) { + if (idx == int(SHADER_TYPE_TEXT)) { + E->get().use_templates = true; + E->get().extensions.push_back("gdshader"); + E->get().default_extension = "gdshader"; + } else { + E->get().default_extension = "tres"; + } + E->get().extensions.push_back("res"); + E->get().extensions.push_back("tres"); + idx++; + } +} + +void ShaderCreateDialog::_path_hbox_sorted() { + if (is_visible()) { + int filename_start_pos = initial_base_path.rfind("/") + 1; + int filename_end_pos = initial_base_path.length(); + + if (!is_built_in) { + file_path->select(filename_start_pos, filename_end_pos); + } + + file_path->set_caret_column(file_path->get_text().length()); + file_path->set_caret_column(filename_start_pos); + + file_path->grab_focus(); + } +} + +void ShaderCreateDialog::_mode_changed(int p_mode) { + current_mode = p_mode; + EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_mode", p_mode); +} + +void ShaderCreateDialog::_template_changed(int p_template) { + current_template = p_template; + EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_template", p_template); +} + +void ShaderCreateDialog::ok_pressed() { + if (is_new_shader_created) { + _create_new(); + } else { + _load_exist(); + } + + is_new_shader_created = true; + _update_dialog(); +} + +void ShaderCreateDialog::_create_new() { + RES shader; + + if (language_menu->get_selected() == int(SHADER_TYPE_TEXT)) { + Ref<Shader> text_shader; + text_shader.instantiate(); + shader = text_shader; + + StringBuilder code; + code += vformat("shader_type %s;\n", mode_menu->get_text().replace(" ", "").camelcase_to_underscore()); + + if (current_template == 0) { // Default template. + code += "\n"; + switch (current_mode) { + case Shader::MODE_SPATIAL: + code += "void fragment() {\n"; + code += "\t// Place fragment code here.\n"; + code += "}\n"; + break; + case Shader::MODE_CANVAS_ITEM: + code += "void fragment() {\n"; + code += "\t// Place fragment code here.\n"; + code += "}\n"; + break; + case Shader::MODE_PARTICLES: + code += "void start() {\n"; + code += "\t// Place start code here.\n"; + code += "}\n"; + code += "\n"; + code += "void process() {\n"; + code += "\t// Place process code here.\n"; + code += "}\n"; + break; + case Shader::MODE_SKY: + code += "void sky() {\n"; + code += "\t// Place sky code here.\n"; + code += "}\n"; + break; + } + } + text_shader->set_code(code.as_string()); + } else { + Ref<VisualShader> visual_shader; + visual_shader.instantiate(); + shader = visual_shader; + visual_shader->set_engine_version(Engine::get_singleton()->get_version_info()); + visual_shader->set_mode(Shader::Mode(current_mode)); + } + + if (!is_built_in) { + String lpath = ProjectSettings::get_singleton()->localize_path(file_path->get_text()); + shader->set_path(lpath); + Error err = ResourceSaver::save(lpath, shader, ResourceSaver::FLAG_CHANGE_PATH); + if (err != OK) { + alert->set_text(TTR("Error - Could not create shader in filesystem.")); + alert->popup_centered(); + return; + } + } + + emit_signal("shader_created", shader); + hide(); +} + +void ShaderCreateDialog::_load_exist() { + String path = file_path->get_text(); + RES p_shader = ResourceLoader::load(path, "Shader"); + if (p_shader.is_null()) { + alert->set_text(vformat(TTR("Error loading shader from %s"), path)); + alert->popup_centered(); + return; + } + + emit_signal("shader_created", p_shader); + hide(); +} + +void ShaderCreateDialog::_language_changed(int p_language) { + ShaderTypeData data = language_data[p_language]; + + String selected_ext = "." + data.default_extension; + String path = file_path->get_text(); + String extension = ""; + + if (path != "") { + if (path.find(".") != -1) { + extension = path.get_extension(); + } + if (extension.length() == 0) { + path += selected_ext; + } else { + path = path.get_basename() + selected_ext; + } + } else { + path = "shader" + selected_ext; + } + _path_changed(path); + file_path->set_text(path); + + template_menu->set_disabled(!data.use_templates); + template_menu->clear(); + + if (data.use_templates) { + int last_template = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_template", 0); + + template_menu->add_item(TTR("Default")); + template_menu->add_item(TTR("Empty")); + + template_menu->select(last_template); + current_template = last_template; + } else { + template_menu->add_item(TTR("N/A")); + } + + EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected())); + _update_dialog(); +} + +void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { + is_built_in = p_enabled; + if (p_enabled) { + is_new_shader_created = true; + } else { + _path_changed(file_path->get_text()); + } + _update_dialog(); +} + +void ShaderCreateDialog::_browse_path() { + file_browse->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + file_browse->set_title(TTR("Open Shader / Choose Location")); + file_browse->get_ok_button()->set_text(TTR("Open")); + + file_browse->set_disable_overwrite_warning(true); + file_browse->clear_filters(); + + List<String> extensions = language_data[language_menu->get_selected()].extensions; + + for (const String &E : extensions) { + file_browse->add_filter("*." + E); + } + + file_browse->set_current_path(file_path->get_text()); + file_browse->popup_file_dialog(); +} + +void ShaderCreateDialog::_file_selected(const String &p_file) { + String p = ProjectSettings::get_singleton()->localize_path(p_file); + file_path->set_text(p); + _path_changed(p); + + String filename = p.get_file().get_basename(); + int select_start = p.rfind(filename); + file_path->select(select_start, select_start + filename.length()); + file_path->set_caret_column(select_start + filename.length()); + file_path->grab_focus(); +} + +void ShaderCreateDialog::_path_changed(const String &p_path) { + if (is_built_in) { + return; + } + + is_path_valid = false; + is_new_shader_created = true; + + String path_error = _validate_path(p_path, false); + if (path_error != "") { + _msg_path_valid(false, path_error); + _update_dialog(); + return; + } + + DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES); + String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); + if (f->file_exists(p)) { + is_new_shader_created = false; + _msg_path_valid(true, TTR("File exists, it will be reused.")); + } + memdelete(f); + + is_path_valid = true; + _update_dialog(); +} + +void ShaderCreateDialog::_path_submitted(const String &p_path) { + ok_pressed(); +} + +void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled) { + if (p_base_path != "") { + initial_base_path = p_base_path.get_basename(); + file_path->set_text(initial_base_path + "." + language_data[language_menu->get_selected()].default_extension); + current_language = language_menu->get_selected(); + } else { + initial_base_path = ""; + file_path->set_text(""); + } + file_path->deselect(); + + built_in_enabled = p_built_in_enabled; + load_enabled = p_load_enabled; + + _language_changed(current_language); + _path_changed(file_path->get_text()); +} + +String ShaderCreateDialog::_validate_path(const String &p_path, bool p_file_must_exist) { + String p = p_path.strip_edges(); + + if (p == "") { + return TTR("Path is empty."); + } + if (p.get_file().get_basename() == "") { + return TTR("Filename is empty."); + } + + p = ProjectSettings::get_singleton()->localize_path(p); + if (!p.begins_with("res://")) { + return TTR("Path is not local."); + } + + DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (d->change_dir(p.get_base_dir()) != OK) { + memdelete(d); + return TTR("Invalid base path."); + } + memdelete(d); + + DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (f->dir_exists(p)) { + memdelete(f); + return TTR("A directory with the same name exists."); + } else if (p_file_must_exist && !f->file_exists(p)) { + memdelete(f); + return TTR("File does not exist."); + } + memdelete(f); + + String extension = p.get_extension(); + List<String> extensions; + + for (int l = 0; l < SHADER_TYPE_MAX; l++) { + for (List<String>::Element *E = language_data[l].extensions.front(); E; E = E->next()) { + extensions.push_back(E->get()); + } + } + + ShaderTypeData data = language_data[language_menu->get_selected()]; + + bool found = false; + bool match = false; + int index = 0; + for (List<String>::Element *E = extensions.front(); E; E = E->next()) { + if (E->get().nocasecmp_to(extension) == 0) { + found = true; + if (E->get() == data.default_extension) { + match = true; + } + break; + } + index++; + } + + if (!found) { + return TTR("Invalid extension."); + } + if (!match) { + return TTR("Wrong extension chosen."); + } + + String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); + if (path_error != "") { + return path_error; + } + + return ""; +} + +void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { + error_label->set_text("- " + p_msg); + if (valid) { + error_label->add_theme_color_override("font_color", gc->get_theme_color("success_color", "Editor")); + } else { + error_label->add_theme_color_override("font_color", gc->get_theme_color("error_color", "Editor")); + } +} + +void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { + path_error_label->set_text("- " + p_msg); + if (valid) { + path_error_label->add_theme_color_override("font_color", gc->get_theme_color("success_color", "Editor")); + } else { + path_error_label->add_theme_color_override("font_color", gc->get_theme_color("error_color", "Editor")); + } +} + +void ShaderCreateDialog::_update_dialog() { + bool shader_ok = true; + + if (!is_built_in && !is_path_valid) { + _msg_script_valid(false, TTR("Invalid path.")); + shader_ok = false; + } + if (shader_ok) { + _msg_script_valid(true, TTR("Shader path/name is valid.")); + } + if (!built_in_enabled) { + internal->set_pressed(false); + } + + if (is_built_in) { + file_path->set_editable(false); + path_button->set_disabled(true); + re_check_path = true; + } else { + file_path->set_editable(true); + path_button->set_disabled(false); + if (re_check_path) { + re_check_path = false; + _path_changed(file_path->get_text()); + } + } + + internal->set_disabled(!built_in_enabled); + + builtin_warning_label->set_visible(is_built_in); + + if (is_built_in) { + get_ok_button()->set_text(TTR("Create")); + _msg_path_valid(true, TTR("Built-in script (into scene file).")); + } else if (is_new_shader_created) { + get_ok_button()->set_text(TTR("Create")); + if (is_path_valid) { + _msg_path_valid(true, TTR("Will create a new shader file.")); + } + } else if (load_enabled) { + get_ok_button()->set_text(TTR("Load")); + if (is_path_valid) { + _msg_path_valid(true, TTR("Will load an existing shader file.")); + } + } else { + get_ok_button()->set_text(TTR("Create")); + _msg_path_valid(false, TTR("Shader file already exists.")); + + shader_ok = false; + } + + get_ok_button()->set_disabled(!shader_ok); + + Callable entered_call = callable_mp(this, &ShaderCreateDialog::_path_submitted); + if (shader_ok) { + if (!file_path->is_connected("text_submitted", entered_call)) { + file_path->connect("text_submitted", entered_call); + } + } else if (file_path->is_connected("text_submitted", entered_call)) { + file_path->disconnect("text_submitted", entered_call); + } +} + +void ShaderCreateDialog::_bind_methods() { + ClassDB::bind_method(D_METHOD("config", "path", "built_in_enabled", "load_enabled"), &ShaderCreateDialog::config, DEFVAL(true), DEFVAL(true)); + + ADD_SIGNAL(MethodInfo("shader_created", PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"))); +} + +ShaderCreateDialog::ShaderCreateDialog() { + _update_language_info(); + + // Main Controls. + + gc = memnew(GridContainer); + gc->set_columns(2); + + // Error Fields. + + VBoxContainer *vb = memnew(VBoxContainer); + + error_label = memnew(Label); + vb->add_child(error_label); + + path_error_label = memnew(Label); + vb->add_child(path_error_label); + + builtin_warning_label = memnew(Label); + builtin_warning_label->set_text( + TTR("Note: Built-in shaders can't be edited using an external editor.")); + vb->add_child(builtin_warning_label); + builtin_warning_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + builtin_warning_label->hide(); + + status_panel = memnew(PanelContainer); + status_panel->set_h_size_flags(Control::SIZE_FILL); + status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); + status_panel->add_child(vb); + + // Spacing. + + Control *spacing = memnew(Control); + spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); + + vb = memnew(VBoxContainer); + vb->add_child(gc); + vb->add_child(spacing); + vb->add_child(status_panel); + add_child(vb); + + // Language. + + language_menu = memnew(OptionButton); + language_menu->set_custom_minimum_size(Size2(250, 0) * EDSCALE); + language_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL); + gc->add_child(memnew(Label(TTR("Language:")))); + gc->add_child(language_menu); + + for (int i = 0; i < SHADER_TYPE_MAX; i++) { + String language; + bool invalid = false; + switch (i) { + case SHADER_TYPE_TEXT: + language = "Shader"; + default_language = i; + break; + case SHADER_TYPE_VISUAL: + language = "VisualShader"; + break; + case SHADER_TYPE_MAX: + invalid = true; + break; + default: + invalid = true; + break; + } + if (invalid) { + continue; + } + language_menu->add_item(language); + } + if (default_language >= 0) { + language_menu->select(default_language); + } + current_language = default_language; + language_menu->connect("item_selected", callable_mp(this, &ShaderCreateDialog::_language_changed)); + + // Modes. + + mode_menu = memnew(OptionButton); + for (const List<String>::Element *E = ShaderTypes::get_singleton()->get_types_list().front(); E; E = E->next()) { + mode_menu->add_item(E->get().capitalize()); + } + gc->add_child(memnew(Label(TTR("Mode:")))); + gc->add_child(mode_menu); + mode_menu->connect("item_selected", callable_mp(this, &ShaderCreateDialog::_mode_changed)); + + // Templates. + + template_menu = memnew(OptionButton); + gc->add_child(memnew(Label(TTR("Template:")))); + gc->add_child(template_menu); + template_menu->connect("item_selected", callable_mp(this, &ShaderCreateDialog::_template_changed)); + + // Built-in Shader. + + internal = memnew(CheckBox); + internal->set_text(TTR("On")); + internal->connect("toggled", callable_mp(this, &ShaderCreateDialog::_built_in_toggled)); + gc->add_child(memnew(Label(TTR("Built-in Shader:")))); + gc->add_child(internal); + + // Path. + + HBoxContainer *hb = memnew(HBoxContainer); + hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->connect("sort_children", callable_mp(this, &ShaderCreateDialog::_path_hbox_sorted)); + file_path = memnew(LineEdit); + file_path->connect("text_changed", callable_mp(this, &ShaderCreateDialog::_path_changed)); + file_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->add_child(file_path); + path_button = memnew(Button); + path_button->connect("pressed", callable_mp(this, &ShaderCreateDialog::_browse_path)); + hb->add_child(path_button); + gc->add_child(memnew(Label(TTR("Path:")))); + gc->add_child(hb); + + // Dialog Setup. + + file_browse = memnew(EditorFileDialog); + file_browse->connect("file_selected", callable_mp(this, &ShaderCreateDialog::_file_selected)); + file_browse->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); + add_child(file_browse); + + alert = memnew(AcceptDialog); + alert->get_label()->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + alert->get_label()->set_align(Label::ALIGN_CENTER); + alert->get_label()->set_valign(Label::VALIGN_CENTER); + alert->get_label()->set_custom_minimum_size(Size2(325, 60) * EDSCALE); + add_child(alert); + + get_ok_button()->set_text(TTR("Create")); + set_hide_on_ok(false); + + set_title(TTR("Create Shader")); +} diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h new file mode 100644 index 0000000000..cc338cd6b5 --- /dev/null +++ b/editor/shader_create_dialog.h @@ -0,0 +1,115 @@ +/*************************************************************************/ +/* shader_create_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_CREATE_DIALOG_H +#define SHADER_CREATE_DIALOG_H + +#include "editor/editor_file_dialog.h" +#include "editor/editor_settings.h" +#include "scene/gui/check_box.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/grid_container.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/option_button.h" +#include "scene/gui/panel_container.h" + +class ShaderCreateDialog : public ConfirmationDialog { + GDCLASS(ShaderCreateDialog, ConfirmationDialog); + + enum ShaderType { + SHADER_TYPE_TEXT, + SHADER_TYPE_VISUAL, + SHADER_TYPE_MAX, + }; + + struct ShaderTypeData { + List<String> extensions; + String default_extension; + bool use_templates = false; + }; + + List<ShaderTypeData> language_data; + + GridContainer *gc = nullptr; + Label *error_label = nullptr; + Label *path_error_label = nullptr; + Label *builtin_warning_label = nullptr; + PanelContainer *status_panel = nullptr; + OptionButton *language_menu = nullptr; + OptionButton *mode_menu = nullptr; + OptionButton *template_menu = nullptr; + CheckBox *internal = nullptr; + LineEdit *file_path = nullptr; + Button *path_button = nullptr; + EditorFileDialog *file_browse = nullptr; + AcceptDialog *alert = nullptr; + + String initial_base_path; + bool is_new_shader_created = true; + bool is_path_valid = false; + bool is_built_in = false; + bool built_in_enabled = true; + bool load_enabled = false; + bool re_check_path = false; + int current_language = -1; + int default_language = -1; + int current_mode = 0; + int current_template = 0; + + virtual void _update_language_info(); + + void _path_hbox_sorted(); + void _path_changed(const String &p_path = String()); + void _path_submitted(const String &p_path = String()); + void _language_changed(int p_language = 0); + void _built_in_toggled(bool p_enabled); + void _template_changed(int p_template = 0); + void _mode_changed(int p_mode = 0); + void _browse_path(); + void _file_selected(const String &p_file); + String _validate_path(const String &p_path, bool p_file_must_exist); + virtual void ok_pressed() override; + void _create_new(); + void _load_exist(); + void _msg_script_valid(bool valid, const String &p_msg = String()); + void _msg_path_valid(bool valid, const String &p_msg = String()); + void _update_dialog(); + +protected: + void _update_theme(); + void _notification(int p_what); + static void _bind_methods(); + +public: + void config(const String &p_base_path, bool p_built_in_enabled = true, bool p_load_enabled = true); + ShaderCreateDialog(); +}; + +#endif diff --git a/methods.py b/methods.py index 13851d8315..970bd10aa3 100644 --- a/methods.py +++ b/methods.py @@ -828,6 +828,10 @@ def using_clang(env): return "clang" in os.path.basename(env["CC"]) +def using_emcc(env): + return "emcc" in os.path.basename(env["CC"]) + + def show_progress(env): import sys from SCons.Script import Progress, Command, AlwaysBuild diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index 42f8b9f163..2f8b354bb7 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -98,7 +98,7 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo return Variant(); } -void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) { CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node()); Transform3D gt = cs->get_global_transform(); @@ -201,7 +201,7 @@ void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_i } } -void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) { CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node()); if (Object::cast_to<CSGSphere3D>(cs)) { diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index 847313c0b4..2a6ab91102 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -47,8 +47,8 @@ public: virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; - virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; - virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const override; + virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override; + virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) override; CSGShape3DGizmoPlugin(); }; diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index fc2535a6ca..476cb9cf2a 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -80,6 +80,7 @@ if env["builtin_freetype"]: # Forcibly undefine this macro so SIMD is not used in this file, # since currently unsupported in WASM tmp_env = env_freetype.Clone() + tmp_env.disable_warnings() tmp_env.Append(CPPFLAGS=["-U__OPTIMIZE__"]) sfnt = tmp_env.Object(sfnt) thirdparty_sources += [sfnt] diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index 81dd570bcb..d54b1a47df 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -449,12 +449,12 @@ bool TextServerGDNative::shaped_text_add_string(RID p_shaped, const String &p_te return interface->shaped_text_add_string(data, (godot_rid *)&p_shaped, (const godot_string *)&p_text, (const godot_rid **)p_fonts.ptr(), p_size, (const godot_dictionary *)&p_opentype_features, (const godot_string *)&p_language); } -bool TextServerGDNative::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextServerGDNative::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { ERR_FAIL_COND_V(interface == nullptr, false); return interface->shaped_text_add_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align, p_length); } -bool TextServerGDNative::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextServerGDNative::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { ERR_FAIL_COND_V(interface == nullptr, false); return interface->shaped_text_resize_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align); } diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index 7a0725f3d9..a2eb944499 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -154,8 +154,8 @@ public: virtual bool shaped_text_get_preserve_control(RID p_shaped) const override; virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; - virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override; - virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override; + virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override; virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index c0013ac23a..9574c765bc 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -431,9 +431,13 @@ void GDScriptTextDocument::sync_script_content(const String &p_path, const Strin GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); EditorFileSystem::get_singleton()->update_file(path); - Ref<GDScript> script = ResourceLoader::load(path); - script->load_source_code(path); - script->reload(true); + Error error; + Ref<GDScript> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error); + if (error == OK) { + if (script->load_source_code(path) == OK) { + script->reload(true); + } + } } void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) { diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 4fc229a0f8..1512b4bb89 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -523,22 +523,24 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S Array stack; Node *current = nullptr; - stack.push_back(owner_scene_node); + if (owner_scene_node != nullptr) { + stack.push_back(owner_scene_node); - while (!stack.is_empty()) { - current = stack.pop_back(); - Ref<GDScript> script = current->get_script(); - if (script.is_valid() && script->get_path() == path) { - break; - } - for (int i = 0; i < current->get_child_count(); ++i) { - stack.push_back(current->get_child(i)); + while (!stack.is_empty()) { + current = stack.pop_back(); + Ref<GDScript> script = current->get_script(); + if (script.is_valid() && script->get_path() == path) { + break; + } + for (int i = 0; i < current->get_child_count(); ++i) { + stack.push_back(current->get_child(i)); + } } - } - Ref<GDScript> script = current->get_script(); - if (!script.is_valid() || script->get_path() != path) { - current = owner_scene_node; + Ref<GDScript> script = current->get_script(); + if (!script.is_valid() || script->get_path() != path) { + current = owner_scene_node; + } } String code = parser->get_text_for_completion(p_params.position); diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 897f1b2822..5f35d506de 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -73,7 +73,7 @@ namespace GodotTools.Build GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message); } - if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"})) + if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" })) return; // Build failed // Notify running game for hot-reload @@ -92,7 +92,7 @@ namespace GodotTools.Build if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) return; // No solution to build - BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Clean"}); + BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" }); } private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed; @@ -129,10 +129,10 @@ namespace GodotTools.Build RectMinSize = new Vector2(0, 228) * EditorScale; SizeFlagsVertical = (int)SizeFlags.ExpandFill; - var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill}; + var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill }; AddChild(toolBarHBox); - buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")}; + buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; toolBarHBox.AddChild(buildMenuBtn); var buildMenu = buildMenuBtn.GetPopup(); @@ -183,10 +183,14 @@ namespace GodotTools.Build { base._Notification(what); - if (what == NotificationThemeChanged) { - buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); - errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); - warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); + if (what == NotificationThemeChanged) + { + if (buildMenuBtn != null) + buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); + if (errorsBtn != null) + errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); + if (warningsBtn != null) + warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons"); } } } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index fa4888f843..66816f32d1 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1161,7 +1161,7 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te return true; } -bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { _THREAD_SAFE_METHOD_ ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -1191,7 +1191,7 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con return true; } -bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -1222,34 +1222,10 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, if (sd->orientation == ORIENTATION_HORIZONTAL) { sd->objects[key].rect.position.x = sd->width; sd->width += sd->objects[key].rect.size.x; - switch (sd->objects[key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y); - } break; - } sd->glyphs.write[i].advance = sd->objects[key].rect.size.x; } else { sd->objects[key].rect.position.y = sd->width; sd->width += sd->objects[key].rect.size.y; - switch (sd->objects[key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x); - } break; - } sd->glyphs.write[i].advance = sd->objects[key].rect.size.y; } } else { @@ -1279,35 +1255,71 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. + float full_ascent = sd->ascent; + float full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + sd->ascent = full_ascent; + sd->descent = full_descent; } return true; } @@ -1404,33 +1416,9 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng if (new_sd->orientation == ORIENTATION_HORIZONTAL) { new_sd->objects[key].rect.position.x = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.x; - switch (new_sd->objects[key].inline_align) { - case VALIGN_TOP: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y); - } break; - case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2)); - new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y); - } break; - } } else { new_sd->objects[key].rect.position.y = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.y; - switch (new_sd->objects[key].inline_align) { - case VALIGN_TOP: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x); - } break; - case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2)); - new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x); - } break; - } } } else { if (prev_rid != gl.font_rid) { @@ -1464,37 +1452,72 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng } // Align embedded objects to baseline. + float full_ascent = new_sd->ascent; + float full_descent = new_sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = new_sd->descent; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = new_sd->descent - E->get().rect.size.y; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = new_sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = new_sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + new_sd->ascent = full_ascent; + new_sd->descent = full_descent; } - new_sd->valid = true; return shaped_owner.make_rid(new_sd); @@ -2473,33 +2496,9 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { if (sd->orientation == ORIENTATION_HORIZONTAL) { sd->objects[span.embedded_key].rect.position.x = sd->width; sd->width += sd->objects[span.embedded_key].rect.size.x; - switch (sd->objects[span.embedded_key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y); - } break; - } } else { sd->objects[span.embedded_key].rect.position.y = sd->width; sd->width += sd->objects[span.embedded_key].rect.size.y; - switch (sd->objects[span.embedded_key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x); - } break; - } } Glyph gl; gl.start = span.start; @@ -2539,34 +2538,69 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. + float full_ascent = sd->ascent; + float full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } - + sd->ascent = full_ascent; + sd->descent = full_descent; sd->valid = true; return sd->valid; } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 3c4f840bfd..c54686c114 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -216,8 +216,8 @@ public: virtual bool shaped_text_get_preserve_control(RID p_shaped) const override; virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; - virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override; - virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override; + virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override; virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 004cbc2bb3..b60bf8e8d3 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -661,7 +661,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te return true; } -bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -691,7 +691,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con return true; } -bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { _THREAD_SAFE_METHOD_ ShapedTextData *sd = shaped_owner.getornull(p_shaped); ERR_FAIL_COND_V(!sd, false); @@ -724,34 +724,10 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, if (sd->orientation == ORIENTATION_HORIZONTAL) { sd->objects[key].rect.position.x = sd->width; sd->width += sd->objects[key].rect.size.x; - switch (sd->objects[key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.y); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.y / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.y); - } break; - } sd->glyphs.write[i].advance = sd->objects[key].rect.size.x; } else { sd->objects[key].rect.position.y = sd->width; sd->width += sd->objects[key].rect.size.y; - switch (sd->objects[key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[key].rect.size.x); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[key].rect.size.x / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[key].rect.size.x); - } break; - } sd->glyphs.write[i].advance = sd->objects[key].rect.size.y; } } else { @@ -784,35 +760,71 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, } // Align embedded objects to baseline. + float full_ascent = sd->ascent; + float full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + sd->ascent = full_ascent; + sd->descent = full_descent; } return true; } @@ -869,33 +881,9 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng if (new_sd->orientation == ORIENTATION_HORIZONTAL) { new_sd->objects[key].rect.position.x = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.x; - switch (new_sd->objects[key].inline_align) { - case VALIGN_TOP: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.y); - } break; - case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.y / 2)); - new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.y); - } break; - } } else { new_sd->objects[key].rect.position.y = new_sd->width; new_sd->width += new_sd->objects[key].rect.size.y; - switch (new_sd->objects[key].inline_align) { - case VALIGN_TOP: { - new_sd->ascent = MAX(new_sd->ascent, new_sd->objects[key].rect.size.x); - } break; - case VALIGN_CENTER: { - new_sd->ascent = MAX(new_sd->ascent, Math::round(new_sd->objects[key].rect.size.x / 2)); - new_sd->descent = MAX(new_sd->descent, Math::round(new_sd->objects[key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - new_sd->descent = MAX(new_sd->descent, new_sd->objects[key].rect.size.x); - } break; - } } } else { const FontDataFallback *fd = font_owner.getornull(gl.font_rid); @@ -923,35 +911,72 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } } + // Align embedded objects to baseline. + float full_ascent = new_sd->ascent; + float full_descent = new_sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) { if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = new_sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = new_sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -new_sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = new_sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = new_sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } } + new_sd->ascent = full_ascent; + new_sd->descent = full_descent; } new_sd->valid = true; @@ -1336,33 +1361,9 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { if (sd->orientation == ORIENTATION_HORIZONTAL) { sd->objects[span.embedded_key].rect.position.x = sd->width; sd->width += sd->objects[span.embedded_key].rect.size.x; - switch (sd->objects[span.embedded_key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.y); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.y / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.y); - } break; - } } else { sd->objects[span.embedded_key].rect.position.y = sd->width; sd->width += sd->objects[span.embedded_key].rect.size.y; - switch (sd->objects[span.embedded_key].inline_align) { - case VALIGN_TOP: { - sd->ascent = MAX(sd->ascent, sd->objects[span.embedded_key].rect.size.x); - } break; - case VALIGN_CENTER: { - sd->ascent = MAX(sd->ascent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); - sd->descent = MAX(sd->descent, Math::round(sd->objects[span.embedded_key].rect.size.x / 2)); - } break; - case VALIGN_BOTTOM: { - sd->descent = MAX(sd->descent, sd->objects[span.embedded_key].rect.size.x); - } break; - } } Glyph gl; gl.start = span.start; @@ -1456,34 +1457,69 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } // Align embedded objects to baseline. + float full_ascent = sd->ascent; + float full_descent = sd->descent; for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.y = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.y = -(E->get().rect.size.y / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.y = (-sd->ascent + sd->descent) / 2; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.y = sd->descent - E->get().rect.size.y; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.y = 0; + } break; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.y = sd->descent; + } break; + } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.y -= E->get().rect.size.y; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.y -= E->get().rect.size.y / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP } break; } + full_ascent = MAX(full_ascent, -E->get().rect.position.y); + full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y); } else { - switch (E->get().inline_align) { - case VALIGN_TOP: { + switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) { + case INLINE_ALIGN_TO_TOP: { E->get().rect.position.x = -sd->ascent; } break; - case VALIGN_CENTER: { - E->get().rect.position.x = -(E->get().rect.size.x / 2); + case INLINE_ALIGN_TO_CENTER: { + E->get().rect.position.x = (-sd->ascent + sd->descent) / 2; + } break; + case INLINE_ALIGN_TO_BASELINE: { + E->get().rect.position.x = 0; } break; - case VALIGN_BOTTOM: { - E->get().rect.position.x = sd->descent - E->get().rect.size.x; + case INLINE_ALIGN_TO_BOTTOM: { + E->get().rect.position.x = sd->descent; } break; } + switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) { + case INLINE_ALIGN_BOTTOM_TO: { + E->get().rect.position.x -= E->get().rect.size.x; + } break; + case INLINE_ALIGN_CENTER_TO: { + E->get().rect.position.x -= E->get().rect.size.x / 2; + } break; + case INLINE_ALIGN_TOP_TO: { + //NOP + } break; + } + full_ascent = MAX(full_ascent, -E->get().rect.position.x); + full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x); } } - + sd->ascent = full_ascent; + sd->descent = full_descent; sd->valid = true; return sd->valid; } diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index b70c8f4ec0..e3bbde76d4 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -165,8 +165,8 @@ public: virtual bool shaped_text_get_preserve_control(RID p_shaped) const override; virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; - virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) override; - virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) override; + virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override; virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 69822f6ff3..d7263dcf43 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -54,7 +54,7 @@ Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_strin } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(_out_buf_size && (godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; diff --git a/platform/iphone/api/api.cpp b/platform/iphone/api/api.cpp new file mode 100644 index 0000000000..a23791fe1c --- /dev/null +++ b/platform/iphone/api/api.cpp @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* api.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "api.h" + +#if defined(IPHONE_ENABLED) + +void register_iphone_api() { + godot_ios_plugins_initialize(); +} + +void unregister_iphone_api() { + godot_ios_plugins_deinitialize(); +} + +#else + +void register_iphone_api() {} +void unregister_iphone_api() {} + +#endif diff --git a/platform/iphone/api/api.h b/platform/iphone/api/api.h new file mode 100644 index 0000000000..c6570da7ec --- /dev/null +++ b/platform/iphone/api/api.h @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* api.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IPHONE_API_H +#define IPHONE_API_H + +#if defined(IPHONE_ENABLED) +extern void godot_ios_plugins_initialize(); +extern void godot_ios_plugins_deinitialize(); +#endif + +void register_iphone_api(); +void unregister_iphone_api(); + +#endif // IPHONE_API_H diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 8abc3b7ed5..248369369d 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -45,9 +45,6 @@ #include "platform/iphone/vulkan_context_iphone.h" #endif -extern void godot_ios_plugins_initialize(); -extern void godot_ios_plugins_deinitialize(); - class OSIPhone : public OS_Unix { private: static HashMap<String, void *> dynamic_symbol_lookup_table; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index b69996ccf0..a999929cc4 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -145,8 +145,6 @@ void OSIPhone::deinitialize_modules() { if (ios) { memdelete(ios); } - - godot_ios_plugins_deinitialize(); } void OSIPhone::set_main_loop(MainLoop *p_main_loop) { @@ -183,8 +181,6 @@ bool OSIPhone::iterate() { } void OSIPhone::start() { - godot_ios_plugins_initialize(); - Main::start(); if (joypad_iphone) { diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 73aa013701..974f4d3018 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -318,8 +318,16 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; - _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); - Input::get_singleton()->set_mouse_position(wd.mouse_pos); + if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) { + const NSRect contentRect = [wd.window_view frame]; + NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0); + NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; + CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; + CGWarpMouseCursorPosition(lMouseWarpPos); + } else { + _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); + Input::get_singleton()->set_mouse_position(wd.mouse_pos); + } DS_OSX->window_focused = true; DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e78b23c509..4be95487b8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2445,7 +2445,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } else { mb->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); } - + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; case WM_MOUSEHWHEEL: { mb->set_pressed(true); @@ -2456,11 +2456,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (motion < 0) { mb->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } else { mb->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } break; case WM_XBUTTONDOWN: { mb->set_pressed(true); diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 490cf5fe67..589135710f 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -120,7 +120,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { // will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used if (rotation_mode == ROTATION_ORIENTED) { - Vector3 forward = c->interpolate_baked(o_next, cubic); + Vector3 forward = c->interpolate_baked(o_next, cubic) - pos; // Try with the previous position if (forward.length_squared() < CMP_EPSILON2) { diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index fdf6181f1d..258d65112a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -245,6 +245,7 @@ void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_colo ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].custom_bg = p_custom_bg_color; + update(); } Color ItemList::get_item_custom_bg_color(int p_idx) const { @@ -257,6 +258,7 @@ void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_colo ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].custom_fg = p_custom_fg_color; + update(); } Color ItemList::get_item_custom_fg_color(int p_idx) const { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3925e0c38e..3cb1f7d2bc 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2289,7 +2289,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub } } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, VAlign p_align) { +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) { if (current->type == ITEM_TABLE) { return; } @@ -2534,7 +2534,7 @@ void RichTextLabel::push_meta(const Variant &p_meta) { _add_item(item, true); } -void RichTextLabel::push_table(int p_columns, VAlign p_align) { +void RichTextLabel::push_table(int p_columns, InlineAlign p_align) { ERR_FAIL_COND(p_columns < 1); ItemTable *item = memnew(ItemTable); @@ -2897,18 +2897,35 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { columns = 1; } - VAlign align = VALIGN_TOP; - if (subtag.size() > 1) { + int align = INLINE_ALIGN_TOP; + if (subtag.size() > 2) { if (subtag[1] == "top" || subtag[1] == "t") { - align = VALIGN_TOP; + align = INLINE_ALIGN_TOP_TO; } else if (subtag[1] == "center" || subtag[1] == "c") { - align = VALIGN_CENTER; + align = INLINE_ALIGN_CENTER_TO; } else if (subtag[1] == "bottom" || subtag[1] == "b") { - align = VALIGN_BOTTOM; + align = INLINE_ALIGN_BOTTOM_TO; + } + if (subtag[2] == "top" || subtag[2] == "t") { + align |= INLINE_ALIGN_TO_TOP; + } else if (subtag[2] == "center" || subtag[2] == "c") { + align |= INLINE_ALIGN_TO_CENTER; + } else if (subtag[2] == "baseline" || subtag[2] == "l") { + align |= INLINE_ALIGN_TO_BASELINE; + } else if (subtag[2] == "bottom" || subtag[2] == "b") { + align |= INLINE_ALIGN_TO_BOTTOM; + } + } else if (subtag.size() > 1) { + if (subtag[1] == "top" || subtag[1] == "t") { + align = INLINE_ALIGN_TOP; + } else if (subtag[1] == "center" || subtag[1] == "c") { + align = INLINE_ALIGN_CENTER; + } else if (subtag[1] == "bottom" || subtag[1] == "b") { + align = INLINE_ALIGN_BOTTOM; } } - push_table(columns, align); + push_table(columns, (InlineAlign)align); pos = brk_end + 1; tag_stack.push_front("table"); } else if (tag == "cell") { @@ -3187,15 +3204,34 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = end; tag_stack.push_front(bbcode_name); } else if (tag.begins_with("img")) { - VAlign align = VALIGN_TOP; + int align = INLINE_ALIGN_CENTER; if (tag.begins_with("img=")) { - String al = tag.substr(4, tag.length()); - if (al == "top" || al == "t") { - align = VALIGN_TOP; - } else if (al == "center" || al == "c") { - align = VALIGN_CENTER; - } else if (al == "bottom" || al == "b") { - align = VALIGN_BOTTOM; + Vector<String> subtag = tag.substr(4, tag.length()).split(","); + if (subtag.size() > 1) { + if (subtag[0] == "top" || subtag[0] == "t") { + align = INLINE_ALIGN_TOP_TO; + } else if (subtag[0] == "center" || subtag[0] == "c") { + align = INLINE_ALIGN_CENTER_TO; + } else if (subtag[0] == "bottom" || subtag[0] == "b") { + align = INLINE_ALIGN_BOTTOM_TO; + } + if (subtag[1] == "top" || subtag[1] == "t") { + align |= INLINE_ALIGN_TO_TOP; + } else if (subtag[1] == "center" || subtag[1] == "c") { + align |= INLINE_ALIGN_TO_CENTER; + } else if (subtag[1] == "baseline" || subtag[1] == "l") { + align |= INLINE_ALIGN_TO_BASELINE; + } else if (subtag[1] == "bottom" || subtag[1] == "b") { + align |= INLINE_ALIGN_TO_BOTTOM; + } + } else if (subtag.size() > 0) { + if (subtag[0] == "top" || subtag[0] == "t") { + align = INLINE_ALIGN_TOP; + } else if (subtag[0] == "center" || subtag[0] == "c") { + align = INLINE_ALIGN_CENTER; + } else if (subtag[0] == "bottom" || subtag[0] == "b") { + align = INLINE_ALIGN_BOTTOM; + } } } @@ -3236,7 +3272,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } } - add_image(texture, width, height, color, align); + add_image(texture, width, height, color, (InlineAlign)align); } pos = end; @@ -3961,7 +3997,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); @@ -3981,7 +4017,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); - ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP)); ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 999d8b05fd..a73176ba7e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -161,7 +161,7 @@ private: struct ItemImage : public Item { Ref<Texture2D> image; - VAlign inline_align = VALIGN_TOP; + InlineAlign inline_align = INLINE_ALIGN_CENTER; Size2 size; Color color; ItemImage() { type = ITEM_IMAGE; } @@ -248,7 +248,7 @@ private: int total_width = 0; int total_height = 0; - VAlign inline_align = VALIGN_TOP; + InlineAlign inline_align = INLINE_ALIGN_TOP; ItemTable() { type = ITEM_TABLE; } }; @@ -463,7 +463,7 @@ private: public: String get_text(); void add_text(const String &p_text); - void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP); + void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlign p_align = INLINE_ALIGN_CENTER); void add_newline(); bool remove_line(const int p_line); void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); @@ -484,7 +484,7 @@ public: void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize); void push_meta(const Variant &p_meta); - void push_table(int p_columns, VAlign p_align = VALIGN_TOP); + void push_table(int p_columns, InlineAlign p_align = INLINE_ALIGN_TOP); void push_fade(int p_start_index, int p_length); void push_shake(int p_strength, float p_rate); void push_wave(float p_frequency, float p_amplitude); diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 8ffd2df112..ef070589e4 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -261,11 +261,11 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in sign = -1; } - float global_rate_scale = AudioServer::get_singleton()->get_global_rate_scale(); - float base_rate = AudioServer::get_singleton()->get_mix_rate() * global_rate_scale; + float base_rate = AudioServer::get_singleton()->get_mix_rate(); float srate = base->mix_rate; srate *= p_rate_scale; - float fincrement = srate / base_rate; + float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale(); + float fincrement = (srate * playback_speed_scale) / base_rate; int32_t increment = int32_t(MAX(fincrement * MIX_FRAC_LEN, 1)); increment *= sign; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index f1eff6e84f..5e60c7c4ed 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -56,8 +56,8 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(VALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width); ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width); @@ -175,13 +175,13 @@ bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_ return res; } -bool TextLine::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); dirty = true; return res; } -bool TextLine::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { const_cast<TextLine *>(this)->_shape(); return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 1b5c1a3123..b773bd05be 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -76,8 +76,8 @@ public: bool get_preserve_control() const; bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); - bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER); void set_align(HAlign p_align); HAlign get_align() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 958c94fe31..ffae551570 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -59,8 +59,8 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align); ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align); @@ -290,13 +290,13 @@ void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) { dirty_lines = true; } -bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); dirty_lines = true; return res; } -bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); dirty_lines = true; return res; diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index a34e745090..d0747a9e03 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -86,8 +86,8 @@ public: void clear_dropcap(); bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); - bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER); void set_align(HAlign p_align); HAlign get_align() const; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index aefea6723e..f3fa857682 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -48,9 +48,9 @@ void AudioStreamPlaybackResampled::_begin_resample() { void AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { float target_rate = AudioServer::get_singleton()->get_mix_rate(); - float global_rate_scale = AudioServer::get_singleton()->get_global_rate_scale(); + float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale(); - uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale) / double(target_rate * global_rate_scale)) * double(FP_LEN)); + uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale * playback_speed_scale) / double(target_rate)) * double(FP_LEN)); for (int i = 0; i < p_frames; i++) { uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 076cbb58af..4c54188cb2 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -913,14 +913,14 @@ bool AudioServer::is_bus_channel_active(int p_bus, int p_channel) const { return buses[p_bus]->channels[p_channel].active; } -void AudioServer::set_global_rate_scale(float p_scale) { +void AudioServer::set_playback_speed_scale(float p_scale) { ERR_FAIL_COND(p_scale <= 0); - global_rate_scale = p_scale; + playback_speed_scale = p_scale; } -float AudioServer::get_global_rate_scale() const { - return global_rate_scale; +float AudioServer::get_playback_speed_scale() const { + return playback_speed_scale; } void AudioServer::init_channels_and_buffers() { @@ -1277,8 +1277,8 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bus_peak_volume_left_db", "bus_idx", "channel"), &AudioServer::get_bus_peak_volume_left_db); ClassDB::bind_method(D_METHOD("get_bus_peak_volume_right_db", "bus_idx", "channel"), &AudioServer::get_bus_peak_volume_right_db); - ClassDB::bind_method(D_METHOD("set_global_rate_scale", "scale"), &AudioServer::set_global_rate_scale); - ClassDB::bind_method(D_METHOD("get_global_rate_scale"), &AudioServer::get_global_rate_scale); + ClassDB::bind_method(D_METHOD("set_playback_speed_scale", "scale"), &AudioServer::set_playback_speed_scale); + ClassDB::bind_method(D_METHOD("get_playback_speed_scale"), &AudioServer::get_playback_speed_scale); ClassDB::bind_method(D_METHOD("lock"), &AudioServer::lock); ClassDB::bind_method(D_METHOD("unlock"), &AudioServer::unlock); @@ -1302,7 +1302,7 @@ void AudioServer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "bus_count"), "set_bus_count", "get_bus_count"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "device"), "set_device", "get_device"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "global_rate_scale"), "set_global_rate_scale", "get_global_rate_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed_scale"), "set_playback_speed_scale", "get_playback_speed_scale"); ADD_SIGNAL(MethodInfo("bus_layout_changed")); @@ -1322,7 +1322,7 @@ AudioServer::AudioServer() { #endif mix_time = 0; mix_size = 0; - global_rate_scale = 1; + playback_speed_scale = 1; } AudioServer::~AudioServer() { diff --git a/servers/audio_server.h b/servers/audio_server.h index a1a373e1ca..7974c4a2ad 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -177,7 +177,7 @@ private: int channel_count; int to_mix; - float global_rate_scale; + float playback_speed_scale; struct Bus { StringName name; @@ -316,8 +316,8 @@ public: bool is_bus_channel_active(int p_bus, int p_channel) const; - void set_global_rate_scale(float p_scale); - float get_global_rate_scale() const; + void set_playback_speed_scale(float p_scale); + float get_playback_speed_scale() const; virtual void init(); virtual void finish(); diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp index 6a7f2b73c5..852e9ecd74 100644 --- a/servers/physics_3d/collision_solver_3d_sat.cpp +++ b/servers/physics_3d/collision_solver_3d_sat.cpp @@ -956,9 +956,12 @@ static void _collision_sphere_convex_polygon(const Shape3DSW *p_a, const Transfo const Vector3 *vertices = mesh.vertices.ptr(); int vertex_count = mesh.vertices.size(); + // Precalculating this makes the transforms faster. + Basis b_xform_normal = p_transform_b.basis.inverse().transposed(); + // faces of B for (int i = 0; i < face_count; i++) { - Vector3 axis = p_transform_b.xform(faces[i].plane).normal; + Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized(); if (!separator.test_axis(axis)) { return; @@ -1379,9 +1382,12 @@ static void _collision_box_convex_polygon(const Shape3DSW *p_a, const Transform3 } } + // Precalculating this makes the transforms faster. + Basis b_xform_normal = p_transform_b.basis.inverse().transposed(); + // faces of B for (int i = 0; i < face_count; i++) { - Vector3 axis = p_transform_b.xform(faces[i].plane).normal; + Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized(); if (!separator.test_axis(axis)) { return; @@ -1733,9 +1739,12 @@ static void _collision_capsule_convex_polygon(const Shape3DSW *p_a, const Transf int edge_count = mesh.edges.size(); const Vector3 *vertices = mesh.vertices.ptr(); + // Precalculating this makes the transforms faster. + Basis b_xform_normal = p_transform_b.basis.inverse().transposed(); + // faces of B for (int i = 0; i < face_count; i++) { - Vector3 axis = p_transform_b.xform(faces[i].plane).normal; + Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized(); if (!separator.test_axis(axis)) { return; @@ -2057,20 +2066,24 @@ static void _collision_convex_polygon_convex_polygon(const Shape3DSW *p_a, const const Vector3 *vertices_B = mesh_B.vertices.ptr(); int vertex_count_B = mesh_B.vertices.size(); + // Precalculating this makes the transforms faster. + Basis a_xform_normal = p_transform_b.basis.inverse().transposed(); + // faces of A for (int i = 0; i < face_count_A; i++) { - Vector3 axis = p_transform_a.xform(faces_A[i].plane).normal; - //Vector3 axis = p_transform_a.basis.xform( faces_A[i].plane.normal ).normalized(); + Vector3 axis = a_xform_normal.xform(faces_A[i].plane.normal).normalized(); if (!separator.test_axis(axis)) { return; } } + // Precalculating this makes the transforms faster. + Basis b_xform_normal = p_transform_b.basis.inverse().transposed(); + // faces of B for (int i = 0; i < face_count_B; i++) { - Vector3 axis = p_transform_b.xform(faces_B[i].plane).normal; - //Vector3 axis = p_transform_b.basis.xform( faces_B[i].plane.normal ).normalized(); + Vector3 axis = b_xform_normal.xform(faces_B[i].plane.normal).normalized(); if (!separator.test_axis(axis)) { return; diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 699f4a4b90..c609f25e31 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -70,6 +70,28 @@ RID EffectsRD::_get_uniform_set_from_image(RID p_image) { return uniform_set; } +RID EffectsRD::_get_uniform_set_for_input(RID p_texture) { + if (input_to_uniform_set_cache.has(p_texture)) { + RID uniform_set = input_to_uniform_set_cache[p_texture]; + if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) { + return uniform_set; + } + } + + Vector<RD::Uniform> uniforms; + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT; + u.binding = 0; + u.ids.push_back(p_texture); + uniforms.push_back(u); + // This is specific to our subpass shader + RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, TONEMAP_MODE_SUBPASS), 0); + + input_to_uniform_set_cache[p_texture] = uniform_set; + + return uniform_set; +} + RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) { if (texture_to_uniform_set_cache.has(p_texture)) { RID uniform_set = texture_to_uniform_set_cache[p_texture]; @@ -85,7 +107,7 @@ RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler); u.ids.push_back(p_texture); uniforms.push_back(u); - //anything with the same configuration (one texture in binding 0 for set 0), is good + // anything with the same configuration (one texture in binding 0 for set 0), is good RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, 0), 0); texture_to_uniform_set_cache[p_texture] = uniform_set; @@ -778,11 +800,11 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone if (p_settings.view_count > 1) { // Use MULTIVIEW versions - mode += 4; + mode += 6; } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass())); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2); @@ -794,6 +816,45 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone RD::get_singleton()->draw_list_end(); } +void EffectsRD::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings) { + memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant)); + + tonemap.push_constant.use_bcs = p_settings.use_bcs; + tonemap.push_constant.bcs[0] = p_settings.brightness; + tonemap.push_constant.bcs[1] = p_settings.contrast; + tonemap.push_constant.bcs[2] = p_settings.saturation; + + ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses."); + tonemap.push_constant.use_glow = p_settings.use_glow; + + int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS; + if (p_settings.view_count > 1) { + // Use MULTIVIEW versions + mode += 6; + } + + tonemap.push_constant.tonemapper = p_settings.tonemap_mode; + tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; + tonemap.push_constant.exposure = p_settings.exposure; + tonemap.push_constant.white = p_settings.white; + tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey; + + tonemap.push_constant.use_color_correction = p_settings.use_color_correction; + + tonemap.push_constant.use_debanding = p_settings.use_debanding; + + RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass())); + RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_for_input(p_source_color), 0); + RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1); // should be set to a default texture, it's ignored + RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2); // should be set to a default texture, it's ignored + RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.color_correction_texture), 3); + + RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); + RD::get_singleton()->draw_list_draw(p_subpass_draw_list, true); +} + void EffectsRD::luminance_reduction(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of luminance reduction with the mobile renderer."); @@ -1813,12 +1874,16 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n"); tonemap_modes.push_back("\n#define USE_1D_LUT\n"); tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); + tonemap_modes.push_back("\n#define SUBPASS\n"); + tonemap_modes.push_back("\n#define SUBPASS\n#define USE_1D_LUT\n"); // multiview versions of our shaders tonemap_modes.push_back("\n#define MULTIVIEW\n"); tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n"); tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_1D_LUT\n"); tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); + tonemap_modes.push_back("\n#define MULTIVIEW\n#define SUBPASS\n"); + tonemap_modes.push_back("\n#define MULTIVIEW\n#define SUBPASS\n#define USE_1D_LUT\n"); tonemap.shader.initialize(tonemap_modes); @@ -1827,6 +1892,8 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false); tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false); tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false); + tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_MULTIVIEW, false); + tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW, false); } tonemap.shader_version = tonemap.shader.version_create(); diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index eff891571a..79d9a8b738 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -238,11 +238,15 @@ private: TONEMAP_MODE_BICUBIC_GLOW_FILTER, TONEMAP_MODE_1D_LUT, TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT, + TONEMAP_MODE_SUBPASS, + TONEMAP_MODE_SUBPASS_1D_LUT, TONEMAP_MODE_NORMAL_MULTIVIEW, TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, TONEMAP_MODE_1D_LUT_MULTIVIEW, TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, + TONEMAP_MODE_SUBPASS_MULTIVIEW, + TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW, TONEMAP_MODE_MAX }; @@ -718,6 +722,7 @@ private: RID index_array; Map<RID, RID> texture_to_uniform_set_cache; + Map<RID, RID> input_to_uniform_set_cache; Map<RID, RID> image_to_uniform_set_cache; @@ -751,6 +756,7 @@ private: Map<TextureSamplerPair, RID> texture_sampler_to_compute_uniform_set_cache; RID _get_uniform_set_from_image(RID p_texture); + RID _get_uniform_set_for_input(RID p_texture); RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false); RID _get_compute_uniform_set_from_texture_and_sampler(RID p_texture, RID p_sampler); @@ -842,6 +848,7 @@ public: }; void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings); + void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings); void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set); void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao, const Vector<RID> p_ao_slices, RID p_ao_pong, const Vector<RID> p_ao_pong_slices, RID p_upscale_buffer, RID p_importance_map, RID p_importance_map_pong, const CameraMatrix &p_projection, const SSAOSettings &p_settings, bool p_invalidate_uniform_sets, RID &r_downsample_uniform_set, RID &r_gather_uniform_set, RID &r_importance_map_uniform_set); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ac20515c28..0d304556c2 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -183,7 +183,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() { } } -void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { +void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { clear(); ERR_FAIL_COND_MSG(p_view_count != 1, "Multiple views is currently not supported in this renderer, please use the mobile renderer for VR support"); @@ -483,8 +483,8 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p } if (material_uniform_set != prev_material_uniform_set) { - //update uniform set - if (material_uniform_set.is_valid()) { + // Update uniform set. + if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } @@ -1555,6 +1555,14 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } RD::get_singleton()->draw_command_end_label(); + + if (p_render_data->render_buffers.is_valid()) { + _debug_draw_cluster(p_render_data->render_buffers); + + RENDER_TIMESTAMP("Tonemap"); + + _render_buffers_post_process_and_tonemap(p_render_data); + } } void RenderForwardClustered::_render_shadow_begin() { diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 6682c5e9b0..676f633d33 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -112,7 +112,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void ensure_specular(); void ensure_voxelgi(); void clear(); - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); + virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); ~RenderBufferDataForwardClustered(); }; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 4725599148..dea2975660 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -78,12 +78,16 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::clear() { color = RID(); depth = RID(); - color_fb = RID(); + for (int i = 0; i < FB_CONFIG_MAX; i++) { + color_fbs[i] = RID(); + } } -void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { +void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) { clear(); + bool is_half_resolution = false; // Set this once we support this feature. + msaa = p_msaa; width = p_width; @@ -93,15 +97,50 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b color = p_color_buffer; depth = p_depth_buffer; - RD::DataFormat color_format = RenderForwardMobile::singleton->_render_buffers_get_color_format(); + // We are creating 4 configurations here for our framebuffers. if (p_msaa == RS::VIEWPORT_MSAA_DISABLED) { Vector<RID> fb; - fb.push_back(p_color_buffer); - fb.push_back(depth); + fb.push_back(p_color_buffer); // 0 - color buffer + fb.push_back(depth); // 1 - depth buffer + + // Now define our subpasses + Vector<RD::FramebufferPass> passes; + RD::FramebufferPass pass; + + // re-using the same attachments + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + + // - opaque pass + passes.push_back(pass); + color_fbs[FB_CONFIG_ONE_PASS] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); + + // - add sky pass + passes.push_back(pass); + color_fbs[FB_CONFIG_TWO_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); + + // - add alpha pass + passes.push_back(pass); + color_fbs[FB_CONFIG_THREE_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); - color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); + if (!is_half_resolution) { + // - add blit to 2D pass + fb.push_back(p_target_buffer); // 2 - target buffer + + RD::FramebufferPass blit_pass; + blit_pass.color_attachments.push_back(2); + blit_pass.input_attachments.push_back(0); + passes.push_back(blit_pass); + + color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); + } else { + // can't do our blit pass if resolutions don't match + color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RID(); + } } else { + RD::DataFormat color_format = RenderForwardMobile::singleton->_render_buffers_get_color_format(); + RD::TextureFormat tf; if (view_count > 1) { tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; @@ -134,14 +173,87 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b { Vector<RID> fb; - fb.push_back(color_msaa); - fb.push_back(depth_msaa); + fb.push_back(color_msaa); // 0 - msaa color buffer + fb.push_back(depth_msaa); // 1 - msaa depth buffer + + // Now define our subpasses + Vector<RD::FramebufferPass> passes; + RD::FramebufferPass pass; + + // re-using the same attachments + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + + // - opaque pass + passes.push_back(pass); + + // - add sky pass + fb.push_back(color); // 2 - color buffer + passes.push_back(pass); // without resolve for our 3 + 4 subpass config + { + // but with resolve for our 2 subpass config + Vector<RD::FramebufferPass> two_passes; + two_passes.push_back(pass); // opaque subpass without resolve + pass.resolve_attachments.push_back(2); + two_passes.push_back(pass); // sky subpass with resolve + + color_fbs[FB_CONFIG_TWO_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, two_passes, RenderingDevice::INVALID_ID, view_count); + } + + // - add alpha pass (with resolve, we just added that above) + passes.push_back(pass); + color_fbs[FB_CONFIG_THREE_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); - color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); + { + // we also need our one pass with resolve + Vector<RD::FramebufferPass> one_pass_with_resolve; + one_pass_with_resolve.push_back(pass); // note our pass configuration already has resolve.. + color_fbs[FB_CONFIG_ONE_PASS] = RD::get_singleton()->framebuffer_create_multipass(fb, one_pass_with_resolve, RenderingDevice::INVALID_ID, view_count); + } + + if (!is_half_resolution) { + // - add blit to 2D pass + fb.push_back(p_target_buffer); // 3 - target buffer + RD::FramebufferPass blit_pass; + blit_pass.color_attachments.push_back(3); + blit_pass.input_attachments.push_back(2); + passes.push_back(blit_pass); + + color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, view_count); + } else { + // can't do our blit pass if resolutions don't match + color_fbs[FB_CONFIG_FOUR_SUBPASSES] = RID(); + } } } } +RID RenderForwardMobile::reflection_probe_create_framebuffer(RID p_color, RID p_depth) { + // Our attachments + Vector<RID> fb; + fb.push_back(p_color); // 0 + fb.push_back(p_depth); // 1 + + // Now define our subpasses + Vector<RD::FramebufferPass> passes; + RD::FramebufferPass pass; + + // re-using the same attachments + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + + // - opaque pass + passes.push_back(pass); + + // - sky pass + passes.push_back(pass); + + // - alpha pass + passes.push_back(pass); + + return RD::get_singleton()->framebuffer_create_multipass(fb, passes); +} + RenderForwardMobile::RenderBufferDataForwardMobile::~RenderBufferDataForwardMobile() { clear(); } @@ -365,14 +477,29 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color scene_state.ubo.viewport_size[1] = vp_he.y; scene_state.ubo.directional_light_count = 0; + // We can only use our full subpass approach if we're: + // - not reading from SCREEN_TEXTURE/DEPTH_TEXTURE + // - not using ssr/sss (currently not supported) + // - not using glow or other post effects (can't do 4th subpass) + // - rendering to a half sized render buffer (can't do 4th subpass) + // We'll need to restrict how far we're going with subpasses based on this. + Size2i screen_size; - RID opaque_framebuffer; - RID alpha_framebuffer; + RID framebuffer; bool reverse_cull = false; + bool using_subpass_transparent = true; + bool using_subpass_post_process = true; - // I don't think we support either of these in our mobile renderer so probably should phase them out - bool using_ssr = false; - bool using_sss = false; + bool is_half_resolution = false; // Set this once we support this feature. + bool using_ssr = false; // I don't think we support this in our mobile renderer so probably should phase it out + bool using_sss = false; // I don't think we support this in our mobile renderer so probably should phase it out + + // fill our render lists early so we can find out if we use various features + _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR); + render_list[RENDER_LIST_OPAQUE].sort_by_key(); + render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); + _fill_element_info(RENDER_LIST_OPAQUE); + _fill_element_info(RENDER_LIST_ALPHA); if (p_render_data->render_info) { p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = p_render_data->instances->size(); @@ -384,15 +511,36 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color screen_size.x = render_buffer->width; screen_size.y = render_buffer->height; - opaque_framebuffer = render_buffer->color_fb; - alpha_framebuffer = opaque_framebuffer; + if (is_half_resolution) { + // can't do blit subpass + using_subpass_post_process = false; + } else if (env && (env->glow_enabled || env->auto_exposure || camera_effects_uses_dof(p_render_data->camera_effects))) { + // can't do blit subpass + using_subpass_post_process = false; + } + + if (scene_state.used_screen_texture || scene_state.used_depth_texture) { + // can't use our last two subpasses + using_subpass_transparent = false; + using_subpass_post_process = false; + } + + if (using_subpass_post_process) { + // all as subpasses + framebuffer = render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES]; + } else if (using_subpass_transparent) { + // our tonemap pass is separate + framebuffer = render_buffer->color_fbs[FB_CONFIG_THREE_SUBPASSES]; + } else { + // only opaque and sky as subpasses + framebuffer = render_buffer->color_fbs[FB_CONFIG_TWO_SUBPASSES]; + } } else if (p_render_data->reflection_probe.is_valid()) { uint32_t resolution = reflection_probe_instance_get_resolution(p_render_data->reflection_probe); screen_size.x = resolution; screen_size.y = resolution; - opaque_framebuffer = reflection_probe_instance_get_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass); - alpha_framebuffer = opaque_framebuffer; + framebuffer = reflection_probe_instance_get_framebuffer(p_render_data->reflection_probe, p_render_data->reflection_probe_pass); if (storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_render_data->reflection_probe))) { p_render_data->environment = RID(); //no environment on interiors @@ -400,6 +548,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } reverse_cull = true; + using_subpass_transparent = true; // we ignore our screen/depth texture here + using_subpass_post_process = false; // not applicable at all for reflection probes. } else { ERR_FAIL(); //bug? } @@ -411,17 +561,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) - _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR); - render_list[RENDER_LIST_OPAQUE].sort_by_key(); - render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); - - // we no longer use this... - _fill_instance_data(RENDER_LIST_OPAQUE); - _fill_instance_data(RENDER_LIST_ALPHA); - - RD::get_singleton()->draw_command_end_label(); - - // note, no depth prepass here! + RD::get_singleton()->draw_command_end_label(); // Render Setup // setup environment RID radiance_texture; @@ -496,97 +636,199 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // do not try to draw sky if invalid draw_sky = false; } - RD::get_singleton()->draw_command_end_label(); + RD::get_singleton()->draw_command_end_label(); // Setup Sky } } else { clear_color = p_default_bg_color; } - // opaque pass + // update sky buffers (if required) + if (draw_sky || draw_sky_fog_only) { + // !BAS! @TODO See if we can limit doing some things double and maybe even move this into _pre_opaque_render + // and change Forward Clustered in the same way as we have here (but without using subpasses) + RENDER_TIMESTAMP("Setup Sky resolution buffers"); + + RD::get_singleton()->draw_command_begin_label("Setup Sky resolution buffers"); + + if (p_render_data->reflection_probe.is_valid()) { + CameraMatrix correction; + correction.set_depth_correction(true); + CameraMatrix projection = correction * p_render_data->cam_projection; + sky.update_res_buffers(env, 1, &projection, p_render_data->cam_transform, time); + } else { + sky.update_res_buffers(env, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + } + + RD::get_singleton()->draw_command_end_label(); // Setup Sky resolution buffers + } - // !BAS! Look into this, seems most of the code in here related to clustered only, may want to move this code into ForwardClustered/RenderForwardMobile before calling it from here - // does trigger shadow map rendering so kinda important _pre_opaque_render(p_render_data, false, false, RID(), RID()); - RD::get_singleton()->draw_command_begin_label("Render Opaque Pass"); + { + if (render_buffer) { + RD::get_singleton()->draw_command_begin_label("Render 3D Pass"); + } else { + RD::get_singleton()->draw_command_begin_label("Render Reflection Probe Pass"); + } - scene_state.ubo.directional_light_count = p_render_data->directional_light_count; + // opaque pass - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, p_render_data->render_buffers.is_valid()); + RD::get_singleton()->draw_command_begin_label("Render Opaque Subpass"); - RENDER_TIMESTAMP("Render Opaque Pass"); + scene_state.ubo.directional_light_count = p_render_data->directional_light_count; - RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); + _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, p_render_data->render_buffers.is_valid()); - bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss; - bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss; + RENDER_TIMESTAMP("Render Opaque Subpass"); - { - bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only); - bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only); + RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, true); - // regular forward for now - Vector<Color> c; - c.push_back(clear_color.to_linear()); + bool can_continue_color = !scene_state.used_screen_texture && !using_ssr && !using_sss; + bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss; - RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); - _render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); - } + { + bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only); + bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only); + + // regular forward for now + Vector<Color> c; + c.push_back(clear_color.to_linear()); // our render buffer + if (render_buffer) { + if (render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + c.push_back(clear_color.to_linear()); // our resolve buffer + } + if (using_subpass_post_process) { + c.push_back(Color()); // our 2D buffer we're copying into + } + } - RD::get_singleton()->draw_command_end_label(); + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); + render_list_params.framebuffer_format = fb_format; + if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { + // secondary command buffers need more testing at this time + //multi threaded + thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); + RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); + RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params); + } else { + //single threaded + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); + _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); + } + } - if (draw_sky || draw_sky_fog_only) { - RENDER_TIMESTAMP("Render Sky"); + RD::get_singleton()->draw_command_end_label(); //Render Opaque Subpass - RD::get_singleton()->draw_command_begin_label("Draw Sky"); + if (draw_sky || draw_sky_fog_only) { + RENDER_TIMESTAMP("Render Sky Subpass"); - if (p_render_data->reflection_probe.is_valid()) { - CameraMatrix correction; - correction.set_depth_correction(true); - CameraMatrix projection = correction * p_render_data->cam_projection; - sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time); + RD::get_singleton()->draw_command_begin_label("Draw Sky Subpass"); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); + + if (p_render_data->reflection_probe.is_valid()) { + CameraMatrix correction; + correction.set_depth_correction(true); + CameraMatrix projection = correction * p_render_data->cam_projection; + sky.draw(draw_list, env, framebuffer, 1, &projection, p_render_data->cam_transform, time); + } else { + sky.draw(draw_list, env, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + } + + RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass + + // note, if MSAA is used in 2-subpass approach we should get an automatic resolve here } else { - sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + // switch to subpass but we do nothing here so basically we skip (though this should trigger resolve with 2-subpass MSAA). + RD::get_singleton()->draw_list_switch_to_next_pass(); } - RD::get_singleton()->draw_command_end_label(); - } - if (render_buffer && !can_continue_color && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color); - /* - if (using_separate_specular) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->specular_msaa, render_buffer->specular); + if (!using_subpass_transparent) { + // We're done with our subpasses so end our container pass + RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL); + + RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass } - */ - } - if (render_buffer && !can_continue_depth && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->depth_msaa, render_buffer->depth); - } + // transparent pass + RENDER_TIMESTAMP("Render Transparent Subpass"); - // transparent pass - RENDER_TIMESTAMP("Render Transparent Pass"); + RD::get_singleton()->draw_command_begin_label("Render Transparent Subpass"); - RD::get_singleton()->draw_command_begin_label("Render Transparent Pass"); + rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); - rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, true); + if (using_subpass_transparent) { + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); + render_list_params.framebuffer_format = fb_format; + if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { + // secondary command buffers need more testing at this time + //multi threaded + thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); + RD::get_singleton()->draw_list_switch_to_next_pass_split(thread_draw_lists.size(), thread_draw_lists.ptr()); + render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); + RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params); + } else { + //single threaded + RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); + render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); + _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); + } - _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass - { - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); - _render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + // note if we are using MSAA we should get an automatic resolve through our subpass configuration. + + // blit to tonemap + if (render_buffer && using_subpass_post_process) { + _post_process_subpass(render_buffer->color, framebuffer, p_render_data); + } + + RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass + + RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL); + } else { + framebuffer = render_buffer->color_fbs[FB_CONFIG_ONE_PASS]; + + // this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation + // _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); + + RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count); + render_list_params.framebuffer_format = fb_format; + if ((uint32_t)render_list_params.element_count > render_list_thread_threshold && false) { + // secondary command buffers need more testing at this time + //multi threaded + thread_draw_lists.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); + RD::get_singleton()->draw_list_begin_split(framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + RendererThreadPool::singleton->thread_work_pool.do_work(thread_draw_lists.size(), this, &RenderForwardMobile::_render_list_thread_function, &render_list_params); + RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL); + } else { + //single threaded + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); + RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL); + } + + RD::get_singleton()->draw_command_end_label(); // Render Transparent Subpass + } } - RD::get_singleton()->draw_command_end_label(); + if (render_buffer && !using_subpass_post_process) { + RD::get_singleton()->draw_command_begin_label("Post process pass"); + + // If we need extra effects we do this in its own pass + RENDER_TIMESTAMP("Tonemap"); - RD::get_singleton()->draw_command_begin_label("Resolve"); + _render_buffers_post_process_and_tonemap(p_render_data); - if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color); + RD::get_singleton()->draw_command_end_label(); // Post process pass } - RD::get_singleton()->draw_command_end_label(); + if (render_buffer) { + _disable_clear_request(p_render_data); + } } /* these are being called from RendererSceneRenderRD::_pre_opaque_render */ @@ -635,7 +877,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, true); uint32_t render_list_size = render_list[RENDER_LIST_SECONDARY].elements.size() - render_list_from; render_list[RENDER_LIST_SECONDARY].sort_by_key_range(render_list_from, render_list_size); - _fill_instance_data(RENDER_LIST_SECONDARY, render_list_from, render_list_size, false); + _fill_element_info(RENDER_LIST_SECONDARY, render_list_from, render_list_size); { //regular forward for now @@ -713,7 +955,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); - _fill_instance_data(RENDER_LIST_SECONDARY); + _fill_element_info(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); @@ -754,7 +996,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<GeometryInstance *> &p_in PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); - _fill_instance_data(RENDER_LIST_SECONDARY); + _fill_element_info(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); @@ -827,7 +1069,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); - _fill_instance_data(RENDER_LIST_SECONDARY); + _fill_element_info(RENDER_LIST_SECONDARY); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID()); @@ -1413,9 +1655,7 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, RD::get_singleton()->buffer_update(scene_state.uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo, RD::BARRIER_MASK_RASTER); } -void RenderForwardMobile::_fill_instance_data(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) { - // !BAS! Rename this to make clear this is not the same as with the forward renderer and remove p_update_buffer? - +void RenderForwardMobile::_fill_element_info(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements) { RenderList *rl = &render_list[p_render_list]; uint32_t element_total = p_max_elements >= 0 ? uint32_t(p_max_elements) : rl->elements.size(); @@ -1669,7 +1909,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr prev_index_array_rd = index_array_rd; } - RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe); + RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass); if (pipeline_rd != prev_pipeline_rd) { // checking with prev shader does not make so much sense, as @@ -1684,8 +1924,8 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr } if (material_uniform_set != prev_material_uniform_set) { - //update uniform set - if (material_uniform_set.is_valid()) { + // Update uniform set. + if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 973925d562..b981592840 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -92,6 +92,18 @@ protected: /* Render Buffer */ + // We can have: + // - 4 subpasses combining the full render cycle + // - 3 subpasses + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer) + // - 2 subpasses + 1 normal pass for transparent + 1 normal pass for tonemapping/glow/dof/etc (using fb for 2D buffer) + enum RenderBufferMobileFramebufferConfigType { + FB_CONFIG_ONE_PASS, // Single pass frame buffer for alpha pass + FB_CONFIG_TWO_SUBPASSES, // Opaque + Sky sub pass + FB_CONFIG_THREE_SUBPASSES, // Opaque + Sky + Alpha sub pass + FB_CONFIG_FOUR_SUBPASSES, // Opaque + Sky + Alpha sub pass + Tonemap pass + FB_CONFIG_MAX + }; + struct RenderBufferDataForwardMobile : public RenderBufferData { RID color; RID depth; @@ -104,12 +116,12 @@ protected: RID depth_msaa; // RID normal_roughness_buffer_msaa; - RID color_fb; + RID color_fbs[FB_CONFIG_MAX]; int width, height; uint32_t view_count; void clear(); - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); + virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count); ~RenderBufferDataForwardMobile(); }; @@ -152,6 +164,7 @@ protected: RD::FramebufferFormatID framebuffer_format = 0; uint32_t element_offset = 0; uint32_t barrier = RD::BARRIER_MASK_ALL; + uint32_t subpass = 0; RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { elements = p_elements; @@ -195,7 +208,7 @@ protected: virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) override; void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_append = false); - void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); + void _fill_element_info(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1); // void _update_instance_data_buffer(RenderListType p_render_list); static RenderForwardMobile *singleton; @@ -576,6 +589,8 @@ protected: void _update_shader_quality_settings() override; public: + virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth) override; + static void _geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker); static void _geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 18c1fe02a0..053bda59a7 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1089,7 +1089,8 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co if (material_data) { if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { pipeline_variants = &material_data->shader_data->pipeline_variants; - if (material_data->uniform_set.is_valid()) { + // Update uniform set. + if (RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET); } } else { diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index e3516df800..5b734fc603 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -679,10 +679,7 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc for (int i = 0; i < atlas->count; i++) { atlas->reflections.write[i].data.update_reflection_data(storage, atlas->size, mipmaps, false, atlas->reflection, i * 6, storage->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS, sky.roughness_layers, _render_buffers_get_color_format()); for (int j = 0; j < 6; j++) { - Vector<RID> fb; - fb.push_back(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j]); - fb.push_back(atlas->depth_buffer); - atlas->reflections.write[i].fbs[j] = RD::get_singleton()->framebuffer_create(fb); + atlas->reflections.write[i].fbs[j] = reflection_probe_create_framebuffer(atlas->reflections.write[i].data.layers[0].mipmaps[0].views[j], atlas->depth_buffer); } } @@ -728,6 +725,13 @@ bool RendererSceneRenderRD::reflection_probe_instance_begin_render(RID p_instanc return true; } +RID RendererSceneRenderRD::reflection_probe_create_framebuffer(RID p_color, RID p_depth) { + Vector<RID> fb; + fb.push_back(p_color); + fb.push_back(p_depth); + return RD::get_singleton()->framebuffer_create(fb); +} + bool RendererSceneRenderRD::reflection_probe_instance_postprocess_step(RID p_instance) { ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); ERR_FAIL_COND_V(!rpi, false); @@ -1996,6 +2000,75 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende storage->render_target_disable_clear_request(rb->render_target); } +void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data) { + RD::get_singleton()->draw_command_begin_label("Post Process Subpass"); + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers); + ERR_FAIL_COND(!rb); + + RendererSceneEnvironmentRD *env = environment_owner.getornull(p_render_data->environment); + + bool can_use_effects = rb->width >= 8 && rb->height >= 8; + + RENDER_TIMESTAMP("Tonemap"); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); + + EffectsRD::TonemapSettings tonemap; + + if (env) { + tonemap.tonemap_mode = env->tone_mapper; + tonemap.exposure = env->exposure; + tonemap.white = env->white; + } + + // We don't support glow or auto exposure here, if they are needed, don't use subpasses! + // The problem is that we need to use the result so far and process them before we can + // apply this to our results. + if (can_use_effects && env && env->glow_enabled) { + ERR_FAIL_MSG("Glow is not supported when using subpasses."); + } + if (can_use_effects && env && env->auto_exposure) { + ERR_FAIL_MSG("Glow is not supported when using subpasses."); + } + + tonemap.use_glow = false; + tonemap.glow_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK); + tonemap.use_auto_exposure = false; + tonemap.exposure_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE); + + tonemap.use_color_correction = false; + tonemap.use_1d_color_correction = false; + tonemap.color_correction_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + + if (can_use_effects && env) { + tonemap.use_bcs = env->adjustments_enabled; + tonemap.brightness = env->adjustments_brightness; + tonemap.contrast = env->adjustments_contrast; + tonemap.saturation = env->adjustments_saturation; + if (env->adjustments_enabled && env->color_correction.is_valid()) { + tonemap.use_color_correction = true; + tonemap.use_1d_color_correction = env->use_1d_color_correction; + tonemap.color_correction_texture = storage->texture_get_rd_texture(env->color_correction); + } + } + + tonemap.use_debanding = rb->use_debanding; + tonemap.texture_size = Vector2i(rb->width, rb->height); + + tonemap.view_count = p_render_data->view_count; + + storage->get_effects()->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap); + + RD::get_singleton()->draw_command_end_label(); +} + +void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_data) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_data->render_buffers); + ERR_FAIL_COND(!rb); + + storage->render_target_disable_clear_request(rb->render_target); +} + void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { EffectsRD *effects = storage->get_effects(); @@ -2283,12 +2356,11 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p tf.width = rb->width; tf.height = rb->height; tf.array_layers = rb->view_count; // create a layer for every view - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0); + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0) | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0); - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; } + tf.usage_bits |= RD::TEXTURE_USAGE_INPUT_ATTACHMENT_BIT; // only needed when using subpasses in the mobile renderer rb->texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); } @@ -2326,7 +2398,8 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p rb->texture_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, rb->view_count); } - rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa, p_view_count); + RID target_texture = storage->render_target_get_rd_texture(rb->render_target); + rb->data->configure(rb->texture, rb->depth_texture, target_texture, p_width, p_height, p_msaa, p_view_count); if (is_clustered_enabled()) { rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture); @@ -3846,9 +3919,28 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData _render_scene(&render_data, clear_color); if (p_render_buffers.is_valid()) { - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS || debug_draw == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES) { + /* + _debug_draw_cluster(p_render_buffers); + + RENDER_TIMESTAMP("Tonemap"); + + _render_buffers_post_process_and_tonemap(&render_data); + */ + + _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { + rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture); + } + } +} + +void RendererSceneRenderRD::_debug_draw_cluster(RID p_render_buffers) { + if (p_render_buffers.is_valid() && current_cluster_builder != nullptr) { + RS::ViewportDebugDraw dd = get_debug_draw_mode(); + + if (dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS || dd == RS::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES) { ClusterBuilderRD::ElementType elem_type = ClusterBuilderRD::ELEMENT_TYPE_MAX; - switch (debug_draw) { + switch (dd) { case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS: elem_type = ClusterBuilderRD::ELEMENT_TYPE_OMNI_LIGHT; break; @@ -3864,17 +3956,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData default: { } } - if (current_cluster_builder != nullptr) { - current_cluster_builder->debug(elem_type); - } - } - - RENDER_TIMESTAMP("Tonemap"); - - _render_buffers_post_process_and_tonemap(&render_data); - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { - rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture); + current_cluster_builder->debug(elem_type); } } } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index bb06eb608f..336dd6e1a7 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -95,7 +95,7 @@ protected: double time_step = 0; struct RenderBufferData { - virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0; + virtual void configure(RID p_color_buffer, RID p_depth_buffer, RID p_target_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0; virtual ~RenderBufferData() {} }; virtual RenderBufferData *_create_render_buffer_data() = 0; @@ -117,6 +117,7 @@ protected: virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, const PagedArray<GeometryInstance *> &p_instances) = 0; void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform); + void _debug_draw_cluster(RID p_render_buffers); RenderBufferData *render_buffers_get_data(RID p_render_buffers); @@ -133,6 +134,10 @@ protected: void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_gi, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer); + void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data); + void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data); + void _disable_clear_request(const RenderDataRD *p_render_data); + // needed for a single argument calls (material and uv2) PagedArrayPool<GeometryInstance *> cull_argument_pool; PagedArray<GeometryInstance *> cull_argument; //need this to exist @@ -146,7 +151,7 @@ protected: } else { return nullptr; } - } + }; //used for mobile renderer mostly @@ -524,7 +529,6 @@ private: void _allocate_luminance_textures(RenderBuffers *rb); void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); - void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data); /* Cluster */ @@ -923,6 +927,12 @@ public: virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override; virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; + bool camera_effects_uses_dof(RID p_camera_effects) { + CameraEffects *camfx = camera_effects_owner.getornull(p_camera_effects); + + return camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0; + } + virtual RID light_instance_create(RID p_light) override; virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; @@ -1065,6 +1075,7 @@ public: virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override; virtual bool reflection_probe_instance_has_reflection(RID p_instance) override; virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; + virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth); virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override; uint32_t reflection_probe_instance_get_resolution(RID p_instance); diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index cadf759ec3..88fe538903 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -283,14 +283,17 @@ void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_ RD::DrawListID draw_list = p_list; - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format)); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format, false, RD::get_singleton()->draw_list_get_current_pass())); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0); - if (p_uniform_set.is_valid()) { //material may not have uniform set - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); + // Update uniform sets. + { + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0); + if (RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set. + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); + } + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, 3); } - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, 3); RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); @@ -1467,6 +1470,179 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont RD::get_singleton()->draw_list_end(); } +void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) { + ERR_FAIL_COND(!p_env); + + ERR_FAIL_COND(p_view_count == 0); + ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS); + + Sky *sky = get_sky(p_env->sky); + ERR_FAIL_COND(!sky); + + SkyMaterialData *material = nullptr; + RID sky_material; + + RS::EnvironmentBG background = p_env->background; + + if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) { + ERR_FAIL_COND(!sky); + sky_material = sky_get_material(p_env->sky); + + if (sky_material.is_valid()) { + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + if (!material || !material->shader_data->valid) { + material = nullptr; + } + } + + if (!material) { + sky_material = sky_shader.default_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + } + } + + if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) { + sky_material = sky_scene_state.fog_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + } + + ERR_FAIL_COND(!material); + + SkyShaderData *shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + Basis sky_transform = p_env->sky_orientation; + sky_transform.invert(); + + float multiplier = p_env->bg_energy; + float custom_fov = p_env->sky_custom_fov; + + // Camera + CameraMatrix camera; + uint32_t view_count = p_view_count; + const CameraMatrix *projections = p_projections; + + if (custom_fov) { + // With custom fov we don't support stereo... + float near_plane = p_projections[0].get_z_near(); + float far_plane = p_projections[0].get_z_far(); + float aspect = p_projections[0].get_aspect(); + + camera.set_perspective(custom_fov, aspect, near_plane, far_plane); + + view_count = 1; + projections = &camera; + } + + sky_transform = p_transform.basis * sky_transform; + + if (shader_data->uses_quarter_res) { + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES]; + + RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd); + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } + + if (shader_data->uses_half_res) { + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES]; + + RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd); + + Vector<Color> clear_colors; + clear_colors.push_back(Color(0.0, 0.0, 0.0)); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + RD::get_singleton()->draw_list_end(); + } +} + +void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) { + ERR_FAIL_COND(!p_env); + + ERR_FAIL_COND(p_view_count == 0); + ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS); + + Sky *sky = get_sky(p_env->sky); + ERR_FAIL_COND(!sky); + + SkyMaterialData *material = nullptr; + RID sky_material; + + RS::EnvironmentBG background = p_env->background; + + if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) { + ERR_FAIL_COND(!sky); + sky_material = sky_get_material(p_env->sky); + + if (sky_material.is_valid()) { + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + if (!material || !material->shader_data->valid) { + material = nullptr; + } + } + + if (!material) { + sky_material = sky_shader.default_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + } + } + + if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) { + sky_material = sky_scene_state.fog_material; + material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY); + } + + ERR_FAIL_COND(!material); + + SkyShaderData *shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + Basis sky_transform = p_env->sky_orientation; + sky_transform.invert(); + + float multiplier = p_env->bg_energy; + float custom_fov = p_env->sky_custom_fov; + + // Camera + CameraMatrix camera; + uint32_t view_count = p_view_count; + const CameraMatrix *projections = p_projections; + + if (custom_fov) { + // With custom fov we don't support stereo... + float near_plane = p_projections[0].get_z_near(); + float far_plane = p_projections[0].get_z_far(); + float aspect = p_projections[0].get_aspect(); + + camera.set_perspective(custom_fov, aspect, near_plane, far_plane); + + view_count = 1; + projections = &camera; + } + + sky_transform = p_transform.basis * sky_transform; + + PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND]; + + RID texture_uniform_set; + if (sky) { + texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_BACKGROUND, sky_shader.default_shader_rd); + } else { + texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set; + } + + _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); +} + void RendererSceneSkyRD::invalidate_sky(Sky *p_sky) { if (!p_sky->dirty) { p_sky->dirty = true; diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 7e0b01d58e..7b670bddd5 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -295,6 +295,8 @@ public: void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); + void update_res_buffers(RendererSceneEnvironmentRD *p_env, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); + void draw(RD::DrawListID p_draw_list, RendererSceneEnvironmentRD *p_env, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time); void invalidate_sky(Sky *p_sky); void update_dirty_skys(); diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl index 23f83b3b9c..f028195a74 100644 --- a/servers/rendering/renderer_rd/shaders/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl @@ -35,11 +35,15 @@ void main() { layout(location = 0) in vec2 uv_interp; -#ifdef MULTIVIEW +#ifdef SUBPASS +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput input_color; +#else +#if MULTIVIEW layout(set = 0, binding = 0) uniform sampler2DArray source_color; #else layout(set = 0, binding = 0) uniform sampler2D source_color; #endif +#endif layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; layout(set = 2, binding = 0) uniform sampler2D source_glow; #ifdef USE_1D_LUT @@ -291,6 +295,7 @@ vec3 apply_color_correction(vec3 color) { } #endif +#ifndef SUBPASS vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { const float FXAA_REDUCE_MIN = (1.0 / 128.0); const float FXAA_REDUCE_MUL = (1.0 / 8.0); @@ -346,6 +351,7 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { return rgbB; } } +#endif // !SUBPASS // From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf // and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom) @@ -360,7 +366,10 @@ vec3 screen_space_dither(vec2 frag_coord) { } void main() { -#ifdef MULTIVIEW +#ifdef SUBPASS + // SUBPASS and MULTIVIEW can be combined but in that case we're already reading from the correct layer + vec3 color = subpassLoad(input_color).rgb; +#elif MULTIVIEW vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb; #else vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; @@ -370,14 +379,16 @@ void main() { float exposure = params.exposure; +#ifndef SUBPASS if (params.use_auto_exposure) { exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.auto_exposure_grey); } +#endif color *= exposure; // Early Tonemap & SRGB Conversion - +#ifndef SUBPASS if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) { vec3 glow = gather_glow(source_glow, uv_interp); color.rgb = mix(color.rgb, glow, params.glow_intensity); @@ -386,15 +397,19 @@ void main() { if (params.use_fxaa) { color = do_fxaa(color, exposure, uv_interp); } +#endif + if (params.use_debanding) { // For best results, debanding should be done before tonemapping. // Otherwise, we're adding noise to an already-quantized image. color += screen_space_dither(gl_FragCoord.xy); } + color = apply_tonemapping(color, params.white); color = linear_to_srgb(color); // regular linear -> SRGB conversion +#ifndef SUBPASS // Glow if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) { @@ -406,6 +421,7 @@ void main() { color = apply_glow(color, glow); } +#endif // Additional effects diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index b298ad193b..0297724a22 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -739,7 +739,7 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_UPDATE_BIT); BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_COPY_FROM_BIT); BIND_ENUM_CONSTANT(TEXTURE_USAGE_CAN_COPY_TO_BIT); - BIND_ENUM_CONSTANT(TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT); + BIND_ENUM_CONSTANT(TEXTURE_USAGE_INPUT_ATTACHMENT_BIT); BIND_ENUM_CONSTANT(TEXTURE_SWIZZLE_IDENTITY); BIND_ENUM_CONSTANT(TEXTURE_SWIZZLE_ZERO); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index eaf1ace798..bb028cb84c 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -422,7 +422,7 @@ public: TEXTURE_USAGE_CAN_UPDATE_BIT = (1 << 6), TEXTURE_USAGE_CAN_COPY_FROM_BIT = (1 << 7), TEXTURE_USAGE_CAN_COPY_TO_BIT = (1 << 8), - TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT = (1 << 9), + TEXTURE_USAGE_INPUT_ATTACHMENT_BIT = (1 << 9), }; enum TextureSwizzle { @@ -1065,6 +1065,7 @@ public: virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) = 0; virtual void draw_list_disable_scissor(DrawListID p_list) = 0; + virtual uint32_t draw_list_get_current_pass() = 0; virtual DrawListID draw_list_switch_to_next_pass() = 0; virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) = 0; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 376d23ccb3..4488069698 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -31,18 +31,22 @@ #include "shader_types.h" #include "core/math/math_defs.h" -const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(RS::ShaderMode p_mode) { +const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(RS::ShaderMode p_mode) const { return shader_modes[p_mode].functions; } -const Vector<StringName> &ShaderTypes::get_modes(RS::ShaderMode p_mode) { +const Vector<StringName> &ShaderTypes::get_modes(RS::ShaderMode p_mode) const { return shader_modes[p_mode].modes; } -const Set<String> &ShaderTypes::get_types() { +const Set<String> &ShaderTypes::get_types() const { return shader_types; } +const List<String> &ShaderTypes::get_types_list() const { + return shader_types_list; +} + ShaderTypes *ShaderTypes::singleton = nullptr; static ShaderLanguage::BuiltInInfo constt(ShaderLanguage::DataType p_type) { @@ -440,8 +444,12 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SKY].modes.push_back("use_quarter_res_pass"); shader_modes[RS::SHADER_SKY].modes.push_back("disable_fog"); - shader_types.insert("spatial"); - shader_types.insert("canvas_item"); - shader_types.insert("particles"); - shader_types.insert("sky"); + shader_types_list.push_back("spatial"); + shader_types_list.push_back("canvas_item"); + shader_types_list.push_back("particles"); + shader_types_list.push_back("sky"); + + for (int i = 0; i < shader_types_list.size(); i++) { + shader_types.insert(shader_types_list[i]); + } } diff --git a/servers/rendering/shader_types.h b/servers/rendering/shader_types.h index e59cef6b79..75a310a1b1 100644 --- a/servers/rendering/shader_types.h +++ b/servers/rendering/shader_types.h @@ -46,13 +46,15 @@ class ShaderTypes { static ShaderTypes *singleton; Set<String> shader_types; + List<String> shader_types_list; public: static ShaderTypes *get_singleton() { return singleton; } - const Map<StringName, ShaderLanguage::FunctionInfo> &get_functions(RS::ShaderMode p_mode); - const Vector<StringName> &get_modes(RS::ShaderMode p_mode); - const Set<String> &get_types(); + const Map<StringName, ShaderLanguage::FunctionInfo> &get_functions(RS::ShaderMode p_mode) const; + const Vector<StringName> &get_modes(RS::ShaderMode p_mode) const; + const Set<String> &get_types() const; + const List<String> &get_types_list() const; ShaderTypes(); }; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 1491368109..e32ea9cdea 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -314,8 +314,8 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control); ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(VALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent); diff --git a/servers/text_server.h b/servers/text_server.h index 4c2ada7fc9..d824504bf4 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -181,7 +181,7 @@ public: struct EmbeddedObject { int pos = 0; - VAlign inline_align = VALIGN_TOP; + InlineAlign inline_align = INLINE_ALIGN_CENTER; Rect2 rect; }; Map<Variant, EmbeddedObject> objects; @@ -332,8 +332,8 @@ public: virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0; virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0; - virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1) = 0; - virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER) = 0; + virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) = 0; + virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) = 0; virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range. virtual RID shaped_text_get_parent(RID p_shaped) const = 0; |