diff options
400 files changed, 13359 insertions, 6521 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 697e09955c..f59c0e645d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -337,7 +337,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). #### Rendering -- Some Environment settings such as depth of field have been moved to a CameraEffects resource which is assigned to individual Camera nodes. +- Some Environment settings such as depth of field have been moved to a CameraAttributes resource which is assigned to individual Camera nodes. - [The ACES Fitted tonemapping algorithm is now used in place of the old ACES algorithm.](https://github.com/godotengine/godot/pull/52476) - The old non-fitted ACES tonemapping algorithm was removed. - Quality settings have been moved from individual nodes and resources to the Project Settings for better centralization. diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 74c06123e1..6275502378 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -41,7 +41,10 @@ #include "core/os/keyboard.h" #include "core/variant/variant_parser.h" #include "core/version.h" + +#ifdef TOOLS_ENABLED #include "modules/modules_enabled.gen.h" // For mono. +#endif // TOOLS_ENABLED const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot"; @@ -75,6 +78,7 @@ String ProjectSettings::get_imported_files_path() const { return get_project_data_path().path_join("imported"); } +#ifdef TOOLS_ENABLED // Returns the features that a project must have when opened with this build of Godot. // This is used by the project manager to provide the initial_settings for config/features. const PackedStringArray ProjectSettings::get_required_features() { @@ -137,6 +141,7 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe features.sort(); return features; } +#endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") || @@ -897,6 +902,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); +#ifdef TOOLS_ENABLED PackedStringArray project_features = get_setting("application/config/features"); // If there is no feature list currently present, force one to generate. if (project_features.is_empty()) { @@ -924,6 +930,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } project_features = _trim_to_supported_features(project_features); set_setting("application/config/features", project_features); +#endif // TOOLS_ENABLED RBSet<_VCSort> vclist; diff --git a/core/config/project_settings.h b/core/config/project_settings.h index c845120a26..a9af63ee39 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -48,8 +48,10 @@ public: //properties that are not for built in values begin from this value, so builtin ones are displayed first NO_BUILTIN_ORDER_BASE = 1 << 16 }; +#ifdef TOOLS_ENABLED const static PackedStringArray get_required_features(); const static PackedStringArray get_unsupported_features(const PackedStringArray &p_project_features); +#endif // TOOLS_ENABLED struct AutoloadInfo { StringName name; @@ -116,8 +118,10 @@ protected: Error _save_custom_bnd(const String &p_file); +#ifdef TOOLS_ENABLED const static PackedStringArray _get_supported_features(); const static PackedStringArray _trim_to_supported_features(const PackedStringArray &p_project_features); +#endif // TOOLS_ENABLED void _convert_to_last_version(int p_from_version); diff --git a/core/input/input.cpp b/core/input/input.cpp index 4e538a85ae..1390a0a7fa 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -169,11 +169,10 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if (p_idx == 0 && - (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || - pf == "is_action_just_pressed" || pf == "is_action_just_released" || - pf == "get_action_strength" || pf == "get_action_raw_strength" || - pf == "get_axis" || pf == "get_vector")) { + + if ((p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength" || pf == "get_action_raw_strength")) || + (p_idx < 2 && pf == "get_axis") || + (p_idx < 4 && pf == "get_vector")) { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index ae421654ca..f84a95347a 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -32,6 +32,7 @@ #include "core/io/file_access_encrypted.h" #include "core/os/keyboard.h" +#include "core/string/string_builder.h" #include "core/variant/variant_parser.h" PackedStringArray ConfigFile::_get_sections() const { @@ -130,6 +131,28 @@ void ConfigFile::erase_section_key(const String &p_section, const String &p_key) } } +String ConfigFile::encode_to_text() const { + StringBuilder sb; + bool first = true; + for (const KeyValue<String, HashMap<String, Variant>> &E : values) { + if (first) { + first = false; + } else { + sb.append("\n"); + } + if (!E.key.is_empty()) { + sb.append("[" + E.key + "]\n\n"); + } + + for (const KeyValue<String, Variant> &F : E.value) { + String vstr; + VariantWriter::write_to_string(F.value, vstr); + sb.append(F.key.property_name_encode() + "=" + vstr + "\n"); + } + } + return sb.as_string(); +} + Error ConfigFile::save(const String &p_path) { Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); @@ -295,6 +318,7 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) void ConfigFile::clear() { values.clear(); } + void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value); ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant())); @@ -312,6 +336,8 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); + ClassDB::bind_method(D_METHOD("encode_to_text"), &ConfigFile::encode_to_text); + BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN); ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); diff --git a/core/io/config_file.h b/core/io/config_file.h index 3b07ec52f5..f6209492b7 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -68,6 +68,8 @@ public: Error load(const String &p_path); Error parse(const String &p_data); + String encode_to_text() const; // used by exporter + void clear(); Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key); diff --git a/core/io/image.cpp b/core/io/image.cpp index 2d87523ca4..dee751eec5 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -416,8 +416,8 @@ int Image::get_height() const { return height; } -Vector2i Image::get_size() const { - return Vector2i(width, height); +Size2i Image::get_size() const { + return Size2i(width, height); } bool Image::has_mipmaps() const { diff --git a/core/io/image.h b/core/io/image.h index 9d415423be..fd264a7a38 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -209,7 +209,7 @@ private: public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height - Vector2i get_size() const; + Size2i get_size() const; bool has_mipmaps() const; int get_mipmap_count() const; diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 2f69c10218..b24c49f58d 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -510,7 +510,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += sizeof(double) * 16; } } else { - ERR_FAIL_COND_V((size_t)len < sizeof(float) * 62, ERR_INVALID_DATA); + ERR_FAIL_COND_V((size_t)len < sizeof(float) * 16, ERR_INVALID_DATA); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { val.matrix[i][j] = decode_float(&buf[(i * 4 + j) * sizeof(float)]); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 0af236f766..1df779d034 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -152,42 +152,24 @@ void PacketPeer::_bind_methods() { /***************/ -int PacketPeerExtension::get_available_packet_count() const { - int count; - if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { - return count; - } - WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!"); - return -1; -} - Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!"); return FAILED; } Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!"); return FAILED; } -int PacketPeerExtension::get_max_packet_size() const { - int size; - if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { - return size; - } - WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!"); - return 0; -} - void PacketPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index ec9d33aa5a..07045e62a6 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,6 +35,7 @@ #include "core/object/class_db.h" #include "core/templates/ring_buffer.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -84,16 +85,14 @@ protected: static void _bind_methods(); public: - virtual int get_available_packet_count() const override; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); - /* GDExtension */ - GDVIRTUAL0RC(int, _get_available_packet_count); - GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); - GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); - GDVIRTUAL0RC(int, _get_max_packet_size); + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); }; class PacketPeerStream : public PacketPeer { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index c65968ef03..053ff64069 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -410,48 +410,39 @@ void StreamPeer::_bind_methods() { //////////////////////////////// -int StreamPeerExtension::get_available_bytes() const { - int count; - if (GDVIRTUAL_CALL(_get_available_bytes, count)) { - return count; - } - WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!"); - return -1; -} - Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) { - int err; + Error err; int received = 0; if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) { - int err; + Error err; int sent = 0; if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!"); return FAILED; } Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!"); return FAILED; diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 4609e52aa2..108a8ce9d9 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -33,6 +33,7 @@ #include "core/object/ref_counted.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -104,16 +105,18 @@ protected: public: virtual Error put_data(const uint8_t *p_data, int p_bytes) override; + GDVIRTUAL3R(Error, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + GDVIRTUAL3R(Error, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; + GDVIRTUAL3R(Error, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; - virtual int get_available_bytes() const override; + GDVIRTUAL3R(Error, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); - GDVIRTUAL0RC(int, _get_available_bytes); + EXBIND0RC(int, get_available_bytes); }; class StreamPeerBuffer : public StreamPeer { diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 8db33bde6f..23d7e379ee 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -57,7 +57,7 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to) static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_manhattan, heuristic_euclidian, heuristic_octile, heuristic_chebyshev }; -void AStarGrid2D::set_size(const Vector2i &p_size) { +void AStarGrid2D::set_size(const Size2i &p_size) { ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0); if (p_size != size) { size = p_size; @@ -65,7 +65,7 @@ void AStarGrid2D::set_size(const Vector2i &p_size) { } } -Vector2i AStarGrid2D::get_size() const { +Size2i AStarGrid2D::get_size() const { return size; } @@ -80,14 +80,14 @@ Vector2 AStarGrid2D::get_offset() const { return offset; } -void AStarGrid2D::set_cell_size(const Vector2 &p_cell_size) { +void AStarGrid2D::set_cell_size(const Size2 &p_cell_size) { if (!cell_size.is_equal_approx(p_cell_size)) { cell_size = p_cell_size; dirty = true; } } -Vector2 AStarGrid2D::get_cell_size() const { +Size2 AStarGrid2D::get_cell_size() const { return cell_size; } diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 66312d10ac..bf6363aa01 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -58,9 +58,9 @@ public: }; private: - Vector2i size; + Size2i size; Vector2 offset; - Vector2 cell_size = Vector2(1, 1); + Size2 cell_size = Size2(1, 1); bool dirty = false; bool jumping_enabled = false; @@ -136,14 +136,14 @@ protected: GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i) public: - void set_size(const Vector2i &p_size); - Vector2i get_size() const; + void set_size(const Size2i &p_size); + Size2i get_size() const; void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; - void set_cell_size(const Vector2 &p_cell_size); - Vector2 get_cell_size() const; + void set_cell_size(const Size2 &p_cell_size); + Size2 get_cell_size() const; void update(); diff --git a/core/math/basis.cpp b/core/math/basis.cpp index bc50d0e64c..0eb6320ac6 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -1033,13 +1033,13 @@ void Basis::rotate_sh(real_t *p_values) { Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); - ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero."); #endif Vector3 v_z = -p_target.normalized(); Vector3 v_x = p_up.cross(v_z); #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); + ERR_FAIL_COND_V_MSG(v_x.is_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other."); #endif v_x.normalize(); Vector3 v_y = v_z.cross(v_x); diff --git a/core/math/basis.h b/core/math/basis.h index 2853947ba7..cc2924f5ff 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -238,10 +238,8 @@ struct _NO_DISCARD_ Basis { Basis(const Vector3 &p_axis, real_t p_angle, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_angle, p_scale); } static Basis from_scale(const Vector3 &p_scale); - _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { - rows[0] = row0; - rows[1] = row1; - rows[2] = row2; + _FORCE_INLINE_ Basis(const Vector3 &p_x_axis, const Vector3 &p_y_axis, const Vector3 &p_z_axis) { + set_columns(p_x_axis, p_y_axis, p_z_axis); } _FORCE_INLINE_ Basis() {} diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index ec96753c79..9238293b48 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -35,6 +35,111 @@ #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" +void Geometry3D::get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt) { + // Based on David Eberly's Computation of Distance Between Line Segments algorithm. + + Vector3 p = p_p1 - p_p0; + Vector3 q = p_q1 - p_q0; + Vector3 r = p_p0 - p_q0; + + real_t a = p.dot(p); + real_t b = p.dot(q); + real_t c = q.dot(q); + real_t d = p.dot(r); + real_t e = q.dot(r); + + real_t s = 0.0f; + real_t t = 0.0f; + + real_t det = a * c - b * b; + if (det > CMP_EPSILON) { + // Non-parallel segments + real_t bte = b * e; + real_t ctd = c * d; + + if (bte <= ctd) { + // s <= 0.0f + if (e <= 0.0f) { + // t <= 0.0f + s = (-d >= a ? 1 : (-d > 0.0f ? -d / a : 0.0f)); + t = 0.0f; + } else if (e < c) { + // 0.0f < t < 1 + s = 0.0f; + t = e / c; + } else { + // t >= 1 + s = (b - d >= a ? 1 : (b - d > 0.0f ? (b - d) / a : 0.0f)); + t = 1; + } + } else { + // s > 0.0f + s = bte - ctd; + if (s >= det) { + // s >= 1 + if (b + e <= 0.0f) { + // t <= 0.0f + s = (-d <= 0.0f ? 0.0f : (-d < a ? -d / a : 1)); + t = 0.0f; + } else if (b + e < c) { + // 0.0f < t < 1 + s = 1; + t = (b + e) / c; + } else { + // t >= 1 + s = (b - d <= 0.0f ? 0.0f : (b - d < a ? (b - d) / a : 1)); + t = 1; + } + } else { + // 0.0f < s < 1 + real_t ate = a * e; + real_t btd = b * d; + + if (ate <= btd) { + // t <= 0.0f + s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a)); + t = 0.0f; + } else { + // t > 0.0f + t = ate - btd; + if (t >= det) { + // t >= 1 + s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a)); + t = 1; + } else { + // 0.0f < t < 1 + s /= det; + t /= det; + } + } + } + } + } else { + // Parallel segments + if (e <= 0.0f) { + s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a)); + t = 0.0f; + } else if (e >= c) { + s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a)); + t = 1; + } else { + s = 0.0f; + t = e / c; + } + } + + r_ps = (1 - s) * p_p0 + s * p_p1; + r_qt = (1 - t) * p_q0 + t * p_q1; +} + +real_t Geometry3D::get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1) { + Vector3 ps; + Vector3 qt; + get_closest_points_between_segments(p_p0, p_p1, p_q0, p_q1, ps, qt); + Vector3 st = qt - ps; + return st.length(); +} + void Geometry3D::MeshData::optimize_vertices() { HashMap<int, int> vtx_remap; diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 59c56906f4..e5ace9db72 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -37,96 +37,8 @@ class Geometry3D { public: - static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) { -// Do the function 'd' as defined by pb. I think it's a dot product of some sort. -#define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z)) - - // Calculate the parametric position on the 2 curves, mua and mub. - real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1)); - real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1); - - // Clip the value between [0..1] constraining the solution to lie on the original curves. - if (mua < 0) { - mua = 0; - } - if (mub < 0) { - mub = 0; - } - if (mua > 1) { - mua = 1; - } - if (mub > 1) { - mub = 1; - } - c1 = p1.lerp(p2, mua); - c2 = q1.lerp(q2, mub); - } - - static real_t get_closest_distance_between_segments(const Vector3 &p_from_a, const Vector3 &p_to_a, const Vector3 &p_from_b, const Vector3 &p_to_b) { - Vector3 u = p_to_a - p_from_a; - Vector3 v = p_to_b - p_from_b; - Vector3 w = p_from_a - p_to_a; - real_t a = u.dot(u); // Always >= 0 - real_t b = u.dot(v); - real_t c = v.dot(v); // Always >= 0 - real_t d = u.dot(w); - real_t e = v.dot(w); - real_t D = a * c - b * b; // Always >= 0 - real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 - real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 - - // Compute the line parameters of the two closest points. - if (D < (real_t)CMP_EPSILON) { // The lines are almost parallel. - sN = 0.0f; // Force using point P0 on segment S1 - sD = 1.0f; // to prevent possible division by 0.0 later. - tN = e; - tD = c; - } else { // Get the closest points on the infinite lines - sN = (b * e - c * d); - tN = (a * e - b * d); - if (sN < 0.0f) { // sc < 0 => the s=0 edge is visible. - sN = 0.0f; - tN = e; - tD = c; - } else if (sN > sD) { // sc > 1 => the s=1 edge is visible. - sN = sD; - tN = e + b; - tD = c; - } - } - - if (tN < 0.0f) { // tc < 0 => the t=0 edge is visible. - tN = 0.0f; - // Recompute sc for this edge. - if (-d < 0.0f) { - sN = 0.0f; - } else if (-d > a) { - sN = sD; - } else { - sN = -d; - sD = a; - } - } else if (tN > tD) { // tc > 1 => the t=1 edge is visible. - tN = tD; - // Recompute sc for this edge. - if ((-d + b) < 0.0f) { - sN = 0; - } else if ((-d + b) > a) { - sN = sD; - } else { - sN = (-d + b); - sD = a; - } - } - // Finally do the division to get sc and tc. - sc = (Math::is_zero_approx(sN) ? 0.0f : sN / sD); - tc = (Math::is_zero_approx(tN) ? 0.0f : tN / tD); - - // Get the difference of the two closest points. - Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) - - return dP.length(); // Return the closest distance. - } + static void get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt); + static real_t get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1); static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { Vector3 e1 = p_v1 - p_v0; diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index d9b5d55454..56dbba393a 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -182,6 +182,10 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const { return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); } +bool Vector2::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y); +} + Vector2::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; } diff --git a/core/math/vector2.h b/core/math/vector2.h index caa6b226e7..9441f84087 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -124,6 +124,7 @@ struct _NO_DISCARD_ Vector2 { Vector2 reflect(const Vector2 &p_normal) const; bool is_equal_approx(const Vector2 &p_v) const; + bool is_zero_approx() const; Vector2 operator+(const Vector2 &p_v) const; void operator+=(const Vector2 &p_v); diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index fdbbb8cb5c..4db45fe798 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -134,17 +134,21 @@ Vector3 Vector3::octahedron_tangent_decode(const Vector2 &p_oct, float *sign) { } Basis Vector3::outer(const Vector3 &p_with) const { - Vector3 row0(x * p_with.x, x * p_with.y, x * p_with.z); - Vector3 row1(y * p_with.x, y * p_with.y, y * p_with.z); - Vector3 row2(z * p_with.x, z * p_with.y, z * p_with.z); - - return Basis(row0, row1, row2); + Basis basis; + basis.rows[0] = Vector3(x * p_with.x, x * p_with.y, x * p_with.z); + basis.rows[1] = Vector3(y * p_with.x, y * p_with.y, y * p_with.z); + basis.rows[2] = Vector3(z * p_with.x, z * p_with.y, z * p_with.z); + return basis; } bool Vector3::is_equal_approx(const Vector3 &p_v) const { return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); } +bool Vector3::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z); +} + Vector3::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; } diff --git a/core/math/vector3.h b/core/math/vector3.h index 7cae6e2481..3944afa92e 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -142,6 +142,7 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; bool is_equal_approx(const Vector3 &p_v) const; + bool is_zero_approx() const; /* Operators */ diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp index 273a111891..fb651fafce 100644 --- a/core/math/vector4.cpp +++ b/core/math/vector4.cpp @@ -71,6 +71,10 @@ bool Vector4::is_equal_approx(const Vector4 &p_vec4) const { return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w); } +bool Vector4::is_zero_approx() const { + return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w); +} + real_t Vector4::length() const { return Math::sqrt(length_squared()); } diff --git a/core/math/vector4.h b/core/math/vector4.h index 17d0de18e1..f964264108 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -73,6 +73,7 @@ struct _NO_DISCARD_ Vector4 { _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Vector4 &p_vec4) const; + bool is_zero_approx() const; real_t length() const; void normalize(); Vector4 normalized() const; diff --git a/core/string/ustring.h b/core/string/ustring.h index 31de7cc464..b8ae3c2392 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -376,8 +376,6 @@ public: String path_join(const String &p_file) const; char32_t unicode_at(int p_idx) const; - void erase(int p_pos, int p_chars); - CharString ascii(bool p_allow_extended = false) const; CharString utf8() const; Error parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false); diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index d9f4359ee5..c1cb782a57 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -195,6 +195,15 @@ bool Dictionary::has_all(const Array &p_keys) const { return true; } +Variant Dictionary::find_key(const Variant &p_value) const { + for (const KeyValue<Variant, Variant> &E : _p->variant_map) { + if (E.value == p_value) { + return E.key; + } + } + return Variant(); +} + bool Dictionary::erase(const Variant &p_key) { ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state."); if (p_key.get_type() == Variant::STRING_NAME) { diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 2632893e8d..d9c9db56cf 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -66,6 +66,7 @@ public: bool has(const Variant &p_key) const; bool has_all(const Array &p_keys) const; + Variant find_key(const Variant &p_value) const; bool erase(const Variant &p_key); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index a4bce979fe..8f15a62f79 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1605,6 +1605,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, normalized, sarray(), varray()); bind_method(Vector2, is_normalized, sarray(), varray()); bind_method(Vector2, is_equal_approx, sarray("to"), varray()); + bind_method(Vector2, is_zero_approx, sarray(), varray()); bind_method(Vector2, posmod, sarray("mod"), varray()); bind_method(Vector2, posmodv, sarray("modv"), varray()); bind_method(Vector2, project, sarray("b"), varray()); @@ -1693,6 +1694,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, normalized, sarray(), varray()); bind_method(Vector3, is_normalized, sarray(), varray()); bind_method(Vector3, is_equal_approx, sarray("to"), varray()); + bind_method(Vector3, is_zero_approx, sarray(), varray()); bind_method(Vector3, inverse, sarray(), varray()); bind_method(Vector3, clamp, sarray("min", "max"), varray()); bind_method(Vector3, snapped, sarray("step"), varray()); @@ -1756,6 +1758,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector4, dot, sarray("with"), varray()); bind_method(Vector4, inverse, sarray(), varray()); bind_method(Vector4, is_equal_approx, sarray("with"), varray()); + bind_method(Vector4, is_zero_approx, sarray(), varray()); /* Vector4i */ @@ -2014,6 +2017,7 @@ static void _register_variant_builtin_methods() { bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false)); bind_method(Dictionary, has, sarray("key"), varray()); bind_method(Dictionary, has_all, sarray("keys"), varray()); + bind_method(Dictionary, find_key, sarray("value"), varray()); bind_method(Dictionary, erase, sarray("key"), varray()); bind_method(Dictionary, hash, sarray(), varray()); bind_method(Dictionary, keys, sarray(), varray()); diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index c83ea8c60a..ee49523c90 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -99,6 +99,12 @@ </signal> </signals> <theme_items> + <theme_item name="button_margin" data_type="constant" type="int" default="32"> + Offset that is applied to the content of the window on the bottom, effectively moving the button row. + </theme_item> + <theme_item name="margin" data_type="constant" type="int" default="8"> + Offset that is applied to the content of the window on top, left, and right. + </theme_item> <theme_item name="panel" data_type="style" type="StyleBox"> Panel that fills up the background of the window. </theme_item> diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml index 68354f092c..0bc5484e3a 100644 --- a/doc/classes/AnimatedSprite3D.xml +++ b/doc/classes/AnimatedSprite3D.xml @@ -10,12 +10,6 @@ <link title="2D Sprite animation (also applies to 3D)">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> </tutorials> <methods> - <method name="is_playing" qualifiers="const"> - <return type="bool" /> - <description> - Returns [code]true[/code] if an animation is currently being played. - </description> - </method> <method name="play"> <return type="void" /> <param index="0" name="anim" type="StringName" default="&""" /> @@ -40,7 +34,7 @@ <member name="frames" type="SpriteFrames" setter="set_sprite_frames" getter="get_sprite_frames"> The [SpriteFrames] resource containing the animation(s). </member> - <member name="playing" type="bool" setter="_set_playing" getter="_is_playing" default="false"> + <member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false"> If [code]true[/code], the [member animation] is currently playing. </member> </members> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 2262c4fe47..ee28675d89 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -194,10 +194,13 @@ The emitted light's color. See [member emission_enabled]. </member> <member name="emission_enabled" type="bool" setter="set_feature" getter="get_feature" default="false"> - If [code]true[/code], the body emits light. Emitting light makes the object appear brighter. The object can also cast light on other objects if a [VoxelGI] is used and this object is used in baked lighting. + If [code]true[/code], the body emits light. Emitting light makes the object appear brighter. The object can also cast light on other objects if a [VoxelGI], SDFGI, or [LightmapGI] is used and this object is used in baked lighting. </member> - <member name="emission_energy" type="float" setter="set_emission_energy" getter="get_emission_energy" default="1.0"> - The emitted light's strength. See [member emission_enabled]. + <member name="emission_energy_multiplier" type="float" setter="set_emission_energy_multiplier" getter="get_emission_energy_multiplier" default="1.0"> + Multiplier for emitted light. See [member emission_enabled]. + </member> + <member name="emission_intensity" type="float" setter="set_emission_intensity" getter="get_emission_intensity"> + Luminance of emitted light, measured in nits (candela per square meter). Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. The default is roughly equivalent to an indoor lightbulb. </member> <member name="emission_on_uv2" type="bool" setter="set_flag" getter="get_flag" default="false"> Use [code]UV2[/code] to read from the [member emission_texture]. diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml index 9323642274..b3fa55f154 100644 --- a/doc/classes/BitMap.xml +++ b/doc/classes/BitMap.xml @@ -17,7 +17,7 @@ </method> <method name="create"> <return type="void" /> - <param index="0" name="size" type="Vector2" /> + <param index="0" name="size" type="Vector2i" /> <description> Creates a bitmap with the specified size, filled with [code]false[/code]. </description> @@ -32,13 +32,21 @@ </method> <method name="get_bit" qualifiers="const"> <return type="bool" /> - <param index="0" name="position" type="Vector2" /> + <param index="0" name="x" type="int" /> + <param index="1" name="y" type="int" /> + <description> + Returns bitmap's value at the specified position. + </description> + </method> + <method name="get_bitv" qualifiers="const"> + <return type="bool" /> + <param index="0" name="position" type="Vector2i" /> <description> Returns bitmap's value at the specified position. </description> </method> <method name="get_size" qualifiers="const"> - <return type="Vector2" /> + <return type="Vector2i" /> <description> Returns bitmap's dimensions. </description> @@ -52,14 +60,14 @@ <method name="grow_mask"> <return type="void" /> <param index="0" name="pixels" type="int" /> - <param index="1" name="rect" type="Rect2" /> + <param index="1" name="rect" type="Rect2i" /> <description> Applies morphological dilation or erosion to the bitmap. If [param pixels] is positive, dilation is applied to the bitmap. If [param pixels] is negative, erosion is applied to the bitmap. [param rect] defines the area where the morphological operation is applied. Pixels located outside the [param rect] are unaffected by [method grow_mask]. </description> </method> <method name="opaque_to_polygons" qualifiers="const"> <return type="PackedVector2Array[]" /> - <param index="0" name="rect" type="Rect2" /> + <param index="0" name="rect" type="Rect2i" /> <param index="1" name="epsilon" type="float" default="2.0" /> <description> Creates an [Array] of polygons covering a rectangular portion of the bitmap. It uses a marching squares algorithm, followed by Ramer-Douglas-Peucker (RDP) reduction of the number of vertices. Each polygon is described as a [PackedVector2Array] of its vertices. @@ -72,26 +80,35 @@ </method> <method name="resize"> <return type="void" /> - <param index="0" name="new_size" type="Vector2" /> + <param index="0" name="new_size" type="Vector2i" /> <description> Resizes the image to [param new_size]. </description> </method> <method name="set_bit"> <return type="void" /> - <param index="0" name="position" type="Vector2" /> - <param index="1" name="bit" type="bool" /> + <param index="0" name="x" type="int" /> + <param index="1" name="y" type="int" /> + <param index="2" name="bit" type="bool" /> <description> Sets the bitmap's element at the specified position, to the specified value. </description> </method> <method name="set_bit_rect"> <return type="void" /> - <param index="0" name="rect" type="Rect2" /> + <param index="0" name="rect" type="Rect2i" /> <param index="1" name="bit" type="bool" /> <description> Sets a rectangular portion of the bitmap to the specified value. </description> </method> + <method name="set_bitv"> + <return type="void" /> + <param index="0" name="position" type="Vector2i" /> + <param index="1" name="bit" type="bool" /> + <description> + Sets the bitmap's element at the specified position, to the specified value. + </description> + </method> </methods> </class> diff --git a/doc/classes/BoxContainer.xml b/doc/classes/BoxContainer.xml index 65ceab3e30..a06b0e9c4b 100644 --- a/doc/classes/BoxContainer.xml +++ b/doc/classes/BoxContainer.xml @@ -22,13 +22,25 @@ <member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="BoxContainer.AlignmentMode" default="0"> The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]). </member> + <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false"> + If [code]true[/code], the [BoxContainer] will arrange its children vertically, rather than horizontally. + Can't be changed when using [HBoxContainer] and [VBoxContainer]. + </member> </members> <constants> <constant name="ALIGNMENT_BEGIN" value="0" enum="AlignmentMode"> + The child controls will be arranged at the beginning of the container, i.e. top if orientation is vertical, left if orientation is horizontal (right for RTL layout). </constant> <constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode"> + The child controls will be centered in the container. </constant> <constant name="ALIGNMENT_END" value="2" enum="AlignmentMode"> + The child controls will be arranged at the end of the container, i.e. bottom if orientation is vertical, right if orientation is horizontal (left for RTL layout). </constant> </constants> + <theme_items> + <theme_item name="separation" data_type="constant" type="int" default="4"> + The space between the [BoxContainer]'s elements, in pixels. + </theme_item> + </theme_items> </class> diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 65fdecc3c6..322c4f0bf4 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -150,6 +150,9 @@ </method> </methods> <members> + <member name="attributes" type="CameraAttributes" setter="set_attributes" getter="get_attributes"> + The [CameraAttributes] to use for this camera. + </member> <member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="1048575"> The culling mask that describes which 3D render layers are rendered by this camera. </member> @@ -160,9 +163,6 @@ <member name="doppler_tracking" type="int" setter="set_doppler_tracking" getter="get_doppler_tracking" enum="Camera3D.DopplerTracking" default="0"> If not [constant DOPPLER_TRACKING_DISABLED], this camera will simulate the [url=https://en.wikipedia.org/wiki/Doppler_effect]Doppler effect[/url] for objects changed in particular [code]_process[/code] methods. See [enum DopplerTracking] for possible values. </member> - <member name="effects" type="CameraEffects" setter="set_effects" getter="get_effects"> - The [CameraEffects] to use for this camera. - </member> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> The [Environment] to use for this camera. </member> diff --git a/doc/classes/CameraAttributes.xml b/doc/classes/CameraAttributes.xml new file mode 100644 index 0000000000..a741728c14 --- /dev/null +++ b/doc/classes/CameraAttributes.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributes" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Parent class for camera settings. + </brief_description> + <description> + Controls camera-specific attributes such as depth of field and exposure override. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment]. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + See also [Environment] for general 3D environment settings. + This is a pure virtual class that is inherited by [CameraAttributesPhysical] and [CameraAttributesPractical]. + </description> + <tutorials> + </tutorials> + <members> + <member name="auto_exposure_enabled" type="bool" setter="set_auto_exposure_enabled" getter="is_auto_exposure_enabled"> + If [code]true[/code], enables the tonemapping auto exposure mode of the scene renderer. If [code]true[/code], the renderer will automatically determine the exposure setting to adapt to the scene's illumination and the observed light. + </member> + <member name="auto_exposure_scale" type="float" setter="set_auto_exposure_scale" getter="get_auto_exposure_scale"> + The scale of the auto exposure effect. Affects the intensity of auto exposure. + </member> + <member name="auto_exposure_speed" type="float" setter="set_auto_exposure_speed" getter="get_auto_exposure_speed"> + The speed of the auto exposure effect. Affects the time needed for the camera to perform auto exposure. + </member> + <member name="exposure_multiplier" type="float" setter="set_exposure_multiplier" getter="get_exposure_multiplier"> + Multiplier for the exposure amount. A higher value results in a brighter image. + </member> + <member name="exposure_sensitivity" type="float" setter="set_exposure_sensitivity" getter="get_exposure_sensitivity"> + Sensitivity of camera sensors, measured in ISO. A higher sensitivity results in a brighter image. Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. When [member auto_exposure_enabled] this can be used as a method of exposure compensation, doubling the value will increase the exposure value (measured in EV100) by 1 stop. + </member> + </members> +</class> diff --git a/doc/classes/CameraAttributesPhysical.xml b/doc/classes/CameraAttributesPhysical.xml new file mode 100644 index 0000000000..a61e735932 --- /dev/null +++ b/doc/classes/CameraAttributesPhysical.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributesPhysical" inherits="CameraAttributes" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Physically-based camera settings. + </brief_description> + <description> + [CameraAttributesPhysical] is used to set rendering settings based on a physically-based camera's settings. It is responsible for exposure, auto-exposure, and depth of field. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment] and will override the [Camera3D]s [member Camera3D.far], [member Camera3D.near], [member Camera3D.fov], and [member Camera3D.keep_aspect] properties. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + The default settings are intended for use in an outdoor environment, tips for settings for use in an indoor environment can be found in each setting's documentation. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_fov" qualifiers="const"> + <return type="float" /> + <description> + Returns the vertical field of view that corresponds to the [member frustum_focal_length]. This value is calculated internally whenever [member frustum_focal_length] is changed. + </description> + </method> + </methods> + <members> + <member name="auto_exposure_max_exposure_value" type="float" setter="set_auto_exposure_max_exposure_value" getter="get_auto_exposure_max_exposure_value" default="10.0"> + The maximum luminance (in EV100) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing below a certain brightness, resulting in a cut off point where the scene will remain bright. + </member> + <member name="auto_exposure_min_exposure_value" type="float" setter="set_auto_exposure_min_exposure_value" getter="get_auto_exposure_min_exposure_value" default="-8.0"> + The minimum luminance luminance (in EV100) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing above a certain brightness, resulting in a cut off point where the scene will remain dark. + </member> + <member name="exposure_aperture" type="float" setter="set_aperture" getter="get_aperture" default="16.0"> + Size of the aperture of the camera, measured in f-stops. An f-stop is a unitless ratio between the focal length of the camera and the diameter of the aperture. A high aperture setting will result in a smaller aperture which leads to a dimmer image and sharper focus. A low aperture results in a wide aperture which lets in more light resulting in a brighter, less-focused image. Default is appropriate for outdoors at daytime (i.e. for use with a default [DirectionalLight3D]), for indoor lighting, a value between 2 and 4 is more appropriate. + Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. + </member> + <member name="exposure_shutter_speed" type="float" setter="set_shutter_speed" getter="get_shutter_speed" default="100.0"> + Time for shutter to open and close, measured in seconds. A higher value will let in more light leading to a brighter image, while a lower amount will let in less light leading to a darker image. + Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. + </member> + <member name="frustum_far" type="float" setter="set_far" getter="get_far" default="4000.0"> + Override value for [member Camera3D.far]. Used internally when calculating depth of field. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.far] property. + </member> + <member name="frustum_focal_length" type="float" setter="set_focal_length" getter="get_focal_length" default="35.0"> + Distance between camera lens and camera aperture, measured in millimeters. Controls field of view and depth of field. A larger focal length will result in a smaller field of view and a narrower depth of field meaning fewer objects will be in focus. A smaller focal length will result in a wider field of view and a larger depth of field meaning more objects will be in focus. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.fov] property and the [member Camera3D.keep_aspect] property. + </member> + <member name="frustum_focus_distance" type="float" setter="set_focus_distance" getter="get_focus_distance" default="10.0"> + Distance from camera of object that will be in focus, measured in meters. Internally this will be clamped to be at least 1 millimeter larger than [member frustum_focal_length]. + </member> + <member name="frustum_near" type="float" setter="set_near" getter="get_near" default="0.05"> + Override value for [member Camera3D.near]. Used internally when calculating depth of field. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.near] property. + </member> + </members> +</class> diff --git a/doc/classes/CameraAttributesPractical.xml b/doc/classes/CameraAttributesPractical.xml new file mode 100644 index 0000000000..924b02fc79 --- /dev/null +++ b/doc/classes/CameraAttributesPractical.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributesPractical" inherits="CameraAttributes" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Camera settings in an easy to use format. + </brief_description> + <description> + Controls camera-specific attributes such as auto-exposure, depth of field, and exposure override. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment]. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + </description> + <tutorials> + </tutorials> + <members> + <member name="auto_exposure_max_sensitivity" type="float" setter="set_auto_exposure_max_sensitivity" getter="get_auto_exposure_max_sensitivity" default="800.0"> + The maximum sensitivity (in ISO) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing below a certain brightness, resulting in a cut off point where the scene will remain bright. + </member> + <member name="auto_exposure_min_sensitivity" type="float" setter="set_auto_exposure_min_sensitivity" getter="get_auto_exposure_min_sensitivity" default="0.0"> + The minimum sensitivity (in ISO) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing above a certain brightness, resulting in a cut off point where the scene will remain dark. + </member> + <member name="dof_blur_amount" type="float" setter="set_dof_blur_amount" getter="get_dof_blur_amount" default="0.1"> + Sets the maximum amount of blur. When using physically-based blur amounts, will instead act as a multiplier. High values lead to an increased amount of bluriness, but can be much more expensive to calculate. It is best to keep this as low as possible for a given art style. + </member> + <member name="dof_blur_far_distance" type="float" setter="set_dof_blur_far_distance" getter="get_dof_blur_far_distance" default="10.0"> + Objects further from the [Camera3D] by this amount will be blurred by the depth of field effect. Measured in meters. + </member> + <member name="dof_blur_far_enabled" type="bool" setter="set_dof_blur_far_enabled" getter="is_dof_blur_far_enabled" default="false"> + Enables depth of field blur for objects further than [member dof_blur_far_distance]. Strength of blur is controlled by [member dof_blur_amount] and modulated by [member dof_blur_far_transition]. + </member> + <member name="dof_blur_far_transition" type="float" setter="set_dof_blur_far_transition" getter="get_dof_blur_far_transition" default="5.0"> + When positive, distance over which (starting from [member dof_blur_far_distance]) blur effect will scale from 0 to [member dof_blur_amount]. When negative, uses physically-based scaling so depth of field effect will scale from 0 at [member dof_blur_far_distance] and will increase in a physically accurate way as objects get further from the [Camera3D]. + </member> + <member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0"> + Objects closer from the [Camera3D] by this amount will be blurred by the depth of field effect. Measured in meters. + </member> + <member name="dof_blur_near_enabled" type="bool" setter="set_dof_blur_near_enabled" getter="is_dof_blur_near_enabled" default="false"> + Enables depth of field blur for objects closer than [member dof_blur_near_distance]. Strength of blur is controlled by [member dof_blur_amount] and modulated by [member dof_blur_near_transition]. + </member> + <member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0"> + When positive, distance over which blur effect will scale from 0 to [member dof_blur_amount], ending at [member dof_blur_near_distance]. When negative, uses physically-based scaling so depth of field effect will scale from 0 at [member dof_blur_near_distance] and will increase in a physically accurate way as objects get closer to the [Camera3D]. + </member> + </members> +</class> diff --git a/doc/classes/CameraEffects.xml b/doc/classes/CameraEffects.xml deleted file mode 100644 index c108b30f23..0000000000 --- a/doc/classes/CameraEffects.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="CameraEffects" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> - <brief_description> - Contains camera-specific effects such as depth of field and exposure override. - </brief_description> - <description> - Contains camera-specific effects such as depth of field and exposure override. - See also [Environment] for general 3D environment settings. - </description> - <tutorials> - </tutorials> - <members> - <member name="dof_blur_amount" type="float" setter="set_dof_blur_amount" getter="get_dof_blur_amount" default="0.1"> - The amount of blur for both near and far depth-of-field effects. The amount of blur increases the radius of the blur effect, making the affected area blurrier. However, If the amount is too high, you might start to see lines appearing, especially when using a low quality blur. - </member> - <member name="dof_blur_far_distance" type="float" setter="set_dof_blur_far_distance" getter="get_dof_blur_far_distance" default="10.0"> - The distance from the camera where the far blur effect affects the rendering. - </member> - <member name="dof_blur_far_enabled" type="bool" setter="set_dof_blur_far_enabled" getter="is_dof_blur_far_enabled" default="false"> - If [code]true[/code], enables the depth-of-field far blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no far away objects. - </member> - <member name="dof_blur_far_transition" type="float" setter="set_dof_blur_far_transition" getter="get_dof_blur_far_transition" default="5.0"> - The length of the transition between the no-blur area and far blur. - </member> - <member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0"> - Distance from the camera where the near blur effect affects the rendering. - </member> - <member name="dof_blur_near_enabled" type="bool" setter="set_dof_blur_near_enabled" getter="is_dof_blur_near_enabled" default="false"> - If [code]true[/code], enables the depth-of-field near blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no nearby objects. - </member> - <member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0"> - The length of the transition between the near blur and no-blur area. - </member> - <member name="override_exposure" type="float" setter="set_override_exposure" getter="get_override_exposure" default="1.0"> - The exposure override value to use. Higher values will result in a brighter scene. Only effective if [member override_exposure_enabled] is [code]true[/code]. - </member> - <member name="override_exposure_enabled" type="bool" setter="set_override_exposure_enabled" getter="is_override_exposure_enabled" default="false"> - If [code]true[/code], overrides the manual or automatic exposure defined in the [Environment] with the value in [member override_exposure]. - </member> - </members> -</class> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 705d2282c1..e992d6f9d4 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -55,6 +55,7 @@ <member name="presets_visible" type="bool" setter="set_presets_visible" getter="are_presets_visible" default="true"> If [code]true[/code], saved color presets are visible. </member> + <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" overrides="BoxContainer" default="true" /> </members> <signals> <signal name="color_changed"> diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index d3ad4e6e4b..7ba53f852b 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -98,6 +98,12 @@ Removes the entire contents of the config. </description> </method> + <method name="encode_to_text" qualifiers="const"> + <return type="String" /> + <description> + Obtain the text version of this config file (the same text that would be written to a file). + </description> + </method> <method name="erase_section"> <return type="void" /> <param index="0" name="section" type="String" /> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 40b5e88fff..9dbeeb360c 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -4,7 +4,7 @@ Dictionary type. </brief_description> <description> - Dictionary type. Associative container which contains values referenced by unique keys. Dictionaries are composed of pairs of keys (which must be unique) and values. Dictionaries will preserve the insertion order when adding elements, even though this may not be reflected when printing the dictionary. In other programming languages, this data structure is sometimes referred to as a hash map or associative array. + Dictionary type. Associative container which contains values referenced by unique keys. Dictionaries are composed of pairs of keys (which must be unique) and values. Dictionaries will preserve the insertion order when adding elements. In other programming languages, this data structure is sometimes referred to as a hash map or associative array. You can define a dictionary by placing a comma-separated list of [code]key: value[/code] pairs in curly braces [code]{}[/code]. Erasing elements while iterating over them [b]is not supported[/b] and will result in undefined behavior. [b]Note:[/b] Dictionaries are always passed by reference. To get a copy of a dictionary which can be modified independently of the original dictionary, use [method duplicate]. @@ -218,6 +218,14 @@ [b]Note:[/b] Don't erase elements while iterating over the dictionary. You can iterate over the [method keys] array instead. </description> </method> + <method name="find_key" qualifiers="const"> + <return type="Variant" /> + <param index="0" name="value" type="Variant" /> + <description> + Returns the first key whose associated value is equal to [param value], or [code]null[/code] if no such value is found. + [b]Node:[/b] [code]null[/code] is also a valid key. I you have it in your [Dictionary], the [method find_key] method can give misleading results. + </description> + </method> <method name="get" qualifiers="const"> <return type="Variant" /> <param index="0" name="key" type="Variant" /> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 35a2861c54..1d225aa06e 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -148,9 +148,10 @@ <param index="0" name="menu_root" type="String" /> <param index="1" name="label" type="String" /> <param index="2" name="callback" type="Callable" /> - <param index="3" name="tag" type="Variant" default="null" /> - <param index="4" name="accelerator" type="int" enum="Key" default="0" /> - <param index="5" name="index" type="int" default="-1" /> + <param index="3" name="key_callback" type="Callable" /> + <param index="4" name="tag" type="Variant" default="null" /> + <param index="5" name="accelerator" type="int" enum="Key" default="0" /> + <param index="6" name="index" type="int" default="-1" /> <description> Adds a new checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -168,9 +169,10 @@ <param index="1" name="icon" type="Texture2D" /> <param index="2" name="label" type="String" /> <param index="3" name="callback" type="Callable" /> - <param index="4" name="tag" type="Variant" default="null" /> - <param index="5" name="accelerator" type="int" enum="Key" default="0" /> - <param index="6" name="index" type="int" default="-1" /> + <param index="4" name="key_callback" type="Callable" /> + <param index="5" name="tag" type="Variant" default="null" /> + <param index="6" name="accelerator" type="int" enum="Key" default="0" /> + <param index="7" name="index" type="int" default="-1" /> <description> Adds a new checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -188,9 +190,10 @@ <param index="1" name="icon" type="Texture2D" /> <param index="2" name="label" type="String" /> <param index="3" name="callback" type="Callable" /> - <param index="4" name="tag" type="Variant" default="null" /> - <param index="5" name="accelerator" type="int" enum="Key" default="0" /> - <param index="6" name="index" type="int" default="-1" /> + <param index="4" name="key_callback" type="Callable" /> + <param index="5" name="tag" type="Variant" default="null" /> + <param index="6" name="accelerator" type="int" enum="Key" default="0" /> + <param index="7" name="index" type="int" default="-1" /> <description> Adds a new item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -208,9 +211,10 @@ <param index="1" name="icon" type="Texture2D" /> <param index="2" name="label" type="String" /> <param index="3" name="callback" type="Callable" /> - <param index="4" name="tag" type="Variant" default="null" /> - <param index="5" name="accelerator" type="int" enum="Key" default="0" /> - <param index="6" name="index" type="int" default="-1" /> + <param index="4" name="key_callback" type="Callable" /> + <param index="5" name="tag" type="Variant" default="null" /> + <param index="6" name="accelerator" type="int" enum="Key" default="0" /> + <param index="7" name="index" type="int" default="-1" /> <description> Adds a new radio-checkable item with text [param label] and icon [param icon] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -228,9 +232,10 @@ <param index="0" name="menu_root" type="String" /> <param index="1" name="label" type="String" /> <param index="2" name="callback" type="Callable" /> - <param index="3" name="tag" type="Variant" default="null" /> - <param index="4" name="accelerator" type="int" enum="Key" default="0" /> - <param index="5" name="index" type="int" default="-1" /> + <param index="3" name="key_callback" type="Callable" /> + <param index="4" name="tag" type="Variant" default="null" /> + <param index="5" name="accelerator" type="int" enum="Key" default="0" /> + <param index="6" name="index" type="int" default="-1" /> <description> Adds a new item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -249,9 +254,10 @@ <param index="2" name="max_states" type="int" /> <param index="3" name="default_state" type="int" /> <param index="4" name="callback" type="Callable" /> - <param index="5" name="tag" type="Variant" default="null" /> - <param index="6" name="accelerator" type="int" enum="Key" default="0" /> - <param index="7" name="index" type="int" default="-1" /> + <param index="5" name="key_callback" type="Callable" /> + <param index="6" name="tag" type="Variant" default="null" /> + <param index="7" name="accelerator" type="int" enum="Key" default="0" /> + <param index="8" name="index" type="int" default="-1" /> <description> Adds a new item with text [param labe] to the global menu with ID [param menu_root]. Contrarily to normal binary items, multistate items can have more than two states, as defined by [param max_states]. Each press or activate of the item will increase the state by one. The default value is defined by [param default_state]. @@ -270,9 +276,10 @@ <param index="0" name="menu_root" type="String" /> <param index="1" name="label" type="String" /> <param index="2" name="callback" type="Callable" /> - <param index="3" name="tag" type="Variant" default="null" /> - <param index="4" name="accelerator" type="int" enum="Key" default="0" /> - <param index="5" name="index" type="int" default="-1" /> + <param index="3" name="key_callback" type="Callable" /> + <param index="4" name="tag" type="Variant" default="null" /> + <param index="5" name="accelerator" type="int" enum="Key" default="0" /> + <param index="6" name="index" type="int" default="-1" /> <description> Adds a new radio-checkable item with text [param label] to the global menu with ID [param menu_root]. Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. @@ -384,6 +391,15 @@ [b]Note:[/b] This method is implemented on macOS. </description> </method> + <method name="global_menu_get_item_key_callback" qualifiers="const"> + <return type="Callable" /> + <param index="0" name="menu_root" type="String" /> + <param index="1" name="idx" type="int" /> + <description> + Returns the callback of the item accelerator at index [param idx]. + [b]Note:[/b] This method is implemented on macOS. + </description> + </method> <method name="global_menu_get_item_max_states" qualifiers="const"> <return type="int" /> <param index="0" name="menu_root" type="String" /> @@ -502,7 +518,7 @@ <param index="1" name="idx" type="int" /> <param index="2" name="callback" type="Callable" /> <description> - Sets the callback of the item at index [param idx]. Callback is emitted when an item is pressed or its accelerator is activated. + Sets the callback of the item at index [param idx]. Callback is emitted when an item is pressed. [b]Note:[/b] This method is implemented on macOS. </description> </method> @@ -557,6 +573,16 @@ [b]Note:[/b] This method is implemented on macOS. </description> </method> + <method name="global_menu_set_item_key_callback"> + <return type="void" /> + <param index="0" name="menu_root" type="String" /> + <param index="1" name="idx" type="int" /> + <param index="2" name="key_callback" type="Callable" /> + <description> + Sets the callback of the item at index [param idx]. Callback is emitted when its accelerator is activated. + [b]Note:[/b] This method is implemented on macOS. + </description> + </method> <method name="global_menu_set_item_max_states"> <return type="void" /> <param index="0" name="menu_root" type="String" /> diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml new file mode 100644 index 0000000000..1d63af9233 --- /dev/null +++ b/doc/classes/EditorExportPlatform.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorExportPlatform" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> +</class> diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 091bac7d8e..3e8ce10aa5 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -10,6 +10,51 @@ <tutorials> </tutorials> <methods> + <method name="_begin_customize_resources" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="features" type="PackedStringArray" /> + <description> + Return true if this plugin will customize resources based on the platform and features used. + </description> + </method> + <method name="_begin_customize_scenes" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="platform" type="EditorExportPlatform" /> + <param index="1" name="features" type="PackedStringArray" /> + <description> + Return true if this plugin will customize scenes based on the platform and features used. + </description> + </method> + <method name="_customize_resource" qualifiers="virtual"> + <return type="Resource" /> + <param index="0" name="resource" type="Resource" /> + <param index="1" name="path" type="String" /> + <description> + Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code]. + The [i]path[/i] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty. + </description> + </method> + <method name="_customize_scene" qualifiers="virtual"> + <return type="Node" /> + <param index="0" name="scene" type="Node" /> + <param index="1" name="path" type="String" /> + <description> + Customize a scene. If changes are made to it, return the same or a new scene. Otherwise, return [code]null[/code]. If a new scene is returned, it is up to you to dispose of the old one. + </description> + </method> + <method name="_end_customize_resources" qualifiers="virtual"> + <return type="void" /> + <description> + This is called when the customization process for resources ends. + </description> + </method> + <method name="_end_customize_scenes" qualifiers="virtual"> + <return type="void" /> + <description> + This is called when the customization process for scenes ends. + </description> + </method> <method name="_export_begin" qualifiers="virtual"> <return type="void" /> <param index="0" name="features" type="PackedStringArray" /> @@ -36,6 +81,18 @@ Calling [method skip] inside this callback will make the file not included in the export. </description> </method> + <method name="_get_customization_configuration_hash" qualifiers="virtual const"> + <return type="int" /> + <description> + Return a hash based on the configuration passed (for both scenes and resources). This helps keep separate caches for separate export configurations. + </description> + </method> + <method name="_get_name" qualifiers="virtual const"> + <return type="String" /> + <description> + Return the name identifier of this plugin (for future identification by the exporter). + </description> + </method> <method name="add_file"> <return type="void" /> <param index="0" name="path" type="String" /> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 864dbd423a..695f2cbc66 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -63,21 +63,6 @@ <member name="ambient_light_source" type="int" setter="set_ambient_source" getter="get_ambient_source" enum="Environment.AmbientSource" default="0"> The ambient light source to use for rendering materials and global illumination. </member> - <member name="auto_exposure_enabled" type="bool" setter="set_tonemap_auto_exposure_enabled" getter="is_tonemap_auto_exposure_enabled" default="false"> - If [code]true[/code], enables the tonemapping auto exposure mode of the scene renderer. If [code]true[/code], the renderer will automatically determine the exposure setting to adapt to the scene's illumination and the observed light. - </member> - <member name="auto_exposure_max_luma" type="float" setter="set_tonemap_auto_exposure_max" getter="get_tonemap_auto_exposure_max" default="8.0"> - The maximum luminance value for the auto exposure. - </member> - <member name="auto_exposure_min_luma" type="float" setter="set_tonemap_auto_exposure_min" getter="get_tonemap_auto_exposure_min" default="0.05"> - The minimum luminance value for the auto exposure. - </member> - <member name="auto_exposure_scale" type="float" setter="set_tonemap_auto_exposure_grey" getter="get_tonemap_auto_exposure_grey" default="0.4"> - The scale of the auto exposure effect. Affects the intensity of auto exposure. - </member> - <member name="auto_exposure_speed" type="float" setter="set_tonemap_auto_exposure_speed" getter="get_tonemap_auto_exposure_speed" default="0.5"> - The speed of the auto exposure effect. Affects the time needed for the camera to perform auto exposure. - </member> <member name="background_camera_feed_id" type="int" setter="set_camera_feed_id" getter="get_camera_feed_id" default="1"> The ID of the camera feed to show in the background. </member> @@ -87,14 +72,17 @@ <member name="background_color" type="Color" setter="set_bg_color" getter="get_bg_color" default="Color(0, 0, 0, 1)"> The [Color] displayed for clear areas of the scene. Only effective when using the [constant BG_COLOR] background mode. </member> - <member name="background_energy" type="float" setter="set_bg_energy" getter="get_bg_energy" default="1.0"> - The power of the light emitted by the background. + <member name="background_energy_multiplier" type="float" setter="set_bg_energy_multiplier" getter="get_bg_energy_multiplier" default="1.0"> + Multiplier for background energy. Increase to make background brighter, decrease to make background dimmer. + </member> + <member name="background_intensity" type="float" setter="set_bg_intensity" getter="get_bg_intensity" default="30000.0"> + Luminance of background measured in nits (candela per square meter). Only used when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. The default value is roughly equivalent to the sky at midday. </member> <member name="background_mode" type="int" setter="set_background" getter="get_background" enum="Environment.BGMode" default="0"> The background mode. See [enum BGMode] for possible values. </member> <member name="fog_aerial_perspective" type="float" setter="set_fog_aerial_perspective" getter="get_fog_aerial_perspective" default="0.0"> - Blend factor between the fog's color and the color of the background [Sky]. Must have [member background_mode] set to [constant BG_SKY]. + If set above [code]0.0[/code] (exclusive), blends between the fog's color and the color of the background [Sky]. This has a small performance cost when set above [code]0.0[/code]. Must have [member background_mode] set to [constant BG_SKY]. This is useful to simulate [url=https://en.wikipedia.org/wiki/Aerial_perspective]aerial perspective[/url] in large scenes with low density fog. However, it is not very useful for high-density fog, as the sky will shine through. When set to [code]1.0[/code], the fog color comes completely from the [Sky]. If set to [code]0.0[/code], aerial perspective is disabled. </member> <member name="fog_density" type="float" setter="set_fog_density" getter="get_fog_density" default="0.01"> @@ -115,6 +103,10 @@ <member name="fog_light_energy" type="float" setter="set_fog_light_energy" getter="get_fog_light_energy" default="1.0"> The fog's brightness. Higher values result in brighter fog. </member> + <member name="fog_sky_affect" type="float" setter="set_fog_sky_affect" getter="get_fog_sky_affect" default="1.0"> + The factor to use when affecting the sky with non-volumetric fog. [code]1.0[/code] means that fog can fully obscure the sky. Lower values reduce the impact of fog on sky rendering, with [code]0.0[/code] not affecting sky rendering at all. + [b]Note:[/b] [member fog_sky_affect] has no visual effect if [member fog_aerial_perspective] is [code]1.0[/code]. + </member> <member name="fog_sun_scatter" type="float" setter="set_fog_sun_scatter" getter="get_fog_sun_scatter" default="0.0"> If set above [code]0.0[/code], renders the scene's directional light(s) in the fog color depending on the view angle. This can be used to give the impression that the sun is "piercing" through the fog. </member> @@ -327,6 +319,9 @@ <member name="volumetric_fog_length" type="float" setter="set_volumetric_fog_length" getter="get_volumetric_fog_length" default="64.0"> The distance over which the volumetric fog is computed. Increase to compute fog over a greater range, decrease to add more detail when a long range is not needed. For best quality fog, keep this as low as possible. </member> + <member name="volumetric_fog_sky_affect" type="float" setter="set_volumetric_fog_sky_affect" getter="get_volumetric_fog_sky_affect" default="1.0"> + The factor to use when affecting the sky with volumetric fog. [code]1.0[/code] means that volumetric fog can fully obscure the sky. Lower values reduce the impact of volumetric fog on sky rendering, with [code]0.0[/code] not affecting sky rendering at all. + </member> <member name="volumetric_fog_temporal_reprojection_amount" type="float" setter="set_volumetric_fog_temporal_reprojection_amount" getter="get_volumetric_fog_temporal_reprojection_amount" default="0.9"> The amount by which to blend the last frame with the current frame. A higher number results in smoother volumetric fog, but makes "ghosting" much worse. A lower value reduces ghosting but can result in the per-frame temporal jitter becoming visible. </member> diff --git a/doc/classes/FlowContainer.xml b/doc/classes/FlowContainer.xml index 256e20447b..d449049ef1 100644 --- a/doc/classes/FlowContainer.xml +++ b/doc/classes/FlowContainer.xml @@ -17,4 +17,18 @@ </description> </method> </methods> + <members> + <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false"> + If [code]true[/code], the [FlowContainer] will arrange its children vertically, rather than horizontally. + Can't be changed when using [HFlowContainer] and [VFlowContainer]. + </member> + </members> + <theme_items> + <theme_item name="h_separation" data_type="constant" type="int" default="4"> + The horizontal separation of children nodes. + </theme_item> + <theme_item name="v_separation" data_type="constant" type="int" default="4"> + The vertical separation of children nodes. + </theme_item> + </theme_items> </class> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index cc8484d9fa..e9ebbc0a41 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -11,6 +11,12 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="get_correlated_color" qualifiers="const"> + <return type="Color" /> + <description> + Returns the [Color] of an idealized blackbody at the given [member light_temperature]. This value is calculated internally based on the [member light_temperature]. This [Color] is multiplied by [member light_color] before being sent to the [RenderingServer]. + </description> + </method> <method name="get_param" qualifiers="const"> <return type="float" /> <param index="0" name="param" type="int" enum="Light3D.Param" /> @@ -67,6 +73,15 @@ Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). [b]Note:[/b] This property is ignored if [member light_energy] is equal to [code]0.0[/code], as the light won't be present at all in the GI shader. </member> + <member name="light_intensity_lumens" type="float" setter="set_param" getter="get_param"> + Used by positional lights ([OmniLight3D] and [SpotLight3D]) when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. Sets the intensity of the light source measured in Lumens. Lumens are a measure of luminous flux, which is the total amount of visible light emitted by a light source per unit of time. + For [SpotLight3D]s, we assume that the area outside the visible cone is surrounded by a perfect light absorbing material. Accordingly, the apparent brightness of the cone area does not change as the cone increases and decreases in size. + A typical household lightbulb can range from around 600 lumens to 1,200 lumens, a candle is about 13 lumens, while a streetlight can be approximately 60,000 lumens. + </member> + <member name="light_intensity_lux" type="float" setter="set_param" getter="get_param"> + Used by [DirectionalLight3D]s when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. Sets the intensity of the light source measured in Lux. Lux is a measure pf luminous flux per unit area, it is equal to one lumen per square metre. Lux is the measure of how much light hits a surface at a given time. + On a clear sunny day a surface in direct sunlight may be approximately 100,000 lux, a typical room in a home may be approximately 50 lux, while the moonlit ground may be approximately 0.1 lux. + </member> <member name="light_negative" type="bool" setter="set_negative" getter="is_negative" default="false"> If [code]true[/code], the light's effect is reversed, darkening areas and casting bright shadows. </member> @@ -80,6 +95,10 @@ <member name="light_specular" type="float" setter="set_param" getter="get_param" default="0.5"> The intensity of the specular blob in objects affected by the light. At [code]0[/code], the light becomes a pure diffuse light. When not baking emission, this can be used to avoid unrealistic reflections when placing lights above an emissive surface. </member> + <member name="light_temperature" type="float" setter="set_temperature" getter="get_temperature"> + Sets the color temperature of the light source, measured in Kelvin. This is used to calculate a correlated color temperature which tints the [member light_color]. + The sun on a cloudy day is approximately 6500 Kelvin, on a clear day it is between 5500 to 6000 Kelvin, and on a clear day at sunrise or sunset it ranges to around 1850 Kelvin. + </member> <member name="light_volumetric_fog_energy" type="float" setter="set_param" getter="get_param" default="1.0"> Secondary multiplier multiplied with [member light_energy] then used with the [Environment]'s volumetric fog (if enabled). If set to [code]0.0[/code], computing volumetric fog will be skipped for this light, which can improve performance for large amounts of lights when volumetric fog is enabled. [b]Note:[/b] To prevent short-lived dynamic light effects from poorly interacting with volumetric fog, lights used in those effects should have [member light_volumetric_fog_energy] set to [code]0.0[/code] unless [member Environment.volumetric_fog_temporal_reprojection_enabled] is disabled (or unless the reprojection amount is significantly lowered). @@ -166,7 +185,10 @@ <constant name="PARAM_TRANSMITTANCE_BIAS" value="19" enum="Param"> Constant for accessing [member shadow_transmittance_bias]. </constant> - <constant name="PARAM_MAX" value="20" enum="Param"> + <constant name="PARAM_INTENSITY" value="20" enum="Param"> + Constant for accessing [member light_intensity_lumens] and [member light_intensity_lux]. Only used when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. + </constant> + <constant name="PARAM_MAX" value="21" enum="Param"> Represents the size of the [enum Param] enum. </constant> <constant name="BAKE_DISABLED" value="0" enum="BakeMode"> diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index c0766cd1ec..dd8c7be489 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -20,6 +20,9 @@ <member name="bounces" type="int" setter="set_bounces" getter="get_bounces" default="1"> Number of light bounces that are taken into account during baking. Higher values result in brighter, more realistic lighting, at the cost of longer bake times. If set to [code]0[/code], only environment lighting, direct light and emissive lighting is baked. </member> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The [CameraAttributes] resource that specifies exposure levels to bake at. Auto-exposure and non exposure properties will be ignored. Exposure settings should be used to reduce the dynamic range present when baking. If exposure is too high, the [LightmapGI] will have banding artifacts or may have over-exposure artifacts. + </member> <member name="directional" type="bool" setter="set_directional" getter="is_directional" default="false"> If [code]true[/code], bakes lightmaps to contain directional information as spherical harmonics. This results in more realistic lighting appearance, especially with normal mapped materials and for lights that have their direct light baked ([member Light3D.light_bake_mode] set to [constant Light3D.BAKE_STATIC]). The directional information is also used to provide rough reflections for static and dynamic objects. This has a small run-time performance cost as the shader has to perform more work to interpret the direction information from the lightmap. Directional lightmaps also take longer to bake and result in larger file sizes. [b]Note:[/b] The property's name has no relationship with [DirectionalLight3D]. [member directional] works with all light types. diff --git a/doc/classes/MultiplayerPeerExtension.xml b/doc/classes/MultiplayerPeerExtension.xml index 3a193abd7d..18bc18e6e7 100644 --- a/doc/classes/MultiplayerPeerExtension.xml +++ b/doc/classes/MultiplayerPeerExtension.xml @@ -16,7 +16,7 @@ </description> </method> <method name="_get_connection_status" qualifiers="virtual const"> - <return type="int" /> + <return type="int" enum="MultiplayerPeer.ConnectionStatus" /> <description> Called when the connection status is requested on the [MultiplayerPeer] (see [method MultiplayerPeer.get_connection_status]). </description> @@ -28,7 +28,7 @@ </description> </method> <method name="_get_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="r_buffer" type="const uint8_t **" /> <param index="1" name="r_buffer_size" type="int32_t*" /> <description> @@ -54,7 +54,7 @@ </description> </method> <method name="_get_transfer_mode" qualifiers="virtual const"> - <return type="int" /> + <return type="int" enum="MultiplayerPeer.TransferMode" /> <description> Called when the transfer mode to use is read on this [MultiplayerPeer] (see [member MultiplayerPeer.transfer_mode]). </description> @@ -78,13 +78,13 @@ </description> </method> <method name="_poll" qualifiers="virtual"> - <return type="int" /> + <return type="void" /> <description> Called when the [MultiplayerAPI] is polled. See [method MultiplayerAPI.poll]. </description> </method> <method name="_put_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_buffer" type="const uint8_t*" /> <param index="1" name="p_buffer_size" type="int" /> <description> @@ -92,7 +92,7 @@ </description> </method> <method name="_put_packet_script" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_buffer" type="PackedByteArray" /> <description> Called when a packet needs to be sent by the [MultiplayerAPI], if [method _put_packet] isn't implemented. Use this when extending this class via GDScript. @@ -121,7 +121,7 @@ </method> <method name="_set_transfer_mode" qualifiers="virtual"> <return type="void" /> - <param index="0" name="p_mode" type="int" /> + <param index="0" name="p_mode" type="int" enum="MultiplayerPeer.TransferMode" /> <description> Called when the transfer mode is set on this [MultiplayerPeer] (see [member MultiplayerPeer.transfer_mode]). </description> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index bf3aff2c58..3437e0eca4 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -165,7 +165,7 @@ <signal name="velocity_computed"> <param index="0" name="safe_velocity" type="Vector3" /> <description> - Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. + Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true. </description> </signal> </signals> diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index 7c48f0b52b..3bb5b361ca 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -171,7 +171,7 @@ <signal name="velocity_computed"> <param index="0" name="safe_velocity" type="Vector3" /> <description> - Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. + Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true. </description> </signal> </signals> diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml new file mode 100644 index 0000000000..1e086fb730 --- /dev/null +++ b/doc/classes/NavigationLink2D.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NavigationLink2D" inherits="Node2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Creates a link between two locations that [NavigationServer2D] can route agents through. + </brief_description> + <description> + Creates a link between two locations that [NavigationServer2D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_navigation_layer_value" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer_number" type="int" /> + <description> + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + <method name="set_navigation_layer_value"> + <return type="void" /> + <param index="0" name="layer_number" type="int" /> + <param index="1" name="value" type="bool" /> + <description> + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + </methods> + <members> + <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> + Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + </member> + <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> + Whether this link is currently active. If [code]false[/code], [method NavigationServer2D.map_get_path] will ignore this link. + </member> + <member name="end_location" type="Vector2" setter="set_end_location" getter="get_end_location" default="Vector2(0, 0)"> + Ending position of the link. + This position will search out the nearest polygon in the navigation mesh to attach to. + The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. + </member> + <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> + When pathfinding enters this link from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + </member> + <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> + A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer2D.map_get_path]. + </member> + <member name="start_location" type="Vector2" setter="set_start_location" getter="get_start_location" default="Vector2(0, 0)"> + Starting position of the link. + This position will search out the nearest polygon in the navigation mesh to attach to. + The distance the link will search is controlled by [method NavigationServer2D.map_set_link_connection_radius]. + </member> + <member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0"> + When pathfinding moves along the link the traveled distance is multiplied with [code]travel_cost[/code] for determining the shortest path. + </member> + </members> +</class> diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml new file mode 100644 index 0000000000..4d5d81bec5 --- /dev/null +++ b/doc/classes/NavigationLink3D.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NavigationLink3D" inherits="Node3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Creates a link between two locations that [NavigationServer3D] can route agents through. + </brief_description> + <description> + Creates a link between two locations that [NavigationServer3D] can route agents through. Links can be used to express navigation methods that aren't just traveling along the surface of the navigation mesh, like zip-lines, teleporters, or jumping across gaps. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_navigation_layer_value" qualifiers="const"> + <return type="bool" /> + <param index="0" name="layer_number" type="int" /> + <description> + Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + <method name="set_navigation_layer_value"> + <return type="void" /> + <param index="0" name="layer_number" type="int" /> + <param index="1" name="value" type="bool" /> + <description> + Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + </methods> + <members> + <member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true"> + Whether this link can be traveled in both directions or only from [member start_location] to [member end_location]. + </member> + <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> + Whether this link is currently active. If [code]false[/code], [method NavigationServer3D.map_get_path] will ignore this link. + </member> + <member name="end_location" type="Vector3" setter="set_end_location" getter="get_end_location" default="Vector3(0, 0, 0)"> + Ending position of the link. + This position will search out the nearest polygon in the navigation mesh to attach to. + The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. + </member> + <member name="enter_cost" type="float" setter="set_enter_cost" getter="get_enter_cost" default="0.0"> + When pathfinding enters this link from another regions navmesh the [code]enter_cost[/code] value is added to the path distance for determining the shortest path. + </member> + <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1"> + A bitfield determining all navigation layers the link belongs to. These navigation layers will be checked when requesting a path with [method NavigationServer3D.map_get_path]. + </member> + <member name="start_location" type="Vector3" setter="set_start_location" getter="get_start_location" default="Vector3(0, 0, 0)"> + Starting position of the link. + This position will search out the nearest polygon in the navigation mesh to attach to. + The distance the link will search is controlled by [method NavigationServer3D.map_set_link_connection_radius]. + </member> + <member name="travel_cost" type="float" setter="set_travel_cost" getter="get_travel_cost" default="1.0"> + When pathfinding moves along the link the traveled distance is multiplied with [code]travel_cost[/code] for determining the shortest path. + </member> + </members> +</class> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index b85c1c6649..0874e183e4 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -133,6 +133,117 @@ Returns all created navigation map [RID]s on the NavigationServer. This returns both 2D and 3D created navigation maps as there is technically no distinction between them. </description> </method> + <method name="link_create" qualifiers="const"> + <return type="RID" /> + <description> + Create a new link between two locations on a map. + </description> + </method> + <method name="link_get_end_location" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the ending location of this [code]link[/code]. + </description> + </method> + <method name="link_get_enter_cost" qualifiers="const"> + <return type="float" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the [code]enter_cost[/code] of this [code]link[/code]. + </description> + </method> + <method name="link_get_map" qualifiers="const"> + <return type="RID" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the navigation map [RID] the requested [code]link[/code] is currently assigned to. + </description> + </method> + <method name="link_get_navigation_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the navigation layers for this [code]link[/code]. + </description> + </method> + <method name="link_get_start_location" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the starting location of this [code]link[/code]. + </description> + </method> + <method name="link_get_travel_cost" qualifiers="const"> + <return type="float" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the [code]travel_cost[/code] of this [code]link[/code]. + </description> + </method> + <method name="link_is_bidirectional" qualifiers="const"> + <return type="bool" /> + <param index="0" name="link" type="RID" /> + <description> + Returns whether this [code]link[/code] can be travelled in both directions. + </description> + </method> + <method name="link_set_bidirectional" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="bidirectional" type="bool" /> + <description> + Sets whether this [code]link[/code] can be travelled in both directions. + </description> + </method> + <method name="link_set_end_location" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="location" type="Vector2" /> + <description> + Sets the exit location for the [code]link[/code]. + </description> + </method> + <method name="link_set_enter_cost" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="enter_cost" type="float" /> + <description> + Sets the [code]enter_cost[/code] for this [code]link[/code]. + </description> + </method> + <method name="link_set_map" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="map" type="RID" /> + <description> + Sets the navigation map [RID] for the link. + </description> + </method> + <method name="link_set_navigation_layers" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <description> + Set the links's navigation layers. This allows selecting links from a path request (when using [method NavigationServer2D.map_get_path]). + </description> + </method> + <method name="link_set_start_location" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="location" type="Vector2" /> + <description> + Sets the entry location for this [code]link[/code]. + </description> + </method> + <method name="link_set_travel_cost" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="travel_cost" type="float" /> + <description> + Sets the [code]travel_cost[/code] for this [code]link[/code]. + </description> + </method> <method name="map_create" qualifiers="const"> <return type="RID" /> <description> @@ -186,6 +297,20 @@ Returns the edge connection margin of the map. The edge connection margin is a distance used to connect two regions. </description> </method> + <method name="map_get_link_connection_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="map" type="RID" /> + <description> + Returns the link connection radius of the map. This distance is the maximum range any link will search for navigation mesh polygons to connect to. + </description> + </method> + <method name="map_get_links" qualifiers="const"> + <return type="RID[]" /> + <param index="0" name="map" type="RID" /> + <description> + Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code]. + </description> + </method> <method name="map_get_path" qualifiers="const"> <return type="PackedVector2Array" /> <param index="0" name="map" type="RID" /> @@ -235,6 +360,14 @@ Set the map edge connection margin used to weld the compatible region edges. </description> </method> + <method name="map_set_link_connection_radius" qualifiers="const"> + <return type="void" /> + <param index="0" name="map" type="RID" /> + <param index="1" name="radius" type="float" /> + <description> + Set the map's link connection radius used to connect links to navigation polygons. + </description> + </method> <method name="region_create" qualifiers="const"> <return type="RID" /> <description> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 5b2a8fc08b..255f2a902c 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -133,6 +133,117 @@ Returns all created navigation map [RID]s on the NavigationServer. This returns both 2D and 3D created navigation maps as there is technically no distinction between them. </description> </method> + <method name="link_create" qualifiers="const"> + <return type="RID" /> + <description> + Create a new link between two locations on a map. + </description> + </method> + <method name="link_get_end_location" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the ending location of this [code]link[/code]. + </description> + </method> + <method name="link_get_enter_cost" qualifiers="const"> + <return type="float" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the [code]enter_cost[/code] of this [code]link[/code]. + </description> + </method> + <method name="link_get_map" qualifiers="const"> + <return type="RID" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the navigation map [RID] the requested [code]link[/code] is currently assigned to. + </description> + </method> + <method name="link_get_navigation_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the navigation layers for this [code]link[/code]. + </description> + </method> + <method name="link_get_start_location" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the starting location of this [code]link[/code]. + </description> + </method> + <method name="link_get_travel_cost" qualifiers="const"> + <return type="float" /> + <param index="0" name="link" type="RID" /> + <description> + Returns the [code]travel_cost[/code] of this [code]link[/code]. + </description> + </method> + <method name="link_is_bidirectional" qualifiers="const"> + <return type="bool" /> + <param index="0" name="link" type="RID" /> + <description> + Returns whether this [code]link[/code] can be travelled in both directions. + </description> + </method> + <method name="link_set_bidirectional" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="bidirectional" type="bool" /> + <description> + Sets whether this [code]link[/code] can be travelled in both directions. + </description> + </method> + <method name="link_set_end_location" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="location" type="Vector3" /> + <description> + Sets the exit location for the [code]link[/code]. + </description> + </method> + <method name="link_set_enter_cost" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="enter_cost" type="float" /> + <description> + Sets the [code]enter_cost[/code] for this [code]link[/code]. + </description> + </method> + <method name="link_set_map" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="map" type="RID" /> + <description> + Sets the navigation map [RID] for the link. + </description> + </method> + <method name="link_set_navigation_layers" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="navigation_layers" type="int" /> + <description> + Set the links's navigation layers. This allows selecting links from a path request (when using [method NavigationServer3D.map_get_path]). + </description> + </method> + <method name="link_set_start_location" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="location" type="Vector3" /> + <description> + Sets the entry location for this [code]link[/code]. + </description> + </method> + <method name="link_set_travel_cost" qualifiers="const"> + <return type="void" /> + <param index="0" name="link" type="RID" /> + <param index="1" name="travel_cost" type="float" /> + <description> + Sets the [code]travel_cost[/code] for this [code]link[/code]. + </description> + </method> <method name="map_create" qualifiers="const"> <return type="RID" /> <description> @@ -204,6 +315,20 @@ Returns the edge connection margin of the map. This distance is the minimum vertex distance needed to connect two edges from different regions. </description> </method> + <method name="map_get_link_connection_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="map" type="RID" /> + <description> + Returns the link connection radius of the map. This distance is the maximum range any link will search for navigation mesh polygons to connect to. + </description> + </method> + <method name="map_get_links" qualifiers="const"> + <return type="RID[]" /> + <param index="0" name="map" type="RID" /> + <description> + Returns all navigation link [RID]s that are currently assigned to the requested navigation [code]map[/code]. + </description> + </method> <method name="map_get_path" qualifiers="const"> <return type="PackedVector3Array" /> <param index="0" name="map" type="RID" /> @@ -260,6 +385,14 @@ Set the map edge connection margin used to weld the compatible region edges. </description> </method> + <method name="map_set_link_connection_radius" qualifiers="const"> + <return type="void" /> + <param index="0" name="map" type="RID" /> + <param index="1" name="radius" type="float" /> + <description> + Set the map's link connection radius used to connect links to navigation polygons. + </description> + </method> <method name="map_set_up" qualifiers="const"> <return type="void" /> <param index="0" name="map" type="RID" /> diff --git a/doc/classes/PacketPeerExtension.xml b/doc/classes/PacketPeerExtension.xml index 28263b3f59..afb1aa4016 100644 --- a/doc/classes/PacketPeerExtension.xml +++ b/doc/classes/PacketPeerExtension.xml @@ -18,14 +18,14 @@ </description> </method> <method name="_get_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="r_buffer" type="const uint8_t **" /> <param index="1" name="r_buffer_size" type="int32_t*" /> <description> </description> </method> <method name="_put_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_buffer" type="const uint8_t*" /> <param index="1" name="p_buffer_size" type="int" /> <description> diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml index 7c2ea088c8..9d303d80e5 100644 --- a/doc/classes/PhysicalSkyMaterial.xml +++ b/doc/classes/PhysicalSkyMaterial.xml @@ -11,8 +11,7 @@ <tutorials> </tutorials> <members> - <member name="exposure" type="float" setter="set_exposure" getter="get_exposure" default="0.1"> - Sets the exposure of the sky. Higher exposure values make the entire sky brighter. + <member name="energy_multiplier" type="float" setter="set_energy_multiplier" getter="get_energy_multiplier" default="1.0"> </member> <member name="ground_color" type="Color" setter="set_ground_color" getter="get_ground_color" default="Color(0.1, 0.07, 0.034, 1)"> Modulates the [Color] on the bottom half of the sky to represent the ground. diff --git a/doc/classes/ProceduralSkyMaterial.xml b/doc/classes/ProceduralSkyMaterial.xml index 3cc4bd71f7..6ba8e57380 100644 --- a/doc/classes/ProceduralSkyMaterial.xml +++ b/doc/classes/ProceduralSkyMaterial.xml @@ -17,14 +17,14 @@ <member name="ground_curve" type="float" setter="set_ground_curve" getter="get_ground_curve" default="0.02"> How quickly the [member ground_horizon_color] fades into the [member ground_bottom_color]. </member> - <member name="ground_energy" type="float" setter="set_ground_energy" getter="get_ground_energy" default="1.0"> - Amount of energy contribution from the ground. + <member name="ground_energy_multiplier" type="float" setter="set_ground_energy_multiplier" getter="get_ground_energy_multiplier" default="1.0"> + Multiplier for ground color. A higher value will make the ground brighter. </member> <member name="ground_horizon_color" type="Color" setter="set_ground_horizon_color" getter="get_ground_horizon_color" default="Color(0.6463, 0.6558, 0.6708, 1)"> Color of the ground at the horizon. Blends with [member ground_bottom_color]. </member> <member name="sky_cover" type="Texture2D" setter="set_sky_cover" getter="get_sky_cover"> - The sky cover texture to use. This texture must use an equirectangular projection (similar to [PanoramaSkyMaterial]). The texture's colors will be [i]added[/i] to the existing sky color, and will be multiplied by [member sky_energy] and [member sky_cover_modulate]. This is mainly suited to displaying stars at night, but it can also be used to display clouds at day or night (with a non-physically-accurate look). + The sky cover texture to use. This texture must use an equirectangular projection (similar to [PanoramaSkyMaterial]). The texture's colors will be [i]added[/i] to the existing sky color, and will be multiplied by [member sky_energy_multiplier] and [member sky_cover_modulate]. This is mainly suited to displaying stars at night, but it can also be used to display clouds at day or night (with a non-physically-accurate look). </member> <member name="sky_cover_modulate" type="Color" setter="set_sky_cover_modulate" getter="get_sky_cover_modulate" default="Color(1, 1, 1, 1)"> The tint to apply to the [member sky_cover] texture. This can be used to change the sky cover's colors or opacity independently of the sky energy, which is useful for day/night or weather transitions. Only effective if a texture is defined in [member sky_cover]. @@ -32,8 +32,8 @@ <member name="sky_curve" type="float" setter="set_sky_curve" getter="get_sky_curve" default="0.15"> How quickly the [member sky_horizon_color] fades into the [member sky_top_color]. </member> - <member name="sky_energy" type="float" setter="set_sky_energy" getter="get_sky_energy" default="1.0"> - Amount of energy contribution from the sky. + <member name="sky_energy_multiplier" type="float" setter="set_sky_energy_multiplier" getter="get_sky_energy_multiplier" default="1.0"> + Multiplier for sky color. A higher value will make the sky brighter. </member> <member name="sky_horizon_color" type="Color" setter="set_sky_horizon_color" getter="get_sky_horizon_color" default="Color(0.6463, 0.6558, 0.6708, 1)"> Color of the sky at the horizon. Blends with [member sky_top_color]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index e8e8ca3725..e62c0433b4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -497,6 +497,12 @@ <member name="debug/shapes/navigation/enable_geometry_face_random_color" type="bool" setter="" getter="" default="true"> If enabled, colorizes each navigation mesh polygon face with a random color when "Visible Navigation" is enabled in the Debug menu. </member> + <member name="debug/shapes/navigation/enable_link_connections" type="bool" setter="" getter="" default="true"> + If enabled, displays navigation link connections when "Visible Navigation" is enabled in the Debug menu. + </member> + <member name="debug/shapes/navigation/enable_link_connections_xray" type="bool" setter="" getter="" default="true"> + If enabled, displays navigation link connections through geometry when "Visible Navigation" is enabled in the Debug menu. + </member> <member name="debug/shapes/navigation/geometry_color" type="Color" setter="" getter="" default="Color(0.1, 1, 0.7, 0.4)"> Color of the navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu. </member> @@ -512,6 +518,12 @@ <member name="debug/shapes/navigation/geometry_face_disabled_color" type="Color" setter="" getter="" default="Color(0.5, 0.5, 0.5, 0.4)"> Color to display disabled navigation mesh polygon faces, visible when "Visible Navigation" is enabled in the Debug menu. </member> + <member name="debug/shapes/navigation/link_connection_color" type="Color" setter="" getter="" default="Color(1, 0.5, 1, 1)"> + Color to use to display navigation link connections, visible when "Visible Navigation" is enabled in the Debug menu. + </member> + <member name="debug/shapes/navigation/link_connection_disabled_color" type="Color" setter="" getter="" default="Color(0.5, 0.5, 0.5, 1)"> + Color to use to display disabled navigation link connections, visible when "Visible Navigation" is enabled in the Debug menu. + </member> <member name="debug/shapes/paths/geometry_color" type="Color" setter="" getter="" default="Color(0.1, 1, 0.7, 0.4)"> Color of the curve path geometry, visible when "Visible Paths" is enabled in the Debug menu. </member> @@ -1439,12 +1451,18 @@ <member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="1"> Default edge connection margin for 2D navigation maps. See [method NavigationServer2D.map_set_edge_connection_margin]. </member> + <member name="navigation/2d/default_link_connection_radius" type="int" setter="" getter="" default="4"> + Default link connection radius for 2D navigation maps. See [method NavigationServer2D.map_set_link_connection_radius]. + </member> <member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.25"> Default cell size for 3D navigation maps. See [method NavigationServer3D.map_set_cell_size]. </member> <member name="navigation/3d/default_edge_connection_margin" type="float" setter="" getter="" default="0.25"> Default edge connection margin for 3D navigation maps. See [method NavigationServer3D.map_set_edge_connection_margin]. </member> + <member name="navigation/3d/default_link_connection_radius" type="float" setter="" getter="" default="1.0"> + Default link connection radius for 3D navigation maps. See [method NavigationServer3D.map_set_link_connection_radius]. + </member> <member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768"> Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. </member> @@ -1807,6 +1825,55 @@ <member name="rendering/lightmapping/probe_capture/update_speed" type="float" setter="" getter="" default="15"> The framerate-independent update speed when representing dynamic object lighting from [LightmapProbe]s. Higher values make dynamic object lighting update faster. Higher values can prevent fast-moving objects from having "outdated" indirect lighting displayed on them, at the cost of possible flickering when an object moves from a bright area to a shaded area. </member> + <member name="rendering/lights_and_shadows/directional_shadow/16_bits" type="bool" setter="" getter="" default="true"> + Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/size" type="int" setter="" getter="" default="4096"> + The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value will be rounded up to the nearest power of 2. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048"> + Lower-end override for [member rendering/lights_and_shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> + Quality setting for shadows cast by [DirectionalLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. + [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. + [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply [i]constant[/i] shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> + Lower-end override for [member rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_16_bits" type="bool" setter="" getter="" default="true"> + Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv" type="int" setter="" getter="" default="2"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv" type="int" setter="" getter="" default="2"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv" type="int" setter="" getter="" default="3"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv" type="int" setter="" getter="" default="4"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_size" type="int" setter="" getter="" default="4096"> + Size for shadow atlas (used for OmniLights and SpotLights). See documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_size.mobile" type="int" setter="" getter="" default="2048"> + Lower-end override for [member rendering/lights_and_shadows/positional_shadow/atlas_size] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> + Quality setting for shadows cast by [OmniLight3D]s and [SpotLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. + [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. + [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> + Lower-end override for [member rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/use_physical_light_units" type="bool" setter="" getter="" default="false"> + Enables the use of physically based units for light sources. Physically based units tend to be much larger than the arbitrary units used by Godot, but they can be used to match lighting within Godot to real-world lighting. Due to the large dynamic range of lighting conditions present in nature, Godot bakes exposure into the various lighting quantities before rendering. Most light sources bake exposure automatically at run time based on the active [CameraAttributes] resource, but [LightmapGI] and [VoxelGI] require a [CameraAttributes] resource to be set at bake time to reduce the dynamic range. At run time, Godot will automatically reconcile the baked exposure with the active exposure to ensure lighting remains consistent. + </member> <member name="rendering/limits/cluster_builder/max_clustered_elements" type="float" setter="" getter="" default="512"> </member> <member name="rendering/limits/forward_renderer/threaded_render_minimum_instances" type="int" setter="" getter="" default="500"> @@ -1901,50 +1968,6 @@ <member name="rendering/shading/overrides/force_vertex_shading.mobile" type="bool" setter="" getter="" default="true"> Lower-end override for [member rendering/shading/overrides/force_vertex_shading] on mobile devices, due to performance concerns or driver support. </member> - <member name="rendering/shadows/directional_shadow/16_bits" type="bool" setter="" getter="" default="true"> - </member> - <member name="rendering/shadows/directional_shadow/size" type="int" setter="" getter="" default="4096"> - The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value will be rounded up to the nearest power of 2. - </member> - <member name="rendering/shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048"> - Lower-end override for [member rendering/shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/directional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> - Quality setting for shadows cast by [DirectionalLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. - [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. - [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply [i]constant[/i] shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. - </member> - <member name="rendering/shadows/directional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> - Lower-end override for [member rendering/shadows/directional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/positional_shadow/atlas_16_bits" type="bool" setter="" getter="" default="true"> - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv" type="int" setter="" getter="" default="2"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv" type="int" setter="" getter="" default="2"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv" type="int" setter="" getter="" default="3"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv" type="int" setter="" getter="" default="4"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_size" type="int" setter="" getter="" default="4096"> - Size for shadow atlas (used for OmniLights and SpotLights). See documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_size.mobile" type="int" setter="" getter="" default="2048"> - Lower-end override for [member rendering/shadows/positional_shadow/atlas_size] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/positional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> - Quality setting for shadows cast by [OmniLight3D]s and [SpotLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. - [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. - [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. - </member> - <member name="rendering/shadows/positional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> - Lower-end override for [member rendering/shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. - </member> <member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3"> The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level]. </member> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 3a61e16454..cdfea7de91 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -26,29 +26,27 @@ <description> </description> </method> - <method name="camera_create"> + <method name="camera_attributes_create"> <return type="RID" /> <description> - Creates a camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions. + Creates a camera attributes object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_attributes_[/code] RenderingServer functions. Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. </description> </method> - <method name="camera_effects_create"> - <return type="RID" /> - <description> - </description> - </method> - <method name="camera_effects_set_custom_exposure"> + <method name="camera_attributes_set_auto_exposure"> <return type="void" /> - <param index="0" name="camera_effects" type="RID" /> + <param index="0" name="camera_attributes" type="RID" /> <param index="1" name="enable" type="bool" /> - <param index="2" name="exposure" type="float" /> + <param index="2" name="min_sensitivity" type="float" /> + <param index="3" name="max_sensitivity" type="float" /> + <param index="4" name="speed" type="float" /> + <param index="5" name="scale" type="float" /> <description> </description> </method> - <method name="camera_effects_set_dof_blur"> + <method name="camera_attributes_set_dof_blur"> <return type="void" /> - <param index="0" name="camera_effects" type="RID" /> + <param index="0" name="camera_attributes" type="RID" /> <param index="1" name="far_enable" type="bool" /> <param index="2" name="far_distance" type="float" /> <param index="3" name="far_transition" type="float" /> @@ -59,20 +57,46 @@ <description> </description> </method> - <method name="camera_effects_set_dof_blur_bokeh_shape"> + <method name="camera_attributes_set_dof_blur_bokeh_shape"> <return type="void" /> <param index="0" name="shape" type="int" enum="RenderingServer.DOFBokehShape" /> <description> </description> </method> - <method name="camera_effects_set_dof_blur_quality"> + <method name="camera_attributes_set_dof_blur_quality"> <return type="void" /> <param index="0" name="quality" type="int" enum="RenderingServer.DOFBlurQuality" /> <param index="1" name="use_jitter" type="bool" /> <description> </description> </method> - <method name="camera_set_camera_effects"> + <method name="camera_attributes_set_exposure"> + <return type="void" /> + <param index="0" name="camera_attributes" type="RID" /> + <param index="1" name="multiplier" type="float" /> + <param index="2" name="normalization" type="float" /> + <description> + Sets the exposure values that will be used by the renderers. The normalization amount is used to bake a given Exposure Value (EV) into rendering calculations to reduce the dynamic range of the scene. + The normalization factor can be calculated from exposure value (EV100) as follows: + [codeblock] + func get_exposure_normalization(float ev100): + return 1.0 / (pow(2.0, ev100) * 1.2) + [/codeblock] + The exposure value can be calculated from aperture (in f-stops), shutter speed (in seconds), and sensitivity (in ISO) as follows: + [codeblock] + func get_exposure(float aperture, float shutter_speed, float sensitivity): + return log2((aperture * aperture) / shutterSpeed * (100.0 / sensitivity)) + [/codeblock] + </description> + </method> + <method name="camera_create"> + <return type="RID" /> + <description> + Creates a camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions. + Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. + </description> + </method> + <method name="camera_set_camera_attributes"> <return type="void" /> <param index="0" name="camera" type="RID" /> <param index="1" name="effects" type="RID" /> @@ -976,7 +1000,8 @@ <method name="environment_set_bg_energy"> <return type="void" /> <param index="0" name="env" type="RID" /> - <param index="1" name="energy" type="float" /> + <param index="1" name="multiplier" type="float" /> + <param index="2" name="exposure_value" type="float" /> <description> Sets the intensity of the background color. </description> @@ -1000,6 +1025,7 @@ <param index="6" name="height" type="float" /> <param index="7" name="height_density" type="float" /> <param index="8" name="aerial_perspective" type="float" /> + <param index="9" name="sky_affect" type="float" /> <description> </description> </method> @@ -1143,11 +1169,6 @@ <param index="1" name="tone_mapper" type="int" enum="RenderingServer.EnvironmentToneMapper" /> <param index="2" name="exposure" type="float" /> <param index="3" name="white" type="float" /> - <param index="4" name="auto_exposure" type="bool" /> - <param index="5" name="min_luminance" type="float" /> - <param index="6" name="max_luminance" type="float" /> - <param index="7" name="auto_exp_speed" type="float" /> - <param index="8" name="auto_exp_grey" type="float" /> <description> Sets the variables to be used with the "tonemap" post-process effect. See [Environment] for more details. </description> @@ -1167,6 +1188,7 @@ <param index="10" name="temporal_reprojection" type="bool" /> <param index="11" name="temporal_reprojection_amount" type="float" /> <param index="12" name="ambient_inject" type="float" /> + <param index="13" name="sky_affect" type="float" /> <description> </description> </method> @@ -1774,6 +1796,14 @@ <description> </description> </method> + <method name="lightmap_set_baked_exposure_normalization"> + <return type="void" /> + <param index="0" name="lightmap" type="RID" /> + <param index="1" name="baked_exposure" type="float" /> + <description> + Used to inform the renderer what exposure normalization value was used while baking the lightmap. This value will be used and modulated at run time to ensure that the lightmap maintains a consistent level of exposure even if the scene-wide exposure normalization is changed at run time. For more information see [method camera_attributes_set_exposure]. + </description> + </method> <method name="lightmap_set_probe_bounds"> <return type="void" /> <param index="0" name="lightmap" type="RID" /> @@ -2672,7 +2702,7 @@ The scenario is the 3D world that all the visual instances exist in. </description> </method> - <method name="scenario_set_camera_effects"> + <method name="scenario_set_camera_attributes"> <return type="void" /> <param index="0" name="scenario" type="RID" /> <param index="1" name="effects" type="RID" /> @@ -3480,6 +3510,14 @@ <description> </description> </method> + <method name="voxel_gi_set_baked_exposure_normalization"> + <return type="void" /> + <param index="0" name="voxel_gi" type="RID" /> + <param index="1" name="baked_exposure" type="float" /> + <description> + Used to inform the renderer what exposure normalization value was used while baking the voxel gi. This value will be used and modulated at run time to ensure that the voxel gi maintains a consistent level of exposure even if the scene-wide exposure normalization is changed at run time. For more information see [method camera_attributes_set_exposure]. + </description> + </method> <method name="voxel_gi_set_bias"> <return type="void" /> <param index="0" name="voxel_gi" type="RID" /> @@ -3850,7 +3888,7 @@ </constant> <constant name="LIGHT_PARAM_TRANSMITTANCE_BIAS" value="19" enum="LightParam"> </constant> - <constant name="LIGHT_PARAM_MAX" value="20" enum="LightParam"> + <constant name="LIGHT_PARAM_MAX" value="21" enum="LightParam"> Represents the size of the [enum LightParam] enum. </constant> <constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode"> diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml index fb4b9466b5..f5646e9e97 100644 --- a/doc/classes/SplitContainer.xml +++ b/doc/classes/SplitContainer.xml @@ -27,6 +27,10 @@ <member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset" default="0"> The initial offset of the splitting between the two [Control]s, with [code]0[/code] being at the end of the first [Control]. </member> + <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false"> + If [code]true[/code], the [SplitContainer] will arrange its children vertically, rather than horizontally. + Can't be changed when using [HSplitContainer] and [VSplitContainer]. + </member> </members> <signals> <signal name="dragged"> @@ -47,4 +51,18 @@ The split dragger is never visible and its space collapsed. </constant> </constants> + <theme_items> + <theme_item name="autohide" data_type="constant" type="int" default="1"> + Boolean value. If 1 ([code]true[/code]), the grabber will hide automatically when it isn't under the cursor. If 0 ([code]false[/code]), it's always visible. + </theme_item> + <theme_item name="separation" data_type="constant" type="int" default="12"> + The space between sides of the container. + </theme_item> + <theme_item name="h_grabber" data_type="icon" type="Texture2D"> + The icon used for the grabber drawn in the middle area when [member vertical] is [code]false[/code]. + </theme_item> + <theme_item name="v_grabber" data_type="icon" type="Texture2D"> + The icon used for the grabber drawn in the middle area when [member vertical] is [code]true[/code]. + </theme_item> + </theme_items> </class> diff --git a/doc/classes/StreamPeerExtension.xml b/doc/classes/StreamPeerExtension.xml index 46783de275..ab4bcfd17c 100644 --- a/doc/classes/StreamPeerExtension.xml +++ b/doc/classes/StreamPeerExtension.xml @@ -13,7 +13,7 @@ </description> </method> <method name="_get_data" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="r_buffer" type="uint8_t*" /> <param index="1" name="r_bytes" type="int" /> <param index="2" name="r_received" type="int32_t*" /> @@ -21,7 +21,7 @@ </description> </method> <method name="_get_partial_data" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="r_buffer" type="uint8_t*" /> <param index="1" name="r_bytes" type="int" /> <param index="2" name="r_received" type="int32_t*" /> @@ -29,7 +29,7 @@ </description> </method> <method name="_put_data" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_data" type="const uint8_t*" /> <param index="1" name="p_bytes" type="int" /> <param index="2" name="r_sent" type="int32_t*" /> @@ -37,7 +37,7 @@ </description> </method> <method name="_put_partial_data" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_data" type="const uint8_t*" /> <param index="1" name="p_bytes" type="int" /> <param index="2" name="r_sent" type="int32_t*" /> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index aad83211f5..1aec6d2c0e 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1624,6 +1624,10 @@ Break the line between any unconnected graphemes. </constant> <constant name="BREAK_ADAPTIVE" value="8" enum="LineBreakFlag" is_bitfield="true"> + Should be used only in conjunction with [constant BREAK_WORD_BOUND], break the line between any unconnected graphemes, if it's impossible to break it between the words. + </constant> + <constant name="BREAK_TRIM_EDGE_SPACES" value="16" enum="LineBreakFlag" is_bitfield="true"> + Remove edge spaces from the broken line segments. </constant> <constant name="VC_CHARS_BEFORE_SHAPING" value="0" enum="VisibleCharactersBehavior"> Trims text before the shaping. e.g, increasing [member Label.visible_characters] or [member RichTextLabel.visible_characters] value is visually identical to typing the text. diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 2197947126..e1852340c0 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -212,6 +212,13 @@ Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. </description> </method> + <method name="is_zero_approx" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component. + This method is faster than using [method is_equal_approx] with one value as a zero vector. + </description> + </method> <method name="length" qualifiers="const"> <return type="float" /> <description> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 150d53845c..1ef84050cd 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -180,6 +180,13 @@ Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. </description> </method> + <method name="is_zero_approx" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component. + This method is faster than using [method is_equal_approx] with one value as a zero vector. + </description> + </method> <method name="length" qualifiers="const"> <return type="float" /> <description> diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml index b9f509cfe7..743e2c2fcc 100644 --- a/doc/classes/Vector4.xml +++ b/doc/classes/Vector4.xml @@ -141,6 +141,13 @@ Returns [code]true[/code] if the vector is normalized, i.e. its length is equal to 1. </description> </method> + <method name="is_zero_approx" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component. + This method is faster than using [method is_equal_approx] with one value as a zero vector. + </description> + </method> <method name="length" qualifiers="const"> <return type="float" /> <description> diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index ba4995a5fb..394611b78f 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -32,6 +32,9 @@ </method> </methods> <members> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The [CameraAttributes] resource that specifies exposure levels to bake at. Auto-exposure and non exposure properties will be ignored. Exposure settings should be used to reduce the dynamic range present when baking. If exposure is too high, the [VoxelGI] will have banding artifacts or may have over-exposure artifacts. + </member> <member name="data" type="VoxelGIData" setter="set_probe_data" getter="get_probe_data"> The [VoxelGIData] resource that holds the data for this [VoxelGI]. </member> diff --git a/doc/classes/World3D.xml b/doc/classes/World3D.xml index 56a662d062..f3c7136075 100644 --- a/doc/classes/World3D.xml +++ b/doc/classes/World3D.xml @@ -10,7 +10,8 @@ <link title="Ray-casting">$DOCS_URL/tutorials/physics/ray-casting.html</link> </tutorials> <members> - <member name="camera_effects" type="CameraEffects" setter="set_camera_effects" getter="get_camera_effects"> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The default [CameraAttributes] resource to use if none set on the [Camera3D]. </member> <member name="direct_space_state" type="PhysicsDirectSpaceState3D" setter="" getter="get_direct_space_state"> Direct access to the world's physics 3D space state. Used for querying current and potential collisions. diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index ed8f0b9a04..5255179bb8 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -15,8 +15,8 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <members> - <member name="camera_effects" type="CameraEffects" setter="set_camera_effects" getter="get_camera_effects"> - The [CameraEffects] resource used by this [WorldEnvironment], defining the default properties. This [CameraEffects] resource will be used by all [Camera3D]s that do not define their own [CameraEffects]. + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The default [CameraAttributes] resource to use if none set on the [Camera3D]. </member> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> The [Environment] resource used by this [WorldEnvironment], defining the default properties. diff --git a/drivers/gles3/environment/gi.cpp b/drivers/gles3/environment/gi.cpp index 84cdb81d35..5b16d3539f 100644 --- a/drivers/gles3/environment/gi.cpp +++ b/drivers/gles3/environment/gi.cpp @@ -98,6 +98,13 @@ float GI::voxel_gi_get_energy(RID p_voxel_gi) const { return 0.0; } +void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) { +} + +float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const { + return 1.0; +} + void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) { } diff --git a/drivers/gles3/environment/gi.h b/drivers/gles3/environment/gi.h index 7a0634f22b..5b0aad380e 100644 --- a/drivers/gles3/environment/gi.h +++ b/drivers/gles3/environment/gi.h @@ -74,6 +74,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override; virtual float voxel_gi_get_energy(RID p_voxel_gi) const override; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override; virtual float voxel_gi_get_bias(RID p_voxel_gi) const override; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 12f4c7e4e9..5d111b5395 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -483,6 +483,13 @@ void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { _invalidate_sky(sky); } +float RasterizerSceneGLES3::sky_get_baked_exposure(RID p_sky) const { + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND_V(!sky, 1.0); + + return sky->baked_exposure; +} + void RasterizerSceneGLES3::_invalidate_sky(Sky *p_sky) { if (!p_sky->dirty) { p_sky->dirty = true; @@ -561,13 +568,13 @@ void RasterizerSceneGLES3::_update_dirty_skys() { dirty_sky_list = nullptr; } -void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { +void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - ERR_FAIL_COND(p_env.is_null()); + ERR_FAIL_COND(p_render_data->environment.is_null()); GLES3::SkyMaterialData *material = nullptr; - Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_render_data->environment)); RID sky_material; @@ -639,6 +646,14 @@ void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const Pag float sign = light_storage->light_is_negative(base) ? -1 : 1; sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + if (is_using_physical_light_units()) { + sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_render_data->camera_attributes.is_valid()) { + sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + Color linear_col = light_storage->light_get_color(base); sky_light_data.color[0] = linear_col.r; sky_light_data.color[1] = linear_col.g; @@ -708,7 +723,7 @@ void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const Pag } } -void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -768,12 +783,13 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); glBindVertexArray(sky_globals.screen_triangle_array); glDrawArrays(GL_TRIANGLES, 0, 3); } -void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -866,6 +882,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glBindVertexArray(sky_globals.screen_triangle_array); @@ -887,7 +904,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p _filter_sky_radiance(sky, 0); //Just copy over the first mipmap } sky->processing_layer = 1; - + sky->baked_exposure = p_luminance_multiplier; sky->reflection_dirty = false; } else { if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { @@ -1061,25 +1078,6 @@ Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bak return Ref<Image>(); } -RID RasterizerSceneGLES3::camera_effects_allocate() { - return RID(); -} - -void RasterizerSceneGLES3::camera_effects_initialize(RID p_rid) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { -} - -void RasterizerSceneGLES3::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) { -} - -void RasterizerSceneGLES3::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { -} - void RasterizerSceneGLES3::positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } @@ -1403,8 +1401,9 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -1413,9 +1412,9 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -1459,6 +1458,25 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da } else { } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + if (scene_state.ubo_buffer == 0) { glGenBuffers(1, &scene_state.ubo_buffer); } @@ -1510,7 +1528,17 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b float sign = light_storage->light_is_negative(base) ? -1 : 1; - light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + if (is_using_physical_light_units()) { + light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } else { + light_data.energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); light_data.color[0] = linear_col.r; @@ -1590,7 +1618,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b for (uint32_t i = 0; i < (r_omni_light_count + r_spot_light_count); i++) { uint32_t index = (i < r_omni_light_count) ? i : i - (r_omni_light_count); LightData &light_data = (i < r_omni_light_count) ? scene_state.omni_lights[index] : scene_state.spot_lights[index]; - //RS::LightType type = (i < omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; + RS::LightType type = (i < r_omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; RID base = li->light; @@ -1634,7 +1662,26 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b } } - float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * fade; + + if (is_using_physical_light_units()) { + energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (type == RS::LIGHT_OMNI) { + energy *= 1.0 / (Math_PI * 4.0); + } else { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + energy *= 1.0 / Math_PI; + } + } else { + energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } light_data.color[0] = linear_col.r * energy; light_data.color[1] = linear_col.g * energy; @@ -1671,23 +1718,23 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b glBindBuffer(GL_UNIFORM_BUFFER, 0); } -void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); - RenderBuffers *rb = nullptr; + Ref<RenderSceneBuffersGLES3> rb; if (p_render_buffers.is_valid()) { - rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); + rb = p_render_buffers; + ERR_FAIL_COND(rb.is_null()); } // Assign render data // Use the format from rendererRD RenderDataGLES3 render_data; { - render_data.render_buffers = p_render_buffers; - render_data.transparent_bg = rb->is_transparent; + render_data.render_buffers = rb; + render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); @@ -1707,7 +1754,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * render_data.lights = &p_lights; render_data.reflection_probes = &p_reflection_probes; render_data.environment = p_environment; - render_data.camera_effects = p_camera_effects; + render_data.camera_attributes = p_camera_attributes; render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe_pass = p_reflection_probe_pass; @@ -1768,6 +1815,8 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW); + scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization. + _setup_lights(&render_data, false, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count); _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, !render_data.reflection_probe.is_valid(), clear_color, false); @@ -1778,17 +1827,24 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * bool draw_sky = false; bool draw_sky_fog_only = false; bool keep_color = false; + float sky_energy_multiplier = 1.0; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (render_data.environment.is_valid()) { RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment); - float bg_energy = environment_get_bg_energy(render_data.environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(render_data.environment); + bg_energy_multiplier *= environment_get_bg_intensity(render_data.environment); + + if (render_data.camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(render_data.camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); @@ -1796,9 +1852,9 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(render_data.environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); @@ -1828,11 +1884,13 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * projection = correction * render_data.cam_projection; } - _setup_sky(render_data.environment, p_render_buffers, *render_data.lights, projection, render_data.cam_transform, screen_size); + sky_energy_multiplier *= bg_energy_multiplier; + + _setup_sky(&render_data, *render_data.lights, projection, render_data.cam_transform, screen_size); if (environment_get_sky(render_data.environment).is_valid()) { if (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY || (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_BG && environment_get_background(render_data.environment) == RS::ENV_BG_SKY)) { - _update_sky_radiance(render_data.environment, projection, render_data.cam_transform); + _update_sky_radiance(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier); } } else { // do not try to draw sky if invalid @@ -1936,7 +1994,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; - _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform); + _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -1947,8 +2005,8 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * _render_list_template<PASS_MODE_COLOR_TRANSPARENT>(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); - if (p_render_buffers.is_valid()) { - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); + if (rb.is_valid()) { + _render_buffers_debug_draw(rb, p_shadow_atlas, p_occluder_debug_tex); } glDisable(GL_BLEND); texture_storage->render_target_disable_clear_request(rb->render_target); @@ -2270,74 +2328,10 @@ void RasterizerSceneGLES3::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_dra debug_draw = p_debug_draw; } -RID RasterizerSceneGLES3::render_buffers_create() { - RenderBuffers rb; - return render_buffers_owner.make_rid(rb); -} - -void RasterizerSceneGLES3::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - - //rb->internal_width = p_internal_width; // ignore for now - //rb->internal_height = p_internal_height; - rb->width = p_width; - rb->height = p_height; - //rb->fsr_sharpness = p_fsr_sharpness; - rb->render_target = p_render_target; - //rb->msaa = p_msaa; - //rb->screen_space_aa = p_screen_space_aa; - //rb->use_debanding = p_use_debanding; - //rb->view_count = p_view_count; - - _free_render_buffer_data(rb); - - GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); - - rb->is_transparent = rt->is_transparent; - - // framebuffer - glGenFramebuffers(1, &rb->framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer); - - glBindTexture(GL_TEXTURE_2D, rt->color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); - - glGenTextures(1, &rb->depth_texture); - glBindTexture(GL_TEXTURE_2D, rb->depth_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rb->depth_texture, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - _free_render_buffer_data(rb); - WARN_PRINT("Could not create 3D renderbuffer, status: " + texture_storage->get_framebuffer_error(status)); - return; - } -} - -void RasterizerSceneGLES3::_free_render_buffer_data(RenderBuffers *rb) { - if (rb->depth_texture) { - glDeleteTextures(1, &rb->depth_texture); - rb->depth_texture = 0; - } - if (rb->framebuffer) { - glDeleteFramebuffers(1, &rb->framebuffer); - rb->framebuffer = 0; - } +Ref<RenderSceneBuffers> RasterizerSceneGLES3::render_buffers_create() { + Ref<RenderSceneBuffersGLES3> rb; + rb.instantiate(); + return rb; } //clear render buffers @@ -2365,7 +2359,7 @@ void RasterizerSceneGLES3::_free_render_buffer_data(RenderBuffers *rb) { } */ -void RasterizerSceneGLES3::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { +void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { } void RasterizerSceneGLES3::gi_set_use_half_resolution(bool p_enable) { @@ -2396,16 +2390,13 @@ bool RasterizerSceneGLES3::free(RID p_rid) { ERR_FAIL_COND_V(!sky, false); _free_sky_data(sky); sky_owner.free(p_rid); - } else if (render_buffers_owner.owns(p_rid)) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_rid); - ERR_FAIL_COND_V(!rb, false); - _free_render_buffer_data(rb); - render_buffers_owner.free(p_rid); - } else if (light_instance_owner.owns(p_rid)) { LightInstance *light_instance = light_instance_owner.get_or_null(p_rid); ERR_FAIL_COND_V(!light_instance, false); light_instance_owner.free(p_rid); + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + //not much to delete, just free it + RSG::camera_attributes->camera_attributes_free(p_rid); } else { return false; } @@ -2431,6 +2422,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); + // Quality settings. + use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"); + { // Setup Lights diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index a54d87a3a3..820f3ff9c2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -45,6 +45,7 @@ #include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/sky.glsl.gen.h" #include "storage/material_storage.h" +#include "storage/render_scene_buffers_gles3.h" #include "storage/utilities.h" enum RenderListType { @@ -91,7 +92,7 @@ enum { }; struct RenderDataGLES3 { - RID render_buffers = RID(); + Ref<RenderSceneBuffersGLES3> render_buffers; bool transparent_bg = false; Transform3D cam_transform = Transform3D(); @@ -111,7 +112,7 @@ struct RenderDataGLES3 { const PagedArray<RID> *lights = nullptr; const PagedArray<RID> *reflection_probes = nullptr; RID environment = RID(); - RID camera_effects = RID(); + RID camera_attributes = RID(); RID reflection_probe = RID(); int reflection_probe_pass = 0; @@ -344,7 +345,7 @@ private: float ambient_color_sky_mix; uint32_t material_uv2_mode; - float pad2; + float emissive_exposure_normalization; uint32_t use_ambient_light = 0; uint32_t use_ambient_cubemap = 0; @@ -357,7 +358,7 @@ private: uint32_t directional_light_count; float z_far; float z_near; - float pad1; + float IBL_exposure_normalization; uint32_t fog_enabled; float fog_density; @@ -490,52 +491,21 @@ protected: double time; double time_step = 0; - struct RenderBuffers { - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; - //float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; - //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; - //bool use_debanding = false; - //uint32_t view_count = 1; - - bool is_transparent = false; - - RID render_target; - GLuint internal_texture = 0; // Used for rendering when post effects are enabled - GLuint depth_texture = 0; // Main depth texture - GLuint framebuffer = 0; // Main framebuffer, contains internal_texture and depth_texture or render_target->color and depth_texture - - //built-in textures used for ping pong image processing and blurring - struct Blur { - RID texture; - - struct Mipmap { - RID texture; - int width; - int height; - GLuint fbo; - }; - - Vector<Mipmap> mipmaps; - }; - - Blur blur[2]; //the second one starts from the first mipmap - }; - bool screen_space_roughness_limiter = false; float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - mutable RID_Owner<RenderBuffers, true> render_buffers_owner; + void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); + + /* Camera Attributes */ - void _free_render_buffer_data(RenderBuffers *rb); - void _allocate_blur_textures(RenderBuffers *rb); - void _allocate_depth_backbuffer_textures(RenderBuffers *rb); + struct CameraAttributes { + float exposure_multiplier = 1.0; + float exposure_normalization = 1.0; + }; - void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); + bool use_physical_light_units = false; + mutable RID_Owner<CameraAttributes, true> camera_attributes_owner; /* Environment */ @@ -605,6 +575,7 @@ protected: bool dirty = false; int processing_layer = 0; Sky *dirty_list = nullptr; + float baked_exposure = 1.0; //State to track when radiance cubemap needs updating GLES3::SkyMaterialData *prev_material; @@ -615,12 +586,12 @@ protected: Sky *dirty_sky_list = nullptr; mutable RID_Owner<Sky, true> sky_owner; - void _setup_sky(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); + void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); - void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _filter_sky_radiance(Sky *p_sky, int p_base_layer); - void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _free_sky_data(Sky *p_sky); public: @@ -646,14 +617,14 @@ public: /* SDFGI UPDATE */ - void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(RID p_render_buffers) const override { + void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { + AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { + uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return 0; } @@ -665,6 +636,7 @@ public: void sky_set_mode(RID p_sky, RS::SkyMode p_mode) override; void sky_set_material(RID p_sky, RID p_material) override; Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override; + float sky_get_baked_exposure(RID p_sky) const; /* ENVIRONMENT API */ @@ -686,13 +658,9 @@ public: Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; - RID camera_effects_allocate() override; - void camera_effects_initialize(RID p_rid) override; - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; - - 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; - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; + _FORCE_INLINE_ bool is_using_physical_light_units() { + return use_physical_light_units; + } void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; @@ -743,7 +711,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override; - void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override; @@ -761,8 +729,7 @@ public: return debug_draw; } - RID render_buffers_create() override; - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + Ref<RenderSceneBuffers> render_buffers_create() override; void gi_set_use_half_resolution(bool p_enable) override; void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override; diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index eb1befe38e..21f01d2a8f 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -92,6 +92,7 @@ uniform mat4 orientation; uniform vec4 projection; uniform vec3 position; uniform float time; +uniform float luminance_multiplier; uniform float fog_aerial_perspective; uniform vec3 fog_light_color; @@ -149,6 +150,8 @@ void main() { } + color *= luminance_multiplier; + // Convert to Linear for tonemapping so color matches scene shader better color = srgb_to_linear(color); color *= exposure; diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 683716ca99..6411590aee 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -422,13 +422,17 @@ float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { /* LIGHTMAP CAPTURE */ RID LightStorage::lightmap_allocate() { - return RID(); + return lightmap_owner.allocate_rid(); } void LightStorage::lightmap_initialize(RID p_rid) { + lightmap_owner.initialize_rid(p_rid, Lightmap()); } void LightStorage::lightmap_free(RID p_rid) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + lightmap->dependency.deleted_notify(p_rid); + lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { @@ -443,6 +447,9 @@ void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { } +void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { return PackedVector3Array(); } diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index fa012a7b58..f054f0fdc6 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -92,6 +92,7 @@ struct ReflectionProbe { bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; + float baked_exposure = 1.0; Dependency dependency; }; @@ -103,6 +104,7 @@ struct Lightmap { bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + float baked_exposure = 1.0; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -297,6 +299,9 @@ public: /* LIGHTMAP CAPTURE */ + Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }; + bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }; + virtual RID lightmap_allocate() override; virtual void lightmap_initialize(RID p_rid) override; virtual void lightmap_free(RID p_rid) override; @@ -305,6 +310,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp new file mode 100644 index 0000000000..5d121e2ef9 --- /dev/null +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* render_scene_buffers_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "render_scene_buffers_gles3.h" +#include "texture_storage.h" + +RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { + free_render_buffer_data(); +} + +void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + //internal_size.x = p_internal_size.x; // ignore for now + //internal_size.y = p_internal_size.y; + width = p_target_size.x; + height = p_target_size.y; + //fsr_sharpness = p_fsr_sharpness; + //texture_mipmap_bias = p_texture_mipmap_bias; + render_target = p_render_target; + //msaa = p_msaa; + //screen_space_aa = p_screen_space_aa; + //use_debanding = p_use_debanding; + //view_count = p_view_count; + + free_render_buffer_data(); + + GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); + + is_transparent = rt->is_transparent; + + // framebuffer + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + glBindTexture(GL_TEXTURE_2D, rt->color); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + + glGenTextures(1, &depth_texture); + glBindTexture(GL_TEXTURE_2D, depth_texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + free_render_buffer_data(); + WARN_PRINT("Could not create 3D renderbuffer, status: " + texture_storage->get_framebuffer_error(status)); + return; + } +} + +void RenderSceneBuffersGLES3::free_render_buffer_data() { + if (depth_texture) { + glDeleteTextures(1, &depth_texture); + depth_texture = 0; + } + if (framebuffer) { + glDeleteFramebuffers(1, &framebuffer); + framebuffer = 0; + } +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h new file mode 100644 index 0000000000..ad0d2032b0 --- /dev/null +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* render_scene_buffers_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_SCENE_BUFFERS_GLES3_H +#define RENDER_SCENE_BUFFERS_GLES3_H + +#ifdef GLES3_ENABLED + +#include "servers/rendering/storage/render_scene_buffers.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +class RenderSceneBuffersGLES3 : public RenderSceneBuffers { + GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); + +public: + // Original implementation, need to investigate which ones we'll keep like this and what we'll change... + + int internal_width = 0; + int internal_height = 0; + int width = 0; + int height = 0; + //float fsr_sharpness = 0.2f; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + //bool use_debanding = false; + //uint32_t view_count = 1; + + bool is_transparent = false; + + RID render_target; + GLuint internal_texture = 0; // Used for rendering when post effects are enabled + GLuint depth_texture = 0; // Main depth texture + GLuint framebuffer = 0; // Main framebuffer, contains internal_texture and depth_texture or render_target->color and depth_texture + + //built-in textures used for ping pong image processing and blurring + struct Blur { + RID texture; + + struct Mipmap { + RID texture; + int width; + int height; + GLuint fbo; + }; + + Vector<Mipmap> mipmaps; + }; + + Blur blur[2]; //the second one starts from the first mipmap + +private: +public: + virtual ~RenderSceneBuffersGLES3(); + virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + + virtual void set_fsr_sharpness(float p_fsr_sharpness) override{}; + virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{}; + virtual void set_use_debanding(bool p_use_debanding) override{}; + + void free_render_buffer_data(); +}; + +#endif // GLES3_ENABLED + +#endif // RENDER_SCENE_BUFFERS_GLES3_H diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 654104722b..16bacf1829 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -82,6 +82,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const { return RS::INSTANCE_MULTIMESH; } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { return RS::INSTANCE_LIGHT; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + return RS::INSTANCE_LIGHTMAP; } return RS::INSTANCE_NONE; } @@ -114,6 +116,9 @@ bool Utilities::free(RID p_rid) { } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { GLES3::LightStorage::get_singleton()->light_free(p_rid); return true; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + GLES3::LightStorage::get_singleton()->lightmap_free(p_rid); + return true; } else { return false; } diff --git a/editor/editor_atlas_packer.cpp b/editor/editor_atlas_packer.cpp index 9c6bcd769a..7f4bd8cc89 100644 --- a/editor/editor_atlas_packer.cpp +++ b/editor/editor_atlas_packer.cpp @@ -81,7 +81,7 @@ void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_h int l = k == 0 ? 2 : k - 1; Vector<Point2i> points = Geometry2D::bresenham_line(v[k], v[l]); for (Point2i point : points) { - src_bitmap->set_bit(point, true); + src_bitmap->set_bitv(point, true); } } } @@ -128,7 +128,7 @@ void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_h continue; } - if (src_bitmap->get_bit(Vector2(px, py))) { + if (src_bitmap->get_bit(px, py)) { found_pixel = true; } } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 6aa0bd3f99..bdf1c314f8 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3949,16 +3949,22 @@ void EditorInspector::_add_meta_confirm() { undo_redo->commit_action(); } -void EditorInspector::_check_meta_name(String name) { +void EditorInspector::_check_meta_name(const String &p_name) { String error; - if (name == "") { - error = TTR("Metadata can't be empty."); - } else if (!name.is_valid_identifier()) { - error = TTR("Invalid metadata identifier."); - } else if (object->has_meta(name)) { - error = TTR("Metadata already exists."); - } else if (name[0] == '_') { + if (p_name == "") { + error = TTR("Metadata name can't be empty."); + } else if (!p_name.is_valid_identifier()) { + error = TTR("Metadata name must be a valid identifier."); + } else if (object->has_meta(p_name)) { + Node *node = Object::cast_to<Node>(object); + if (node) { + error = vformat(TTR("Metadata with name \"%s\" already exists on \"%s\"."), p_name, node->get_name()); + } else { + // This should normally never be reached, but the error is set just in case. + error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name, node->get_name()); + } + } else if (p_name[0] == '_') { error = TTR("Names starting with _ are reserved for editor-only metadata."); } @@ -3976,7 +3982,15 @@ void EditorInspector::_check_meta_name(String name) { void EditorInspector::_show_add_meta_dialog() { if (!add_meta_dialog) { add_meta_dialog = memnew(ConfirmationDialog); - add_meta_dialog->set_title(TTR("Add Metadata Property")); + + Node *node = Object::cast_to<Node>(object); + if (node) { + add_meta_dialog->set_title(vformat(TTR("Add Metadata Property for \"%s\""), node->get_name())); + } else { + // This should normally never be reached, but the title is set just in case. + add_meta_dialog->set_title(vformat(TTR("Add Metadata Property"), node->get_name())); + } + VBoxContainer *vbc = memnew(VBoxContainer); add_meta_dialog->add_child(vbc); HBoxContainer *hbc = memnew(HBoxContainer); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 474078853a..d634eae23f 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -538,7 +538,7 @@ class EditorInspector : public ScrollContainer { void _add_meta_confirm(); void _show_add_meta_dialog(); - void _check_meta_name(String name); + void _check_meta_name(const String &p_name); protected: static void _bind_methods(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 251f2892aa..58377eef0f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -170,6 +170,7 @@ #include "editor/plugins/mesh_instance_3d_editor_plugin.h" #include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/multimesh_editor_plugin.h" +#include "editor/plugins/navigation_link_2d_editor_plugin.h" #include "editor/plugins/navigation_polygon_editor_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/plugins/occluder_instance_3d_editor_plugin.h" @@ -366,7 +367,7 @@ void EditorNode::_update_scene_tabs() { scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), i); + DisplayServer::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), callable_mp(this, &EditorNode::_global_menu_scene), Callable(), i); } if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { @@ -501,10 +502,10 @@ void EditorNode::_update_from_settings() { } RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape"))); - RS::get_singleton()->camera_effects_set_dof_blur_bokeh_shape(dof_shape); + RS::get_singleton()->camera_attributes_set_dof_blur_bokeh_shape(dof_shape); RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))); bool dof_jitter = GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter"); - RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); + RS::get_singleton()->camera_attributes_set_dof_blur_quality(dof_quality, dof_jitter); RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to")); RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/limit")); bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0; @@ -520,13 +521,13 @@ void EditorNode::_update_from_settings() { float sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale"); RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale); - uint32_t directional_shadow_size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); - uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); + uint32_t directional_shadow_size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size"); + uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits"); RS::get_singleton()->directional_shadow_atlas_set_size(directional_shadow_size, directional_shadow_16_bits); - RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/positional_shadow/soft_shadow_filter_quality"))); + RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality"))); RS::get_singleton()->positional_soft_shadow_filter_set_quality(shadows_quality); - RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_filter_quality"))); + RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality"))); RS::get_singleton()->directional_soft_shadow_filter_set_quality(directional_shadow_quality); float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); @@ -4110,6 +4111,7 @@ void EditorNode::register_editor_types() { GDREGISTER_CLASS(EditorSyntaxHighlighter); GDREGISTER_ABSTRACT_CLASS(EditorInterface); GDREGISTER_CLASS(EditorExportPlugin); + GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform); GDREGISTER_CLASS(EditorResourceConversionPlugin); GDREGISTER_CLASS(EditorSceneFormatImporter); GDREGISTER_CLASS(EditorScenePostImportPlugin); @@ -7348,6 +7350,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(GPUParticles2DEditorPlugin)); add_editor_plugin(memnew(LightOccluder2DEditorPlugin)); add_editor_plugin(memnew(Line2DEditorPlugin)); + add_editor_plugin(memnew(NavigationLink2DEditorPlugin)); add_editor_plugin(memnew(NavigationPolygonEditorPlugin)); add_editor_plugin(memnew(Path2DEditorPlugin)); add_editor_plugin(memnew(Polygon2DEditorPlugin)); @@ -7421,11 +7424,6 @@ EditorNode::EditorNode() { editor_plugins_force_over = memnew(EditorPluginList); editor_plugins_force_input_forwarding = memnew(EditorPluginList); - Ref<EditorExportTextSceneToBinaryPlugin> export_text_to_binary_plugin; - export_text_to_binary_plugin.instantiate(); - - EditorExport::get_singleton()->add_export_plugin(export_text_to_binary_plugin); - Ref<GDExtensionExportPlugin> gdextension_export_plugin; gdextension_export_plugin.instantiate(); diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 73c365ce4a..33632649c8 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -155,6 +155,10 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; + if (is_read_only()) { + return; + } + if (grabbing_grabber) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::WHEEL_UP) { @@ -196,7 +200,7 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; - if (k.is_valid() && k->is_pressed()) { + if (k.is_valid() && k->is_pressed() && !is_read_only()) { double step = get_step(); double real_step = step; if (step < 1) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index af0e40d1d4..d0bb63dde9 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1358,6 +1358,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("selection_color", "TextEdit", selection_color); theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE); + theme->set_icon("h_grabber", "SplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons"))); + theme->set_icon("v_grabber", "SplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons"))); theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons"))); theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons"))); diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index d291040bd2..29b6a5e546 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -351,6 +351,8 @@ EditorExport::EditorExport() { singleton = this; set_process(true); + + GLOBAL_DEF("editor/export/convert_text_resources_to_binary", true); } EditorExport::~EditorExport() { diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index e07b5e4cdb..525a962222 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -44,6 +44,7 @@ #include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" #include "editor_export_plugin.h" +#include "scene/resources/packed_scene.h" static int _get_pad(int p_alignment, int p_n) { int rest = p_n % p_alignment; @@ -488,6 +489,295 @@ EditorExportPlatform::ExportNotifier::~ExportNotifier() { } } +bool EditorExportPlatform::_export_customize_dictionary(Dictionary &dict, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) { + bool changed = false; + + List<Variant> keys; + dict.get_key_list(&keys); + for (const Variant &K : keys) { + Variant v = dict[K]; + switch (v.get_type()) { + case Variant::OBJECT: { + Ref<Resource> res = v; + if (res.is_valid()) { + for (uint32_t j = 0; j < customize_resources_plugins.size(); j++) { + Ref<Resource> new_res = customize_resources_plugins[j]->_customize_resource(res, ""); + if (new_res.is_valid()) { + changed = true; + if (new_res != res) { + dict[K] = new_res; + res = new_res; + } + break; + } + } + + // If it was not replaced, go through and see if there is something to replace. + if (res.is_valid() && !res->get_path().is_resource_file() && _export_customize_object(res.ptr(), customize_resources_plugins), true) { + changed = true; + } + } + + } break; + case Variant::DICTIONARY: { + Dictionary d = v; + if (_export_customize_dictionary(d, customize_resources_plugins)) { + changed = true; + } + } break; + case Variant::ARRAY: { + Array a = v; + if (_export_customize_array(a, customize_resources_plugins)) { + changed = true; + } + } break; + default: { + } + } + } + return changed; +} + +bool EditorExportPlatform::_export_customize_array(Array &arr, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) { + bool changed = false; + + for (int i = 0; i < arr.size(); i++) { + Variant v = arr.get(i); + switch (v.get_type()) { + case Variant::OBJECT: { + Ref<Resource> res = v; + if (res.is_valid()) { + for (uint32_t j = 0; j < customize_resources_plugins.size(); j++) { + Ref<Resource> new_res = customize_resources_plugins[j]->_customize_resource(res, ""); + if (new_res.is_valid()) { + changed = true; + if (new_res != res) { + arr.set(i, new_res); + res = new_res; + } + break; + } + } + + // If it was not replaced, go through and see if there is something to replace. + if (res.is_valid() && !res->get_path().is_resource_file() && _export_customize_object(res.ptr(), customize_resources_plugins), true) { + changed = true; + } + } + } break; + case Variant::DICTIONARY: { + Dictionary d = v; + if (_export_customize_dictionary(d, customize_resources_plugins)) { + changed = true; + } + } break; + case Variant::ARRAY: { + Array a = v; + if (_export_customize_array(a, customize_resources_plugins)) { + changed = true; + } + } break; + default: { + } + } + } + return changed; +} + +bool EditorExportPlatform::_export_customize_object(Object *p_object, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) { + bool changed = false; + + List<PropertyInfo> props; + p_object->get_property_list(&props); + for (const PropertyInfo &E : props) { + switch (E.type) { + case Variant::OBJECT: { + Ref<Resource> res = p_object->get(E.name); + if (res.is_valid()) { + for (uint32_t j = 0; j < customize_resources_plugins.size(); j++) { + Ref<Resource> new_res = customize_resources_plugins[j]->_customize_resource(res, ""); + if (new_res.is_valid()) { + changed = true; + if (new_res != res) { + p_object->set(E.name, new_res); + res = new_res; + } + break; + } + } + + // If it was not replaced, go through and see if there is something to replace. + if (res.is_valid() && !res->get_path().is_resource_file() && _export_customize_object(res.ptr(), customize_resources_plugins), true) { + changed = true; + } + } + + } break; + case Variant::DICTIONARY: { + Dictionary d = p_object->get(E.name); + if (_export_customize_dictionary(d, customize_resources_plugins)) { + // May have been generated, so set back just in case + p_object->set(E.name, d); + changed = true; + } + } break; + case Variant::ARRAY: { + Array a = p_object->get(E.name); + if (_export_customize_array(a, customize_resources_plugins)) { + // May have been generated, so set back just in case + p_object->set(E.name, a); + changed = true; + } + } break; + default: { + } + } + } + return changed; +} + +bool EditorExportPlatform::_export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) { + bool changed = false; + + if (p_node == p_root || p_node->get_owner() == p_root) { + if (_export_customize_object(p_node, customize_resources_plugins)) { + changed = true; + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + if (_export_customize_scene_resources(p_root, p_node->get_child(i), customize_resources_plugins)) { + changed = true; + } + } + + return changed; +} + +String EditorExportPlatform::_export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save) { + if (!p_force_save && customize_resources_plugins.is_empty() && customize_scenes_plugins.is_empty()) { + return p_path; // do none + } + + // Check if a cache exists + if (export_cache.has(p_path)) { + FileExportCache &fec = export_cache[p_path]; + + if (fec.saved_path.is_empty() || FileAccess::exists(fec.saved_path)) { + // Destination file exists (was not erased) or not needed + + uint64_t mod_time = FileAccess::get_modified_time(p_path); + if (fec.source_modified_time == mod_time) { + // Cached (modified time matches). + fec.used = true; + return fec.saved_path.is_empty() ? p_path : fec.saved_path; + } + + String md5 = FileAccess::get_md5(p_path); + if (FileAccess::exists(p_path + ".import")) { + // Also consider the import file in the string + md5 += FileAccess::get_md5(p_path + ".import"); + } + if (fec.source_md5 == md5) { + // Cached (md5 matches). + fec.source_modified_time = mod_time; + fec.used = true; + return fec.saved_path.is_empty() ? p_path : fec.saved_path; + } + } + } + + FileExportCache fec; + fec.used = true; + fec.source_modified_time = FileAccess::get_modified_time(p_path); + + String md5 = FileAccess::get_md5(p_path); + if (FileAccess::exists(p_path + ".import")) { + // Also consider the import file in the string + md5 += FileAccess::get_md5(p_path + ".import"); + } + + fec.source_md5 = md5; + + // Check if it should convert + + String type = ResourceLoader::get_resource_type(p_path); + + bool modified = false; + + String save_path; + + if (type == "PackedScene") { // Its a scene. + Ref<PackedScene> ps = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE); + ERR_FAIL_COND_V(ps.is_null(), p_path); + Node *node = ps->instantiate(); + ERR_FAIL_COND_V(node == nullptr, p_path); + if (customize_scenes_plugins.size()) { + for (uint32_t i = 0; i < customize_scenes_plugins.size(); i++) { + Node *customized = customize_scenes_plugins[i]->_customize_scene(node, p_path); + if (customized != nullptr) { + node = customized; + modified = true; + } + } + } + if (customize_resources_plugins.size()) { + if (_export_customize_scene_resources(node, node, customize_resources_plugins)) { + modified = true; + } + } + + if (modified || p_force_save) { + // If modified, save it again. This is also used for TSCN -> SCN conversion on export. + + String base_file = p_path.get_file().get_basename() + ".scn"; // use SCN for saving (binary) and repack (If conversting, TSCN PackedScene representation is inefficient, so repacking is also desired). + save_path = export_base_path.path_join("export-" + p_path.md5_text() + "-" + base_file); + + Ref<PackedScene> s; + s.instantiate(); + s->pack(node); + Error err = ResourceSaver::save(s, save_path); + ERR_FAIL_COND_V_MSG(err != OK, p_path, "Unable to save export scene file to: " + save_path); + } + } else { + Ref<Resource> res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE); + ERR_FAIL_COND_V(res.is_null(), p_path); + + if (customize_resources_plugins.size()) { + for (uint32_t i = 0; i < customize_resources_plugins.size(); i++) { + Ref<Resource> new_res = customize_resources_plugins[i]->_customize_resource(res, p_path); + if (new_res.is_valid()) { + modified = true; + if (new_res != res) { + res = new_res; + } + break; + } + } + + if (_export_customize_object(res.ptr(), customize_resources_plugins)) { + modified = true; + } + } + + if (modified || p_force_save) { + // If modified, save it again. This is also used for TRES -> RES conversion on export. + + String base_file = p_path.get_file().get_basename() + ".res"; // use RES for saving (binary) + save_path = export_base_path.path_join("export-" + p_path.md5_text() + "-" + base_file); + + Error err = ResourceSaver::save(res, save_path); + ERR_FAIL_COND_V_MSG(err != OK, p_path, "Unable to save export resource file to: " + save_path); + } + } + + fec.saved_path = save_path; + + export_cache[p_path] = fec; + + return save_path.is_empty() ? p_path : save_path; +} + Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { //figure out paths of files that will be exported HashSet<String> paths; @@ -601,6 +891,15 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Error err = OK; Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + struct SortByName { + bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const { + return left->_get_name() < right->_get_name(); + } + }; + + // Always sort by name, to so if for some reason theya are re-arranged, it still works. + export_plugins.sort_custom<SortByName>(); + for (int i = 0; i < export_plugins.size(); i++) { export_plugins.write[i]->set_export_preset(p_preset); @@ -623,6 +922,65 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } HashSet<String> features = get_features(p_preset, p_debug); + PackedStringArray features_psa; + for (const String &feature : features) { + features_psa.push_back(feature); + } + + // Check if custom processing is needed + uint32_t custom_resources_hash = HASH_MURMUR3_SEED; + uint32_t custom_scene_hash = HASH_MURMUR3_SEED; + + LocalVector<Ref<EditorExportPlugin>> customize_resources_plugins; + LocalVector<Ref<EditorExportPlugin>> customize_scenes_plugins; + + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) { + customize_resources_plugins.push_back(export_plugins[i]); + + custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); + uint64_t hash = export_plugins[i]->_get_customization_configuration_hash(); + custom_resources_hash = hash_murmur3_one_64(hash, custom_resources_hash); + } + if (export_plugins[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) { + customize_scenes_plugins.push_back(export_plugins[i]); + + custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); + uint64_t hash = export_plugins[i]->_get_customization_configuration_hash(); + custom_scene_hash = hash_murmur3_one_64(hash, custom_scene_hash); + } + } + + HashMap<String, FileExportCache> export_cache; + String export_base_path = ProjectSettings::get_singleton()->get_project_data_path().path_join("exported/") + itos(custom_resources_hash); + + bool convert_text_to_binary = GLOBAL_GET("editor/export/convert_text_resources_to_binary"); + + if (convert_text_to_binary || customize_resources_plugins.size() || customize_scenes_plugins.size()) { + // See if we have something to open + Ref<FileAccess> f = FileAccess::open(export_base_path.path_join("file_cache"), FileAccess::READ); + if (f.is_valid()) { + String l = f->get_line(); + while (l != String()) { + Vector<String> fields = l.split("::"); + if (fields.size() == 4) { + FileExportCache fec; + String path = fields[0]; + fec.source_md5 = fields[1].strip_edges(); + fec.source_modified_time = fields[2].strip_edges().to_int(); + fec.saved_path = fields[3]; + fec.used = false; // Assume unused until used. + export_cache[path] = fec; + } + l = f->get_line(); + } + } else { + // create the path + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + d->change_dir(ProjectSettings::get_singleton()->get_project_data_path()); + d->make_dir_recursive("exported/" + itos(custom_resources_hash)); + } + } //store everything in the export medium int idx = 0; @@ -633,85 +991,133 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String type = ResourceLoader::get_resource_type(path); if (FileAccess::exists(path + ".import")) { - //file is imported, replace by what it imports - Ref<ConfigFile> config; - config.instantiate(); - err = config->load(path + ".import"); - if (err != OK) { - ERR_PRINT("Could not parse: '" + path + "', not exported."); - continue; - } + // Before doing this, try to see if it can be customized - String importer_type = config->get_value("remap", "importer"); + String export_path = _export_customize(path, customize_resources_plugins, customize_scenes_plugins, export_cache, export_base_path, false); - if (importer_type == "keep") { - //just keep file as-is - Vector<uint8_t> array = FileAccess::get_file_as_array(path); - err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); + if (export_path != path) { + // It was actually customized.. + // Since the original file is likely not recognized, just use the import system + Ref<ConfigFile> config; + config.instantiate(); + err = config->load(path + ".import"); + if (err != OK) { + ERR_PRINT("Could not parse: '" + path + "', not exported."); + continue; + } + config->set_value("remap", "type", ResourceLoader::get_resource_type(export_path)); + + // Erase all PAths + List<String> keys; + config->get_section_keys("remap", &keys); + for (const String &K : keys) { + if (E.begins_with("path")) { + config->erase_section_key("remap", K); + } + } + // Set actual converted path. + config->set_value("remap", "path", export_path); + + // erase useless sections + config->erase_section("deps"); + config->erase_section("params"); + + String import_text = config->encode_to_text(); + CharString cs = import_text.utf8(); + Vector<uint8_t> sarr; + sarr.resize(cs.size()); + memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); + + err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } + // Now actual remapped file: + sarr = FileAccess::get_file_as_array(export_path); + err = p_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); + if (err != OK) { + return err; + } + } else { + // file is imported and not customized, replace by what it imports + Ref<ConfigFile> config; + config.instantiate(); + err = config->load(path + ".import"); + if (err != OK) { + ERR_PRINT("Could not parse: '" + path + "', not exported."); + continue; + } - continue; - } + String importer_type = config->get_value("remap", "importer"); - List<String> remaps; - config->get_section_keys("remap", &remaps); + if (importer_type == "keep") { + //just keep file as-is + Vector<uint8_t> array = FileAccess::get_file_as_array(path); + err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); - HashSet<String> remap_features; + if (err != OK) { + return err; + } - for (const String &F : remaps) { - String remap = F; - String feature = remap.get_slice(".", 1); - if (features.has(feature)) { - remap_features.insert(feature); + continue; } - } - if (remap_features.size() > 1) { - this->resolve_platform_feature_priorities(p_preset, remap_features); - } + List<String> remaps; + config->get_section_keys("remap", &remaps); - err = OK; + HashSet<String> remap_features; - for (const String &F : remaps) { - String remap = F; - if (remap == "path") { - String remapped_path = config->get_value("remap", remap); - Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); - err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); - } else if (remap.begins_with("path.")) { + for (const String &F : remaps) { + String remap = F; String feature = remap.get_slice(".", 1); + if (features.has(feature)) { + remap_features.insert(feature); + } + } - if (remap_features.has(feature)) { + if (remap_features.size() > 1) { + this->resolve_platform_feature_priorities(p_preset, remap_features); + } + + err = OK; + + for (const String &F : remaps) { + String remap = F; + if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + } else if (remap.begins_with("path.")) { + String feature = remap.get_slice(".", 1); + + if (remap_features.has(feature)) { + String remapped_path = config->get_value("remap", remap); + Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); + err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + } } } - } - if (err != OK) { - return err; - } + if (err != OK) { + return err; + } - //also save the .import file - Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import"); - err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key); + //also save the .import file + Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import"); + err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key); - if (err != OK) { - return err; + if (err != OK) { + return err; + } } } else { + // Customize + bool do_export = true; for (int i = 0; i < export_plugins.size(); i++) { if (export_plugins[i]->get_script_instance()) { //script based - PackedStringArray features_psa; - for (const String &feature : features) { - features_psa.push_back(feature); - } export_plugins.write[i]->_export_file_script(path, type, features_psa); } else { export_plugins.write[i]->_export_file(path, type, features); @@ -748,8 +1154,18 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } //just store it as it comes if (do_export) { - Vector<uint8_t> array = FileAccess::get_file_as_array(path); - err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); + // Customization only happens if plugins did not take care of it before + bool force_binary = convert_text_to_binary && (path.get_extension().to_lower() == "tres" || path.get_extension().to_lower() == "tscn"); + String export_path = _export_customize(path, customize_resources_plugins, customize_scenes_plugins, export_cache, export_base_path, force_binary); + + if (export_path != path) { + // Add a remap entry + path_remaps.push_back(path); + path_remaps.push_back(export_path); + } + + Vector<uint8_t> array = FileAccess::get_file_as_array(export_path); + err = p_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); if (err != OK) { return err; } @@ -759,6 +1175,31 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & idx++; } + if (convert_text_to_binary || customize_resources_plugins.size() || customize_scenes_plugins.size()) { + // End scene customization + + String fcache = export_base_path.path_join("file_cache"); + Ref<FileAccess> f = FileAccess::open(fcache, FileAccess::WRITE); + + if (f.is_valid()) { + for (const KeyValue<String, FileExportCache> &E : export_cache) { + if (E.value.used) { // May be old, unused + String l = E.key + "::" + E.value.source_md5 + "::" + itos(E.value.source_modified_time) + "::" + E.value.saved_path; + f->store_line(l); + } + } + } else { + ERR_PRINT("Error opening export file cache: " + fcache); + } + + for (uint32_t i = 0; i < customize_resources_plugins.size(); i++) { + customize_resources_plugins[i]->_end_customize_resources(); + } + + for (uint32_t i = 0; i < customize_scenes_plugins.size(); i++) { + customize_scenes_plugins[i]->_end_customize_scenes(); + } + } //save config! Vector<String> custom_list; diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index bbdb47e041..93bc54284f 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -40,6 +40,8 @@ struct EditorProgress; #include "scene/gui/rich_text_label.h" #include "scene/main/node.h" +class EditorExportPlugin; + class EditorExportPlatform : public RefCounted { GDCLASS(EditorExportPlatform, RefCounted); @@ -99,6 +101,20 @@ private: static Error _add_shared_object(void *p_userdata, const SharedObject &p_so); + struct FileExportCache { + uint64_t source_modified_time = 0; + String source_md5; + String saved_path; + bool used = false; + }; + + bool _export_customize_dictionary(Dictionary &dict, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); + bool _export_customize_array(Array &array, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); + bool _export_customize_object(Object *p_object, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); + bool _export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); + + String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save); + protected: struct ExportNotifier { ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags); diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 27a671a919..971ea579cc 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -138,6 +138,64 @@ void EditorExportPlugin::_export_end_script() { GDVIRTUAL_CALL(_export_end); } +// Customization + +bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const { + bool ret = false; + if (GDVIRTUAL_CALL(_begin_customize_resources, p_platform, p_features, ret)) { + return ret; + } + return false; +} + +Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) { + Ref<Resource> ret; + if (GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret)) { + return ret; + } + return Ref<Resource>(); +} + +bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const { + bool ret = false; + if (GDVIRTUAL_CALL(_begin_customize_scenes, p_platform, p_features, ret)) { + return ret; + } + return false; +} + +Node *EditorExportPlugin::_customize_scene(Node *p_root, const String &p_path) { + Node *ret = nullptr; + if (GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret)) { + return ret; + } + return nullptr; +} + +uint64_t EditorExportPlugin::_get_customization_configuration_hash() const { + uint64_t ret = 0; + if (GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret)) { + return ret; + } + return 0; +} + +void EditorExportPlugin::_end_customize_scenes() { + GDVIRTUAL_CALL(_end_customize_scenes); +} + +void EditorExportPlugin::_end_customize_resources() { + GDVIRTUAL_CALL(_end_customize_resources); +} + +String EditorExportPlugin::_get_name() const { + String ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_name, ret)) { + return ret; + } + return ""; +} + void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { } @@ -164,38 +222,20 @@ void EditorExportPlugin::_bind_methods() { GDVIRTUAL_BIND(_export_file, "path", "type", "features"); GDVIRTUAL_BIND(_export_begin, "features", "is_debug", "path", "flags"); GDVIRTUAL_BIND(_export_end); -} -EditorExportPlugin::EditorExportPlugin() { -} + GDVIRTUAL_BIND(_begin_customize_resources, "platform", "features"); + GDVIRTUAL_BIND(_customize_resource, "resource", "path"); -/////////////////////// + GDVIRTUAL_BIND(_begin_customize_scenes, "platform", "features"); + GDVIRTUAL_BIND(_customize_scene, "scene", "path"); -void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { - String extension = p_path.get_extension().to_lower(); - if (extension != "tres" && extension != "tscn") { - return; - } + GDVIRTUAL_BIND(_get_customization_configuration_hash); - bool convert = GLOBAL_GET("editor/export/convert_text_resources_to_binary"); - if (!convert) { - return; - } - String tmp_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpfile.res"); - Error err = ResourceFormatLoaderText::convert_file_to_binary(p_path, tmp_path); - if (err != OK) { - DirAccess::remove_file_or_error(tmp_path); - ERR_FAIL(); - } - Vector<uint8_t> data = FileAccess::get_file_as_array(tmp_path); - if (data.size() == 0) { - DirAccess::remove_file_or_error(tmp_path); - ERR_FAIL(); - } - DirAccess::remove_file_or_error(tmp_path); - add_file(p_path + ".converted.res", data, true); + GDVIRTUAL_BIND(_end_customize_scenes); + GDVIRTUAL_BIND(_end_customize_resources); + + GDVIRTUAL_BIND(_get_name); } -EditorExportTextSceneToBinaryPlugin::EditorExportTextSceneToBinaryPlugin() { - GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false); +EditorExportPlugin::EditorExportPlugin() { } diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 04ebc1dfed..3f37ed40be 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -34,6 +34,7 @@ #include "core/extension/native_extension.h" #include "editor_export_preset.h" #include "editor_export_shared_object.h" +#include "scene/main/node.h" class EditorExportPlugin : public RefCounted { GDCLASS(EditorExportPlugin, RefCounted); @@ -77,6 +78,7 @@ class EditorExportPlugin : public RefCounted { macos_plugin_files.clear(); } + // Export void _export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features); void _export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags); void _export_end_script(); @@ -108,6 +110,31 @@ protected: GDVIRTUAL4(_export_begin, Vector<String>, bool, String, uint32_t) GDVIRTUAL0(_export_end) + GDVIRTUAL2RC(bool, _begin_customize_resources, const Ref<EditorExportPlatform> &, const Vector<String> &) + GDVIRTUAL2R(Ref<Resource>, _customize_resource, const Ref<Resource> &, String) + + GDVIRTUAL2RC(bool, _begin_customize_scenes, const Ref<EditorExportPlatform> &, const Vector<String> &) + GDVIRTUAL2R(Node *, _customize_scene, Node *, String) + GDVIRTUAL0RC(uint64_t, _get_customization_configuration_hash) + + GDVIRTUAL0(_end_customize_scenes) + GDVIRTUAL0(_end_customize_resources) + + GDVIRTUAL0RC(String, _get_name) + + bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization + Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made. + + bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization + Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made + + uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes. + + void _end_customize_scenes(); + void _end_customize_resources(); + + virtual String _get_name() const; + public: Vector<String> get_ios_frameworks() const; Vector<String> get_ios_embedded_frameworks() const; @@ -121,12 +148,4 @@ public: EditorExportPlugin(); }; -class EditorExportTextSceneToBinaryPlugin : public EditorExportPlugin { - GDCLASS(EditorExportTextSceneToBinaryPlugin, EditorExportPlugin); - -public: - virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override; - EditorExportTextSceneToBinaryPlugin(); -}; - #endif // EDITOR_EXPORT_PLUGIN_H diff --git a/editor/icons/BoxContainer.svg b/editor/icons/BoxContainer.svg new file mode 100644 index 0000000000..03b918d9cf --- /dev/null +++ b/editor/icons/BoxContainer.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.4520481 6.9086748c-.60273897.602739-.60272261 1.5799281 0 2.1826507l5.4566267 5.4566265c.602739.602739 1.5799281.602723 2.1826507 0l5.4566265-5.4566265c.602739-.602739.602723-1.5799281 0-2.1826507l-5.4566265-5.4566265c-.602739-.60273904-1.5799281-.60272304-2.1826507 0zm1.0913253 1.0913253 1.0913254-1.0913253 5.4566267 5.4566262-1.0913253 1.091326zm2.1826507-2.1826498 1.0913254-1.091326 5.4566265 5.4566267-1.091326 1.091325zm2.1826507-2.182651 1.0913254-1.091325 5.4566258 5.4566258-1.091325 1.0913254z" fill="#8eef97" stroke-width=".771684"/></svg> diff --git a/editor/icons/CameraEffects.svg b/editor/icons/CameraAttributes.svg index 1ee7e15c87..1ee7e15c87 100644 --- a/editor/icons/CameraEffects.svg +++ b/editor/icons/CameraAttributes.svg diff --git a/editor/icons/FlowContainer.svg b/editor/icons/FlowContainer.svg new file mode 100644 index 0000000000..57699ce874 --- /dev/null +++ b/editor/icons/FlowContainer.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1.4491431 6.9081906c-.59885747.5988575-.59885747 1.5847615 0 2.1836189l5.4590474 5.4590465c.5988576.598858 1.5847616.598858 2.183619.000001l5.4590465-5.4590475c.598858-.5988574.598858-1.5847614.000001-2.183619l-5.4590475-5.4590474c-.5988574-.59885747-1.5847614-.59885747-2.183619 0zm1.0918095 1.0918091 5.4590471-5.4590471 5.4590473 5.4590471-5.4590473 5.4590473zm1.6377142-.5459043c-.3024312.3024312-.3024312.7893781 0 1.0918092.3024313.3024311.7893783.3024311 1.0918095 0l1.0918095-1.0918093c.3024312-.3024312.3024312-.7893782 0-1.0918094-.3024312-.3024313-.7893782-.3024313-1.0918095 0zm2.7273401-2.7273401c-.3024312.3024311-.3024312.7893782 0 1.0918094.3024313.3024313.7893783.3024313 1.0918095 0l.5480882-.5480884c.3024311-.3024311.3024319-.7893783 0-1.0918094-.3024311-.3024313-.789378-.3024313-1.0918093 0zm-1.0896258 4.3650542c-.3024313.3024311-.3024313.7893779 0 1.0918095.3024312.302431.7893781.302431 1.0918094 0 .3024312-.3024316.3024313-.7893784 0-1.0918095-.3024311-.3024311-.7893781-.3024311-1.0918094 0zm1.6377142-1.6377142c-.3024313.3024313-.3024313.7893782 0 1.0918093s.7893782.3024311 1.0918093 0l1.6377144-1.637714c.302431-.3024312.302431-.7893783 0-1.0918096-.3024316-.3024312-.7893784-.3024311-1.0918095.0000002zm-.00218 3.2776127c-.3024312.302431-.3024313.789377-.0000001 1.091809.3024312.302431.7893782.302431 1.0918093 0l2.1858035-2.1858026c.302431-.3024311.302431-.7893787 0-1.0918098s-.7893792-.3024311-1.0918103 0z" fill="#8eef97" fill-rule="nonzero" stroke-width=".772026"/></svg> diff --git a/editor/icons/NavigationLink2D.svg b/editor/icons/NavigationLink2D.svg new file mode 100644 index 0000000000..6c5f17e256 --- /dev/null +++ b/editor/icons/NavigationLink2D.svg @@ -0,0 +1,4 @@ +<svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m12.386 5.3097c-0.69157-0.021112-1.3071 0.36382-1.7492 0.86685-0.58 0.58-1.16 1.16-1.74 1.74 0.4588-0.28502 1.0599-0.064948 1.4771-0.037996 0.45549-0.44357 0.89024-0.91006 1.3596-1.3383 0.56256-0.44564 1.4906-0.15731 1.7028 0.52802 0.18967 0.4871-0.049221 1.0098-0.43284 1.3208-0.70048 0.68896-1.3789 1.4022-2.0935 2.0755-0.47999 0.3725-1.2044 0.226-1.5679-0.24034-0.38763-0.38194-1.0641 0.16031-0.78317 0.6241 0.6767 0.94379 2.1573 1.1282 3.0411 0.36751 0.80287-0.7704 1.5793-1.5696 2.3665-2.3564 0.79925-0.83719 0.70104-2.3112-0.19552-3.0393-0.38108-0.32877-0.8822-0.5119-1.385-0.51049zm-3.051 3.051c-0.69157-0.021106-1.3071 0.36382-1.7492 0.86685-0.67513 0.68452-1.37 1.3506-2.0319 2.0474-0.75433 0.87744-0.58087 2.3428 0.34933 3.0252 0.84748 0.68613 2.192 0.54839 2.8998-0.27341 0.63032-0.63031 1.2606-1.2606 1.8909-1.8909-0.4587 0.28554-1.0602 0.0659-1.477 0.038069-0.45445 0.44348-0.88773 0.91034-1.3564 1.3383-0.56256 0.44565-1.4906 0.15731-1.7028-0.52802-0.18967-0.4871 0.049229-1.0098 0.43284-1.3208 0.70048-0.68896 1.3789-1.4022 2.0935-2.0755 0.48-0.3725 1.2044-0.22601 1.5679 0.24036 0.38733 0.38325 1.064-0.16067 0.78313-0.6241-0.39353-0.52481-1.0429-0.84871-1.7002-0.8434z" fill="#8ea6f4" fill-opacity=".99608" stroke-linecap="round" stroke-linejoin="round" stroke-width=".013911"/> +<path d="m2 1c-0.61942-0.0066969-1.0877 0.60314-1 1.198 0.00345 3.968-0.006897 7.9364 0.00517 11.904 0.043388 0.62851 0.69346 0.98513 1.272 0.89776h2.5896c-0.77174-0.5015-1.2078-1.2613-1.3143-2.3356-0.11601-1.1701 0.63729-2.024 1.6748-3.1566 0.65335-0.71326 1.4757-1.5822 2.3587-2.3316 0.76308-0.64765 1.7509-1.679 2.9376-2.578 0.91259-0.69136 2.2893-0.74691 3.1014-0.33143 0.91184 0.46649 1.2635 1.1209 1.4067 1.3826-0.0052-2.335-0.02135-1.3526-0.03955-3.6863 5e-3 -0.64349-0.67497-1.0568-1.2694-0.96289z" fill="#8ea6f4" fill-opacity=".99608"/> +</svg> diff --git a/editor/icons/NavigationLink3D.svg b/editor/icons/NavigationLink3D.svg new file mode 100644 index 0000000000..ea4092c2c7 --- /dev/null +++ b/editor/icons/NavigationLink3D.svg @@ -0,0 +1,4 @@ +<svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m12.386 5.3097c-0.69157-0.021112-1.3071 0.36382-1.7492 0.86685-0.58 0.58-1.16 1.16-1.74 1.74 0.4588-0.28502 1.0599-0.064948 1.4771-0.037996 0.45549-0.44357 0.89024-0.91006 1.3596-1.3383 0.56256-0.44564 1.4906-0.15731 1.7028 0.52802 0.18967 0.4871-0.049221 1.0098-0.43284 1.3208-0.70048 0.68896-1.3789 1.4022-2.0935 2.0755-0.47999 0.3725-1.2044 0.226-1.5679-0.24034-0.38763-0.38194-1.0641 0.16031-0.78317 0.6241 0.6767 0.94379 2.1573 1.1282 3.0411 0.36751 0.80287-0.7704 1.5793-1.5696 2.3665-2.3564 0.79925-0.83719 0.70104-2.3112-0.19552-3.0393-0.38108-0.32877-0.8822-0.5119-1.385-0.51049zm-3.051 3.051c-0.69157-0.021106-1.3071 0.36382-1.7492 0.86685-0.67513 0.68452-1.37 1.3506-2.0319 2.0474-0.75433 0.87744-0.58087 2.3428 0.34933 3.0252 0.84748 0.68613 2.192 0.54839 2.8998-0.27341 0.63032-0.63031 1.2606-1.2606 1.8909-1.8909-0.4587 0.28554-1.0602 0.0659-1.477 0.038069-0.45445 0.44348-0.88773 0.91034-1.3564 1.3383-0.56256 0.44565-1.4906 0.15731-1.7028-0.52802-0.18967-0.4871 0.049229-1.0098 0.43284-1.3208 0.70048-0.68896 1.3789-1.4022 2.0935-2.0755 0.48-0.3725 1.2044-0.22601 1.5679 0.24036 0.38733 0.38325 1.064-0.16067 0.78313-0.6241-0.39353-0.52481-1.0429-0.84871-1.7002-0.8434z" fill="#fc7e7e" fill-opacity=".99608" stroke-linecap="round" stroke-linejoin="round" stroke-width=".013911"/> +<path d="m2 1c-0.61942-0.0066969-1.0877 0.60314-1 1.198 0.00345 3.968-0.006897 7.9364 0.00517 11.904 0.043388 0.62851 0.69346 0.98513 1.272 0.89776h2.5896c-0.77174-0.5015-1.2078-1.2613-1.3143-2.3356-0.11601-1.1701 0.63729-2.024 1.6748-3.1566 0.65335-0.71326 1.4757-1.5822 2.3587-2.3316 0.76308-0.64765 1.7509-1.679 2.9376-2.578 0.91259-0.69136 2.2893-0.74691 3.1014-0.33143 0.91184 0.46649 1.2635 1.1209 1.4067 1.3826-0.0052-2.335-0.02135-1.3526-0.03955-3.6863 5e-3 -0.64349-0.67497-1.0568-1.2694-0.96289z" fill="#fc7d7d" fill-opacity=".99608"/> +</svg> diff --git a/editor/icons/SplitContainer.svg b/editor/icons/SplitContainer.svg new file mode 100644 index 0000000000..bb03350166 --- /dev/null +++ b/editor/icons/SplitContainer.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.452048 6.9086746c-.60273888.6027391-.60272252 1.5799284 0 2.1826511l5.4566266 5.4566263c.6027391.602739 1.5799284.602722 2.1826511 0l5.4566263-5.4566263c.602739-.6027397.602722-1.5799284 0-2.1826511l-5.4566263-5.4566266c-.6027397-.60273888-1.5799284-.60272252-2.1826511 0zm1.0913254 1.0913254 2.1826506-2.1826506 1.636988 1.6369879v2.1826504h2.1826508l1.6369882 1.6369883-2.182651 2.182651zm3.273976-3.273976 2.1826506-2.1826506 5.456627 5.4566266-2.182651 2.182651-1.6369883-1.6369882v-2.1826508h-2.1826504z" fill="#8eef97" stroke-width=".771683"/></svg> diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index c03962b8a4..577a4c32b3 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -99,7 +99,7 @@ Error ResourceImporterBitMap::import(const String &p_source_file, const String & bit = c.a > threshold; } - bitmap->set_bit(Vector2(j, i), bit); + bitmap->set_bit(j, i, bit); } } diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 1ff771bcce..730aa3bd61 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -30,6 +30,7 @@ #include "scene_import_settings.h" +#include "core/config/project_settings.h" #include "editor/editor_file_dialog.h" #include "editor/editor_file_system.h" #include "editor/editor_inspector.h" @@ -1288,6 +1289,11 @@ SceneImportSettings::SceneImportSettings() { base_viewport->add_child(camera); camera->make_current(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } + light = memnew(DirectionalLight3D); light->set_transform(Transform3D().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0))); base_viewport->add_child(light); diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index b5cf82f64b..104a7a9f7e 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -74,6 +74,7 @@ class SceneImportSettings : public ConfirmationDialog { SubViewport *base_viewport = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; bool first_aabb = false; AABB contents_aabb; diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 988f9cc394..46e2fe41af 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -681,7 +681,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) { } if (!found) { for (int i = 0; i < search_path.size(); i++) { - if (Vector3(0, 0, 0).is_equal_approx(skeleton->get_bone_global_rest(search_path[i]).origin)) { + if (skeleton->get_bone_global_rest(search_path[i]).origin.is_zero_approx()) { bone_idx = search_path[i]; // The bone existing at the origin is appropriate as a root. found = true; break; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index f4a718119e..0ec36736bd 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -201,7 +201,7 @@ Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, for (int i = 0; i < bm->get_size().width; i++) { for (int j = 0; j < bm->get_size().height; j++) { - if (bm->get_bit(Point2i(i, j))) { + if (bm->get_bit(i, j)) { w[j * (int)bm->get_size().width + i] = 255; } else { w[j * (int)bm->get_size().width + i] = 0; @@ -342,6 +342,12 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() { RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3))); RS::get_singleton()->camera_set_perspective(camera, 45, 0.1, 10); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes = RS::get_singleton()->camera_attributes_create(); + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds. + RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes); + } + light = RS::get_singleton()->directional_light_create(); light_instance = RS::get_singleton()->instance_create2(light, scenario); RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); @@ -440,6 +446,7 @@ EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { RS::get_singleton()->free(light2); RS::get_singleton()->free(light_instance2); RS::get_singleton()->free(camera); + RS::get_singleton()->free(camera_attributes); RS::get_singleton()->free(scenario); } @@ -743,6 +750,12 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() { //RS::get_singleton()->camera_set_perspective(camera,45,0.1,10); RS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes = RS::get_singleton()->camera_attributes_create(); + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds. + RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes); + } + light = RS::get_singleton()->directional_light_create(); light_instance = RS::get_singleton()->instance_create2(light, scenario); RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); @@ -768,6 +781,7 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { RS::get_singleton()->free(light2); RS::get_singleton()->free(light_instance2); RS::get_singleton()->free(camera); + RS::get_singleton()->free(camera_attributes); RS::get_singleton()->free(scenario); } diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 163cfe79f9..efb2c80cfd 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -91,6 +91,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; + RID camera_attributes; Semaphore preview_done; void _generate_frame_started(); @@ -133,6 +134,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; + RID camera_attributes; Semaphore preview_done; void _generate_frame_started(); diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index b5eca46ad3..e1d68d97b5 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -36,6 +36,7 @@ class GDExtensionExportPlugin : public EditorExportPlugin { protected: virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features); + virtual String _get_name() const { return "GDExtension"; } }; void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { diff --git a/scene/gui/gradient_edit.cpp b/editor/plugins/gradient_editor.cpp index 5e56be7c29..c13b162db6 100644 --- a/scene/gui/gradient_edit.cpp +++ b/editor/plugins/gradient_editor.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* gradient_edit.cpp */ +/* gradient_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "gradient_edit.h" +#include "gradient_editor.h" #include "core/os/keyboard.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" + +void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { + gradient = p_gradient; + connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed)); + gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed)); + set_points(gradient->get_points()); + set_interpolation_mode(gradient->get_interpolation_mode()); +} -GradientEdit::GradientEdit() { - set_focus_mode(FOCUS_ALL); - - popup = memnew(PopupPanel); - picker = memnew(ColorPicker); - popup->add_child(picker); - - gradient_cache.instantiate(); - preview_texture.instantiate(); - - preview_texture->set_width(1024); - add_child(popup, false, INTERNAL_MODE_FRONT); +void GradientEditor::reverse_gradient() { + gradient->reverse(); + set_points(gradient->get_points()); + emit_signal(SNAME("ramp_changed")); + queue_redraw(); } -int GradientEdit::_get_point_from_pos(int x) { +int GradientEditor::_get_point_from_pos(int x) { int result = -1; int total_w = get_size().width - get_size().height - draw_spacing; float min_distance = 1e20; @@ -62,7 +66,7 @@ int GradientEdit::_get_point_from_pos(int x) { return result; } -void GradientEdit::_show_color_picker() { +void GradientEditor::_show_color_picker() { if (grabbed == -1) { return; } @@ -80,10 +84,106 @@ void GradientEdit::_show_color_picker() { popup->popup(); } -GradientEdit::~GradientEdit() { +void GradientEditor::_gradient_changed() { + if (editing) { + return; + } + + editing = true; + Vector<Gradient::Point> points = gradient->get_points(); + set_points(points); + set_interpolation_mode(gradient->get_interpolation_mode()); + queue_redraw(); + editing = false; +} + +void GradientEditor::_ramp_changed() { + editing = true; + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); + undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); + undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode()); + undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); + undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors()); + undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode()); + undo_redo->commit_action(); + editing = false; +} + +void GradientEditor::_color_changed(const Color &p_color) { + if (grabbed == -1) { + return; + } + points.write[grabbed].color = p_color; + queue_redraw(); + emit_signal(SNAME("ramp_changed")); +} + +void GradientEditor::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) { + ERR_FAIL_COND(p_offsets.size() != p_colors.size()); + points.clear(); + for (int i = 0; i < p_offsets.size(); i++) { + Gradient::Point p; + p.offset = p_offsets[i]; + p.color = p_colors[i]; + points.push_back(p); + } + + points.sort(); + queue_redraw(); +} + +Vector<float> GradientEditor::get_offsets() const { + Vector<float> ret; + for (int i = 0; i < points.size(); i++) { + ret.push_back(points[i].offset); + } + return ret; +} + +Vector<Color> GradientEditor::get_colors() const { + Vector<Color> ret; + for (int i = 0; i < points.size(); i++) { + ret.push_back(points[i].color); + } + return ret; +} + +void GradientEditor::set_points(Vector<Gradient::Point> &p_points) { + if (points.size() != p_points.size()) { + grabbed = -1; + } + points.clear(); + points = p_points; + points.sort(); +} + +Vector<Gradient::Point> &GradientEditor::get_points() { + return points; +} + +void GradientEditor::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { + interpolation_mode = p_interp_mode; +} + +Gradient::InterpolationMode GradientEditor::get_interpolation_mode() { + return interpolation_mode; } -void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { +ColorPicker *GradientEditor::get_picker() { + return picker; +} + +PopupPanel *GradientEditor::get_popup() { + return popup; +} + +Size2 GradientEditor::get_minimum_size() const { + return Size2(0, 60) * EDSCALE; +} + +void GradientEditor::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref<InputEventKey> k = p_event; @@ -286,11 +386,11 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } } -void GradientEdit::_notification(int p_what) { +void GradientEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { - picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); + if (!picker->is_connected("color_changed", callable_mp(this, &GradientEditor::_color_changed))) { + picker->connect("color_changed", callable_mp(this, &GradientEditor::_color_changed)); } [[fallthrough]]; } @@ -369,78 +469,24 @@ void GradientEdit::_notification(int p_what) { } } -Size2 GradientEdit::get_minimum_size() const { - return Vector2(0, 16); -} - -void GradientEdit::_color_changed(const Color &p_color) { - if (grabbed == -1) { - return; - } - points.write[grabbed].color = p_color; - queue_redraw(); - emit_signal(SNAME("ramp_changed")); -} - -void GradientEdit::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) { - ERR_FAIL_COND(p_offsets.size() != p_colors.size()); - points.clear(); - for (int i = 0; i < p_offsets.size(); i++) { - Gradient::Point p; - p.offset = p_offsets[i]; - p.color = p_colors[i]; - points.push_back(p); - } - - points.sort(); - queue_redraw(); -} - -Vector<float> GradientEdit::get_offsets() const { - Vector<float> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].offset); - } - return ret; -} - -Vector<Color> GradientEdit::get_colors() const { - Vector<Color> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].color); - } - return ret; -} - -void GradientEdit::set_points(Vector<Gradient::Point> &p_points) { - if (points.size() != p_points.size()) { - grabbed = -1; - } - points.clear(); - points = p_points; - points.sort(); -} - -Vector<Gradient::Point> &GradientEdit::get_points() { - return points; +void GradientEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("ramp_changed")); } -void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { - interpolation_mode = p_interp_mode; -} +GradientEditor::GradientEditor() { + set_focus_mode(FOCUS_ALL); -Gradient::InterpolationMode GradientEdit::get_interpolation_mode() { - return interpolation_mode; -} + popup = memnew(PopupPanel); + picker = memnew(ColorPicker); + popup->add_child(picker); + popup->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(GradientEditor::get_picker())); -ColorPicker *GradientEdit::get_picker() { - return picker; -} + gradient_cache.instantiate(); + preview_texture.instantiate(); -PopupPanel *GradientEdit::get_popup() { - return popup; + preview_texture->set_width(1024); + add_child(popup, false, INTERNAL_MODE_FRONT); } -void GradientEdit::_bind_methods() { - ADD_SIGNAL(MethodInfo("ramp_changed")); +GradientEditor::~GradientEditor() { } diff --git a/scene/gui/gradient_edit.h b/editor/plugins/gradient_editor.h index b7c99f1f1c..816b539ba2 100644 --- a/scene/gui/gradient_edit.h +++ b/editor/plugins/gradient_editor.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* gradient_edit.h */ +/* gradient_editor.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GRADIENT_EDIT_H -#define GRADIENT_EDIT_H +#ifndef GRADIENT_EDITOR_H +#define GRADIENT_EDITOR_H #include "scene/gui/color_picker.h" #include "scene/gui/popup.h" #include "scene/resources/gradient.h" -class GradientEdit : public Control { - GDCLASS(GradientEdit, Control); +class GradientEditor : public Control { + GDCLASS(GradientEditor, Control); PopupPanel *popup = nullptr; ColorPicker *picker = nullptr; @@ -46,6 +46,8 @@ class GradientEdit : public Control { Vector<Gradient::Point> points; Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR; + bool editing = false; + Ref<Gradient> gradient; Ref<Gradient> gradient_cache; Ref<GradientTexture1D> preview_texture; @@ -56,8 +58,10 @@ class GradientEdit : public Control { int draw_spacing = BASE_SPACING; int draw_point_width = BASE_POINT_WIDTH; - void _draw_checker(int x, int y, int w, int h); + void _gradient_changed(); + void _ramp_changed(); void _color_changed(const Color &p_color); + int _get_point_from_pos(int x); void _show_color_picker(); @@ -67,20 +71,26 @@ protected: static void _bind_methods(); public: + void set_gradient(const Ref<Gradient> &p_gradient); + void reverse_gradient(); + void set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors); + Vector<float> get_offsets() const; Vector<Color> get_colors() const; void set_points(Vector<Gradient::Point> &p_points); Vector<Gradient::Point> &get_points(); + void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode); Gradient::InterpolationMode get_interpolation_mode(); + ColorPicker *get_picker(); PopupPanel *get_popup(); virtual Size2 get_minimum_size() const override; - GradientEdit(); - virtual ~GradientEdit(); + GradientEditor(); + virtual ~GradientEditor(); }; -#endif // GRADIENT_EDIT_H +#endif // GRADIENT_EDITOR_H diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 890090c899..0f412aaefd 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -37,62 +37,6 @@ #include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" -Size2 GradientEditor::get_minimum_size() const { - return Size2(0, 60) * EDSCALE; -} - -void GradientEditor::_gradient_changed() { - if (editing) { - return; - } - - editing = true; - Vector<Gradient::Point> points = gradient->get_points(); - set_points(points); - set_interpolation_mode(gradient->get_interpolation_mode()); - queue_redraw(); - editing = false; -} - -void GradientEditor::_ramp_changed() { - editing = true; - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); - undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); - undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode()); - undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); - undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors()); - undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode()); - undo_redo->commit_action(); - editing = false; -} - -void GradientEditor::_bind_methods() { -} - -void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { - gradient = p_gradient; - connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed)); - gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed)); - set_points(gradient->get_points()); - set_interpolation_mode(gradient->get_interpolation_mode()); -} - -void GradientEditor::reverse_gradient() { - gradient->reverse(); - set_points(gradient->get_points()); - emit_signal(SNAME("ramp_changed")); - queue_redraw(); -} - -GradientEditor::GradientEditor() { - GradientEdit::get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(GradientEdit::get_picker())); - editing = false; -} - -/////////////////////// - void GradientReverseButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h index 26bf76fecd..ab191d83e2 100644 --- a/editor/plugins/gradient_editor_plugin.h +++ b/editor/plugins/gradient_editor_plugin.h @@ -32,26 +32,7 @@ #define GRADIENT_EDITOR_PLUGIN_H #include "editor/editor_plugin.h" -#include "scene/gui/gradient_edit.h" - -class GradientEditor : public GradientEdit { - GDCLASS(GradientEditor, GradientEdit); - - bool editing; - Ref<Gradient> gradient; - - void _gradient_changed(); - void _ramp_changed(); - -protected: - static void _bind_methods(); - -public: - virtual Size2 get_minimum_size() const override; - void set_gradient(const Ref<Gradient> &p_gradient); - void reverse_gradient(); - GradientEditor(); -}; +#include "gradient_editor.h" class GradientReverseButton : public BaseButton { GDCLASS(GradientReverseButton, BaseButton); diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 82441bfa76..2087658408 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -30,6 +30,7 @@ #include "material_editor_plugin.h" +#include "core/config/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" @@ -161,6 +162,10 @@ MaterialEditor::MaterialEditor() { // without much distortion. camera->set_perspective(20, 0.1, 10); camera->make_current(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } viewport->add_child(camera); light1 = memnew(DirectionalLight3D); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 06ae43e6d7..828dd9f972 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -55,6 +55,7 @@ class MaterialEditor : public Control { DirectionalLight3D *light1 = nullptr; DirectionalLight3D *light2 = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; Ref<SphereMesh> sphere_mesh; Ref<BoxMesh> box_mesh; diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 90d50d30d9..d8977ea6fc 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -30,6 +30,7 @@ #include "mesh_editor_plugin.h" +#include "core/config/project_settings.h" #include "editor/editor_scale.h" void MeshEditor::gui_input(const Ref<InputEvent> &p_event) { @@ -119,6 +120,11 @@ MeshEditor::MeshEditor() { camera->set_perspective(45, 0.1, 10); viewport->add_child(camera); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } + light1 = memnew(DirectionalLight3D); light1->set_transform(Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); viewport->add_child(light1); diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h index fb61f03485..ab7b5db7c4 100644 --- a/editor/plugins/mesh_editor_plugin.h +++ b/editor/plugins/mesh_editor_plugin.h @@ -36,6 +36,7 @@ #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/subviewport_container.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/material.h" class MeshEditor : public SubViewportContainer { @@ -50,6 +51,7 @@ class MeshEditor : public SubViewportContainer { DirectionalLight3D *light1 = nullptr; DirectionalLight3D *light2 = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; Ref<Mesh> mesh; diff --git a/editor/plugins/navigation_link_2d_editor_plugin.cpp b/editor/plugins/navigation_link_2d_editor_plugin.cpp new file mode 100644 index 0000000000..b72f639fbf --- /dev/null +++ b/editor/plugins/navigation_link_2d_editor_plugin.cpp @@ -0,0 +1,191 @@ +/*************************************************************************/ +/* navigation_link_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_link_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" +#include "servers/navigation_server_3d.h" + +void NavigationLink2DEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", callable_mp(this, &NavigationLink2DEditor::_node_removed)); + } break; + + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &NavigationLink2DEditor::_node_removed)); + } break; + } +} + +void NavigationLink2DEditor::_node_removed(Node *p_node) { + if (p_node == node) { + node = nullptr; + } +} + +bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { + if (!node || !node->is_visible_in_tree()) { + return false; + } + + real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius"); + Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + // Start location + if (xform.xform(node->get_start_location()).distance_to(mb->get_position()) < grab_threshold) { + start_grabbed = true; + original_start_location = node->get_start_location(); + + return true; + } else { + start_grabbed = false; + } + + // End location + if (xform.xform(node->get_end_location()).distance_to(mb->get_position()) < grab_threshold) { + end_grabbed = true; + original_end_location = node->get_end_location(); + + return true; + } else { + end_grabbed = false; + } + } else { + if (start_grabbed) { + undo_redo->create_action(TTR("Set start_location")); + undo_redo->add_do_method(node, "set_start_location", node->get_start_location()); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(node, "set_start_location", original_start_location); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + + start_grabbed = false; + + return true; + } + + if (end_grabbed) { + undo_redo->create_action(TTR("Set end_location")); + undo_redo->add_do_method(node, "set_end_location", node->get_end_location()); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(node, "set_end_location", original_end_location); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + + end_grabbed = false; + + return true; + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())); + point = node->get_global_transform().affine_inverse().xform(point); + + if (start_grabbed) { + node->set_start_location(point); + canvas_item_editor->update_viewport(); + + return true; + } + + if (end_grabbed) { + node->set_end_location(point); + canvas_item_editor->update_viewport(); + + return true; + } + } + + return false; +} + +void NavigationLink2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { + if (!node || !node->is_visible_in_tree()) { + return; + } + + Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Vector2 global_start_location = gt.xform(node->get_start_location()); + Vector2 global_end_location = gt.xform(node->get_end_location()); + + // Only drawing the handles here, since the debug rendering will fill in the rest. + const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); + p_overlay->draw_texture(handle, global_start_location - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_end_location - handle->get_size() / 2); +} + +void NavigationLink2DEditor::edit(NavigationLink2D *p_node) { + if (!canvas_item_editor) { + canvas_item_editor = CanvasItemEditor::get_singleton(); + } + + if (p_node) { + node = p_node; + } else { + node = nullptr; + } + + canvas_item_editor->update_viewport(); +} + +NavigationLink2DEditor::NavigationLink2DEditor() { + undo_redo = EditorNode::get_undo_redo(); +} + +/////////////////////// + +void NavigationLink2DEditorPlugin::edit(Object *p_object) { + editor->edit(Object::cast_to<NavigationLink2D>(p_object)); +} + +bool NavigationLink2DEditorPlugin::handles(Object *p_object) const { + return Object::cast_to<NavigationLink2D>(p_object) != nullptr; +} + +void NavigationLink2DEditorPlugin::make_visible(bool p_visible) { + if (!p_visible) { + edit(nullptr); + } +} + +NavigationLink2DEditorPlugin::NavigationLink2DEditorPlugin() { + editor = memnew(NavigationLink2DEditor); + EditorNode::get_singleton()->get_gui_base()->add_child(editor); +} diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h new file mode 100644 index 0000000000..1c1251bec7 --- /dev/null +++ b/editor/plugins/navigation_link_2d_editor_plugin.h @@ -0,0 +1,83 @@ +/*************************************************************************/ +/* navigation_link_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_LINK_2D_EDITOR_PLUGIN_H +#define NAVIGATION_LINK_2D_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "scene/2d/navigation_link_2d.h" + +class CanvasItemEditor; +class EditorUndoRedoManager; + +class NavigationLink2DEditor : public Control { + GDCLASS(NavigationLink2DEditor, Control); + + Ref<EditorUndoRedoManager> undo_redo; + CanvasItemEditor *canvas_item_editor = nullptr; + NavigationLink2D *node; + + bool start_grabbed = false; + Vector2 original_start_location; + + bool end_grabbed = false; + Vector2 original_end_location; + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + +public: + bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); + void forward_canvas_draw_over_viewport(Control *p_overlay); + void edit(NavigationLink2D *p_node); + + NavigationLink2DEditor(); +}; + +class NavigationLink2DEditorPlugin : public EditorPlugin { + GDCLASS(NavigationLink2DEditorPlugin, EditorPlugin); + + NavigationLink2DEditor *editor = nullptr; + +public: + virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return editor->forward_canvas_gui_input(p_event); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { editor->forward_canvas_draw_over_viewport(p_overlay); } + + virtual String get_name() const override { return "NavigationLink2D"; } + bool has_main_screen() const override { return false; } + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; + virtual void make_visible(bool p_visible) override; + + NavigationLink2DEditorPlugin(); +}; + +#endif // NAVIGATION_LINK_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 878f8c9a95..ec6ea7f39b 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -54,6 +54,7 @@ #include "scene/3d/lightmap_probe.h" #include "scene/3d/marker_3d.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/navigation_link_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/occluder_instance_3d.h" #include "scene/3d/ray_cast_3d.h" @@ -1365,7 +1366,8 @@ void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node()); - Color color = light->get_color(); + Color color = light->get_color().srgb_to_linear() * light->get_correlated_color().srgb_to_linear(); + color = color.linear_to_srgb(); // Make the gizmo color as bright as possible for better visibility color.set_hsv(color.get_h(), color.get_s(), 1); @@ -4998,6 +5000,175 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } } +//// + +NavigationLink3DGizmoPlugin::NavigationLink3DGizmoPlugin() { + create_material("navigation_link_material", NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_color()); + create_material("navigation_link_material_disabled", NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_disabled_color()); + create_handle_material("handles"); +} + +bool NavigationLink3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<NavigationLink3D>(p_spatial) != nullptr; +} + +String NavigationLink3DGizmoPlugin::get_gizmo_name() const { + return "NavigationLink3D"; +} + +int NavigationLink3DGizmoPlugin::get_priority() const { + return -1; +} + +void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_spatial_node()); + + RID nav_map = link->get_world_3d()->get_navigation_map(); + real_t search_radius = NavigationServer3D::get_singleton()->map_get_link_connection_radius(nav_map); + Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); + Vector3::Axis up_axis = up_vector.max_axis_index(); + + Vector3 start_location = link->get_start_location(); + Vector3 end_location = link->get_end_location(); + + Ref<Material> link_material = get_material("navigation_link_material", p_gizmo); + Ref<Material> link_material_disabled = get_material("navigation_link_material_disabled", p_gizmo); + Ref<Material> handles_material = get_material("handles"); + + p_gizmo->clear(); + + // Draw line between the points. + Vector<Vector3> lines; + lines.append(start_location); + lines.append(end_location); + + // Draw start location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(start_location + Vector3(0, a.x, a.y)); + lines.append(start_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(start_location + Vector3(a.x, 0, a.y)); + lines.append(start_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(start_location + Vector3(a.x, a.y, 0)); + lines.append(start_location + Vector3(b.x, b.y, 0)); + break; + } + } + + // Draw end location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(end_location + Vector3(0, a.x, a.y)); + lines.append(end_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(end_location + Vector3(a.x, 0, a.y)); + lines.append(end_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(end_location + Vector3(a.x, a.y, 0)); + lines.append(end_location + Vector3(b.x, b.y, 0)); + break; + } + } + + p_gizmo->add_lines(lines, link->is_enabled() ? link_material : link_material_disabled); + p_gizmo->add_collision_segments(lines); + + Vector<Vector3> handles; + handles.append(start_location); + handles.append(end_location); + p_gizmo->add_handles(handles, handles_material); +} + +String NavigationLink3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { + return p_id == 0 ? TTR("Start Location") : TTR("End Location"); +} + +Variant NavigationLink3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { + NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_spatial_node()); + return p_id == 0 ? link->get_start_location() : link->get_end_location(); +} + +void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { + NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_spatial_node()); + + Transform3D gt = link->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Transform3D ct = p_camera->get_global_transform(); + Vector3 cam_dir = ct.basis.get_column(Vector3::AXIS_Z); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 location = p_id == 0 ? link->get_start_location() : link->get_end_location(); + Plane move_plane = Plane(cam_dir, gt.xform(location)); + + Vector3 intersection; + if (!move_plane.intersects_ray(ray_from, ray_dir, &intersection)) { + return; + } + + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + double snap = Node3DEditor::get_singleton()->get_translate_snap(); + intersection.snap(Vector3(snap, snap, snap)); + } + + location = gi.xform(intersection); + if (p_id == 0) { + link->set_start_location(location); + } else if (p_id == 1) { + link->set_end_location(location); + } +} + +void NavigationLink3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) { + NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_spatial_node()); + + if (p_cancel) { + if (p_id == 0) { + link->set_start_location(p_restore); + } else { + link->set_end_location(p_restore); + } + return; + } + + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + if (p_id == 0) { + ur->create_action(TTR("Change Start Location")); + ur->add_do_method(link, "set_start_location", link->get_start_location()); + ur->add_undo_method(link, "set_start_location", p_restore); + } else { + ur->create_action(TTR("Change End Location")); + ur->add_do_method(link, "set_end_location", link->get_end_location()); + ur->add_undo_method(link, "set_end_location", p_restore); + } + + ur->commit_action(); +} + ////// #define BODY_A_RADIUS 0.25 diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 1b6485ac4e..5924f8571a 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -631,6 +631,23 @@ public: NavigationRegion3DGizmoPlugin(); }; +class NavigationLink3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(NavigationLink3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; + Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; + void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override; + void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override; + + NavigationLink3DGizmoPlugin(); +}; + class JointGizmosDrawer { public: static Basis look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index c60c366ffd..03cf9abed9 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2375,12 +2375,12 @@ void Node3DEditorPlugin::edited_scene_changed() { void Node3DEditorViewport::_project_settings_changed() { //update shadow atlas if changed - int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_size"); - bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_16_bits"); - int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv"); - int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv"); - int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv"); - int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv"); + int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_size"); + bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_16_bits"); + int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv"); + int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv"); + int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv"); + int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv"); viewport->set_positional_shadow_atlas_size(shadowmap_size); viewport->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); @@ -7122,6 +7122,9 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) { WorldEnvironment *new_env = memnew(WorldEnvironment); new_env->set_environment(preview_environment->get_environment()->duplicate(true)); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + new_env->set_camera_attributes(preview_environment->get_camera_attributes()->duplicate(true)); + } undo_redo->create_action(TTR("Add Preview Environment to Scene")); undo_redo->add_do_method(base, "add_child", new_env, true); @@ -7520,6 +7523,7 @@ void Node3DEditor::_register_all_gizmos() { add_gizmo_plugin(Ref<CollisionObject3DGizmoPlugin>(memnew(CollisionObject3DGizmoPlugin))); add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin))); add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin))); + add_gizmo_plugin(Ref<NavigationLink3DGizmoPlugin>(memnew(NavigationLink3DGizmoPlugin))); add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin))); add_gizmo_plugin(Ref<Joint3DGizmoPlugin>(memnew(Joint3DGizmoPlugin))); add_gizmo_plugin(Ref<PhysicalBone3DGizmoPlugin>(memnew(PhysicalBone3DGizmoPlugin))); @@ -7588,7 +7592,7 @@ void Node3DEditor::_preview_settings_changed() { } { //preview env - sky_material->set_sky_energy(environ_energy->get_value()); + sky_material->set_sky_energy_multiplier(environ_energy->get_value()); Color hz_color = environ_sky_color->get_pick_color().lerp(environ_ground_color->get_pick_color(), 0.5).lerp(Color(1, 1, 1), 0.5); sky_material->set_sky_top_color(environ_sky_color->get_pick_color()); sky_material->set_sky_horizon_color(hz_color); @@ -8308,6 +8312,10 @@ void fragment() { preview_environment = memnew(WorldEnvironment); environment.instantiate(); preview_environment->set_environment(environment); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + preview_environment->set_camera_attributes(camera_attributes); + } Ref<Sky> sky; sky.instantiate(); sky_material.instantiate(); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index d38c5948cc..580cb878ce 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -765,6 +765,7 @@ private: DirectionalLight3D *preview_sun = nullptr; WorldEnvironment *preview_environment = nullptr; Ref<Environment> environment; + Ref<CameraAttributesPhysical> camera_attributes; Ref<ProceduralSkyMaterial> sky_material; bool sun_environ_updating = false; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 034716e9de..c57412978d 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -426,12 +426,14 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi bone_shape->set_name("CollisionShape3D"); Transform3D capsule_transform; - capsule_transform.basis = Basis(Vector3(1, 0, 0), Vector3(0, 0, 1), Vector3(0, -1, 0)); + capsule_transform.basis.rows[0] = Vector3(1, 0, 0); + capsule_transform.basis.rows[1] = Vector3(0, 0, 1); + capsule_transform.basis.rows[2] = Vector3(0, -1, 0); bone_shape->set_transform(capsule_transform); /// Get an up vector not collinear with child rest origin Vector3 up = Vector3(0, 1, 0); - if (up.cross(child_rest.origin).is_equal_approx(Vector3())) { + if (up.cross(child_rest.origin).is_zero_approx()) { up = Vector3(0, 0, 1); } diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 5f0576a675..d9291503cb 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -139,7 +139,7 @@ void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { // Center of panel. panning = panning * zoom / previous_zoom; } - button_center_view->set_disabled(panning.is_equal_approx(Vector2())); + button_center_view->set_disabled(panning.is_zero_approx()); previous_zoom = zoom; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 1bfbd342c2..1dce41e4c7 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -448,7 +448,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) } else if (drag_type == DRAG_TYPE_PAN) { panning += mm->get_position() - drag_last_pos; drag_last_pos = mm->get_position(); - button_center_view->set_disabled(panning.is_equal_approx(Vector2())); + button_center_view->set_disabled(panning.is_zero_approx()); } else { // Update hovered point. _grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index c1cf6b2dc3..9c717a0518 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -526,7 +526,6 @@ static const char *gdscript_function_renames[][2] = { { "set_tangent", "surface_set_tangent" }, // ImmediateGeometry broke SurfaceTool { "set_text_align", "set_text_alignment" }, // Button { "set_timer_process_mode", "set_timer_process_callback" }, // Timer - { "set_tonemap_auto_exposure", "set_tonemap_auto_exposure_enabled" }, // Environment { "set_translation", "set_position" }, // Node3D - this broke GLTFNode which is used rarely { "set_unit_offset", "set_progress_ratio" }, // PathFollow2D, PathFollow3D { "set_uv2", "surface_set_uv2" }, // ImmediateMesh broke Surffacetool @@ -1254,12 +1253,12 @@ static const char *project_settings_renames[][2] = { { "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" }, { "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" }, { "rendering/quality/shading/force_vertex_shading.mobile", "rendering/shading/overrides/force_vertex_shading.mobile" }, - { "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/shadows/shadow_atlas/quadrant_0_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/shadows/shadow_atlas/quadrant_1_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/shadows/shadow_atlas/quadrant_2_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/shadows/shadow_atlas/quadrant_3_subdiv" }, - { "rendering/quality/shadow_atlas/size", "rendering/shadows/shadow_atlas/size" }, - { "rendering/quality/shadow_atlas/size.mobile", "rendering/shadows/shadow_atlas/size.mobile" }, + { "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_0_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_1_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_2_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_3_subdiv" }, + { "rendering/quality/shadow_atlas/size", "rendering/lights_and_shadows/shadow_atlas/size" }, + { "rendering/quality/shadow_atlas/size.mobile", "rendering/lights_and_shadows/shadow_atlas/size.mobile" }, { "rendering/vram_compression/import_bptc", "rendering/textures/vram_compression/import_bptc" }, { "rendering/vram_compression/import_etc", "rendering/textures/vram_compression/import_etc" }, { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2" }, diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 9909ab378a..ed8c7b14c8 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1321,7 +1321,7 @@ void ProjectList::update_dock_menu() { } favs_added = 0; } - DisplayServer::get_singleton()->global_menu_add_item("_dock", _projects[i].project_name + " ( " + _projects[i].path + " )", callable_mp(this, &ProjectList::_global_menu_open_project), i); + DisplayServer::get_singleton()->global_menu_add_item("_dock", _projects[i].project_name + " ( " + _projects[i].path + " )", callable_mp(this, &ProjectList::_global_menu_open_project), Callable(), i); total_added++; } } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 88ca371fac..c120468ecb 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -386,10 +386,18 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { Ref<Script> script = p_node->get_script(); if (!script.is_null()) { - item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + script->get_path()); + String additional_notes; + // Can't set tooltip after adding button, need to do it before. + if (script->is_tool()) { + additional_notes += "\n" + TTR("This script is currently running in the editor."); + } + item->add_button(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + script->get_path() + additional_notes); if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == script) { item->set_button_color(0, item->get_button_count(0) - 1, Color(1, 1, 1, 0.5)); } + if (script->is_tool()) { + item->set_button_color(0, item->get_button_count(0) - 1, get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + } } if (p_node->is_class("CanvasItem")) { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 02922086f0..afb59b486c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -318,8 +318,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l Color col = Color(); if (global_functions.has(word)) { // "assert" and "preload" are reserved, so highlight even if not followed by a bracket. - if (word == "assert" || word == "preload" || str[to] == '(') { + if (word == "assert" || word == "preload") { col = global_function_color; + } else { + // For other global functions, check if followed by bracket. + int k = to; + while (k < line_length && is_whitespace(str[k])) { + k++; + } + + if (str[k] == '(') { + col = global_function_color; + } } } else if (keywords.has(word)) { col = keywords[word]; @@ -668,14 +678,14 @@ void GDScriptSyntaxHighlighter::_update_cache() { if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) { function_definition_color = Color(0.4, 0.9, 1.0); - global_function_color = Color(0.6, 0.6, 0.9); + global_function_color = Color(0.64, 0.64, 0.96); node_path_color = Color(0.72, 0.77, 0.49); node_ref_color = Color(0.39, 0.76, 0.35); annotation_color = Color(1.0, 0.7, 0.45); string_name_color = Color(1.0, 0.76, 0.65); } else { function_definition_color = Color(0, 0.6, 0.6); - global_function_color = Color(0.4, 0.2, 0.8); + global_function_color = Color(0.36, 0.18, 0.72); node_path_color = Color(0.18, 0.55, 0); node_ref_color = Color(0.0, 0.5, 0); annotation_color = Color(0.8, 0.37, 0); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index c18412bc63..4c908166df 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2031,8 +2031,13 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, r_type.type.kind = GDScriptParser::DataType::NATIVE; r_type.type.native_type = p_identifier; r_type.type.is_constant = true; - r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(p_identifier); - r_type.value = Variant(); + if (Engine::get_singleton()->has_singleton(p_identifier)) { + r_type.type.is_meta_type = false; + r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); + } else { + r_type.type.is_meta_type = true; + r_type.value = Variant(); + } } return false; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 059ca703ab..19a8b59c6f 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -88,6 +88,8 @@ public: // TODO: Re-add compiled GDScript on export. return; } + + virtual String _get_name() const override { return "GDScript"; } }; static void _editor_init() { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 0ed212e21f..bb83064446 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -435,7 +435,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { node["scale"] = _vec3_to_arr(n->scale); } - if (!n->position.is_equal_approx(Vector3())) { + if (!n->position.is_zero_approx()) { node["translation"] = _vec3_to_arr(n->position); } if (n->children.size()) { diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index f207d4a741..8486e2c58b 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -638,7 +638,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) { NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh); NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform); - NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + if (is_inside_tree()) { + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + } nm.region = region; // add navigation debugmesh visual instances if debug is enabled @@ -661,6 +663,12 @@ bool GridMap::_octant_update(const OctantKey &p_key) { } } +#ifdef DEBUG_ENABLED + if (bake_navigation) { + _update_octant_navigation_debug_edge_connections_mesh(p_key); + } +#endif // DEBUG_ENABLED + //update multimeshes, only if not baked if (baked_meshes.size() == 0) { for (const KeyValue<int, List<Pair<Transform3D, IndexKey>>> &E : multimesh_items) { @@ -755,6 +763,19 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { } } } + +#ifdef DEBUG_ENABLED + if (bake_navigation) { + if (!g.navigation_debug_edge_connections_instance.is_valid()) { + g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); + } + if (!g.navigation_debug_edge_connections_mesh.is_valid()) { + g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + _update_octant_navigation_debug_edge_connections_mesh(p_key); + } +#endif // DEBUG_ENABLED } } @@ -782,6 +803,18 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) { F.value.navmesh_debug_instance = RID(); } } + +#ifdef DEBUG_ENABLED + if (bake_navigation) { + if (g.navigation_debug_edge_connections_instance.is_valid()) { + RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_instance); + g.navigation_debug_edge_connections_instance = RID(); + } + if (g.navigation_debug_edge_connections_mesh.is_valid()) { + RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid()); + } + } +#endif // DEBUG_ENABLED } void GridMap::_octant_clean_up(const OctantKey &p_key) { @@ -808,6 +841,18 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) { } g.navmesh_ids.clear(); +#ifdef DEBUG_ENABLED + if (bake_navigation) { + if (g.navigation_debug_edge_connections_instance.is_valid()) { + RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_instance); + g.navigation_debug_edge_connections_instance = RID(); + } + if (g.navigation_debug_edge_connections_mesh.is_valid()) { + RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid()); + } + } +#endif // DEBUG_ENABLED + //erase multimeshes for (int i = 0; i < g.multimesh_instances.size(); i++) { @@ -832,6 +877,14 @@ void GridMap::_notification(int p_what) { } } break; +#ifdef DEBUG_ENABLED + case NOTIFICATION_ENTER_TREE: { + if (bake_navigation && NavigationServer3D::get_singleton()->get_debug_enabled()) { + _update_navigation_debug_edge_connections(); + } + } break; +#endif // DEBUG_ENABLED + case NOTIFICATION_TRANSFORM_CHANGED: { Transform3D new_xform = get_global_transform(); if (new_xform == last_transform) { @@ -1231,12 +1284,136 @@ RID GridMap::get_bake_mesh_instance(int p_idx) { GridMap::GridMap() { set_notify_transform(true); +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->connect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed)); + NavigationServer3D::get_singleton_mut()->connect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections)); +#endif // DEBUG_ENABLED +} + +#ifdef DEBUG_ENABLED +void GridMap::_update_navigation_debug_edge_connections() { + if (bake_navigation) { + for (const KeyValue<OctantKey, Octant *> &E : octant_map) { + _update_octant_navigation_debug_edge_connections_mesh(E.key); + } + } } +void GridMap::_navigation_map_changed(RID p_map) { + if (bake_navigation && is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) { + _update_navigation_debug_edge_connections(); + } +} +#endif // DEBUG_ENABLED + GridMap::~GridMap() { if (!mesh_library.is_null()) { mesh_library->unregister_owner(this); } clear(); +#ifdef DEBUG_ENABLED + NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed)); + NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections)); +#endif // DEBUG_ENABLED +} + +#ifdef DEBUG_ENABLED +void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const OctantKey &p_key) { + ERR_FAIL_COND(!octant_map.has(p_key)); + Octant &g = *octant_map[p_key]; + + if (!NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (g.navigation_debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false); + } + return; + } + + if (!is_inside_tree()) { + return; + } + + if (!bake_navigation) { + if (g.navigation_debug_edge_connections_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false); + } + return; + } + + if (!g.navigation_debug_edge_connections_instance.is_valid()) { + g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!g.navigation_debug_edge_connections_mesh.is_valid()) { + g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + g.navigation_debug_edge_connections_mesh->clear_surfaces(); + + float edge_connection_margin = NavigationServer3D::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map()); + float half_edge_connection_margin = edge_connection_margin * 0.5; + + Vector<Vector3> vertex_array; + + for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + if (cell_map.has(F.key) && F.value.region.is_valid()) { + int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(F.value.region); + if (connections_count == 0) { + continue; + } + + for (int i = 0; i < connections_count; i++) { + Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(F.value.region, i); + Vector3 connection_pathway_end = NavigationServer3D::get_singleton()->region_get_connection_pathway_end(F.value.region, i); + + Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end); + Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start); + + Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0)); + Vector3 start_left_dir = -start_right_dir; + + Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0)); + Vector3 end_left_dir = -end_right_dir; + + Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin); + Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin); + Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin); + Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin); + + vertex_array.push_back(right_end_pos); + vertex_array.push_back(left_start_pos); + vertex_array.push_back(right_start_pos); + + vertex_array.push_back(left_end_pos); + vertex_array.push_back(right_end_pos); + vertex_array.push_back(right_start_pos); + } + } + } + + if (vertex_array.size() == 0) { + return; + } + + Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_edge_connections_material(); + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; + + g.navigation_debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array); + g.navigation_debug_edge_connections_mesh->surface_set_material(0, edge_connections_material); + + RS::get_singleton()->instance_set_base(g.navigation_debug_edge_connections_instance, g.navigation_debug_edge_connections_mesh->get_rid()); + RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, is_visible_in_tree()); + if (is_inside_tree()) { + RS::get_singleton()->instance_set_scenario(g.navigation_debug_edge_connections_instance, get_world_3d()->get_scenario()); + } + + bool enable_edge_connections = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections(); + if (!enable_edge_connections) { + RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false); + } } +#endif // DEBUG_ENABLED diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 0ed4695fb9..f83ce68b09 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -117,6 +117,10 @@ class GridMap : public Node3D { HashSet<IndexKey> cells; RID collision_debug; RID collision_debug_instance; +#ifdef DEBUG_ENABLED + RID navigation_debug_edge_connections_instance; + Ref<ArrayMesh> navigation_debug_edge_connections_mesh; +#endif // DEBUG_ENABLED bool dirty = false; RID static_body; @@ -186,6 +190,11 @@ class GridMap : public Node3D { bool _octant_update(const OctantKey &p_key); void _octant_clean_up(const OctantKey &p_key); void _octant_transform(const OctantKey &p_key); +#ifdef DEBUG_ENABLED + void _update_octant_navigation_debug_edge_connections_mesh(const OctantKey &p_key); + void _navigation_map_changed(RID p_map); + void _update_navigation_debug_edge_connections(); +#endif // DEBUG_ENABLED bool awaiting_update = false; void _queue_octants_dirty(); diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 2dcf644a06..5b039e65c0 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -670,7 +670,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade return BAKE_OK; } -LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata) { +LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { if (p_step_function) { p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); } @@ -1165,6 +1165,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1); + push_constant.environment_xform[11] = p_exposure_normalization; + for (int i = 0; i < atlas_slices; i++) { push_constant.atlas_slice = i; rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); @@ -1172,6 +1174,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d //no barrier, let them run all together } rd->compute_list_end(); //done + + push_constant.environment_xform[11] = 0.0; } #ifdef DEBUG_TEXTURES diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index bf6b4399ca..b33a475dbc 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -241,7 +241,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; virtual void add_probe(const Vector3 &p_position) override; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; int get_bake_texture_count() const override; Ref<Image> get_bake_texture(int p_index) const override; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index efa6cd50b4..c2557dfed3 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -434,6 +434,7 @@ void main() { imageStore(primary_dynamic, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0)); dynamic_light += static_light * albedo; //send for bounces + dynamic_light *= params.env_transform[2][3]; // exposure_normalization imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0)); #ifdef USE_SH_LIGHTMAPS @@ -444,6 +445,7 @@ void main() { imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]); #else + static_light *= params.env_transform[2][3]; // exposure_normalization imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(static_light, 1.0)); #endif diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs index a1667dbb8f..e43a3469ae 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs @@ -7,7 +7,7 @@ namespace Godot.SourceGenerators.Sample private NodePath _nodePath; private int _velocity; - public override void _Process(float delta) + public override void _Process(double delta) { _ = delta; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index 180cc3cf14..ad4fce8daa 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -117,13 +117,13 @@ namespace GodotTools.Build } } - private void IssueActivated(int idx) + private void IssueActivated(long idx) { if (idx < 0 || idx >= _issuesList.ItemCount) throw new ArgumentOutOfRangeException(nameof(idx), "Item list index out of range."); // Get correct issue idx from issue list - int issueIndex = (int)_issuesList.GetItemMetadata(idx); + int issueIndex = (int)_issuesList.GetItemMetadata((int)idx); if (issueIndex < 0 || issueIndex >= _issues.Count) throw new InvalidOperationException("Issue index out of range."); @@ -311,7 +311,7 @@ namespace GodotTools.Build Copy } - private void IssuesListContextOptionPressed(int id) + private void IssuesListContextOptionPressed(long id) { switch ((IssuesContextMenuOption)id) { @@ -336,9 +336,9 @@ namespace GodotTools.Build } } - private void IssuesListClicked(int index, Vector2 atPosition, int mouseButtonIndex) + private void IssuesListClicked(long index, Vector2 atPosition, long mouseButtonIndex) { - if (mouseButtonIndex != (int)MouseButton.Right) + if (mouseButtonIndex != (long)MouseButton.Right) { return; } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index d05995c495..4041026426 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -93,7 +93,7 @@ namespace GodotTools.Build private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed; - private void BuildMenuOptionPressed(int id) + private void BuildMenuOptionPressed(long id) { switch ((BuildMenuOptions)id) { @@ -175,7 +175,7 @@ namespace GodotTools.Build AddChild(BuildOutputView); } - public override void _Notification(int what) + public override void _Notification(long what) { base._Notification(what); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 851a8b3689..0d2bea2363 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -67,7 +67,7 @@ namespace GodotTools.Export } } - public override void _ExportBegin(string[] features, bool isDebug, string path, int flags) + public override void _ExportBegin(string[] features, bool isDebug, string path, long flags) { base._ExportBegin(features, isDebug, path, flags); @@ -90,7 +90,7 @@ namespace GodotTools.Export } } - private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags) + private void _ExportBeginImpl(string[] features, bool isDebug, string path, long flags) { _ = flags; // Unused diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 994c11fa72..1cfaea3ec9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -111,7 +111,7 @@ namespace GodotTools _toolBarBuildButton.Show(); } - private void _MenuOptionPressed(int id) + private void _MenuOptionPressed(long id) { switch ((MenuOptions)id) { diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs index 260d13a714..89ac8058b9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs +++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs @@ -9,7 +9,7 @@ namespace GodotTools { private Timer _watchTimer; - public override void _Notification(int what) + public override void _Notification(long what) { if (what == Node.NotificationWmWindowFocusIn) { diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 0e496454a1..15a40c8ca5 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2585,6 +2585,16 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface return params; } +StringName BindingsGenerator::_get_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta) { + if (p_type == Variant::INT) { + return _get_int_type_name_from_meta(p_meta); + } else if (p_type == Variant::FLOAT) { + return _get_float_type_name_from_meta(p_meta); + } else { + return Variant::get_type_name(p_type); + } +} + StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { switch (p_meta) { case GodotTypeInfo::METADATA_INT_IS_INT8: @@ -2612,8 +2622,8 @@ StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metada return "ulong"; break; default: - // Assume INT32 - return "int"; + // Assume INT64 + return "long"; } } @@ -2626,12 +2636,8 @@ StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Meta return "double"; break; default: - // Assume real_t (float or double depending of REAL_T_IS_DOUBLE) -#ifdef REAL_T_IS_DOUBLE + // Assume FLOAT64 return "double"; -#else - return "float"; -#endif } } @@ -2922,13 +2928,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (return_info.type == Variant::NIL) { imethod.return_type.cname = name_cache.type_void; } else { - if (return_info.type == Variant::INT) { - imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); - } else if (return_info.type == Variant::FLOAT) { - imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); - } else { - imethod.return_type.cname = Variant::get_type_name(return_info.type); - } + imethod.return_type.cname = _get_type_name_from_meta(return_info.type, m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); } for (int i = 0; i < argc; i++) { @@ -2952,13 +2952,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - if (arginfo.type == Variant::INT) { - iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); - } else if (arginfo.type == Variant::FLOAT) { - iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); - } else { - iarg.type.cname = Variant::get_type_name(arginfo.type); - } + iarg.type.cname = _get_type_name_from_meta(arginfo.type, m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); @@ -3060,13 +3054,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - if (arginfo.type == Variant::INT) { - iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE); - } else if (arginfo.type == Variant::FLOAT) { - iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE); - } else { - iarg.type.cname = Variant::get_type_name(arginfo.type); - } + iarg.type.cname = _get_type_name_from_meta(arginfo.type, GodotTypeInfo::METADATA_NONE); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index c1295385dc..a479c44368 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -703,6 +703,7 @@ class BindingsGenerator { const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters); + StringName _get_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta); StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); diff --git a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs index 1f5ea7532d..fbad482cf6 100644 --- a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs @@ -11,13 +11,13 @@ public partial class _CLASS_ : _BASE_ // Get the gravity from the project settings to be synced with RigidBody nodes. public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle(); - public override void _PhysicsProcess(float delta) + public override void _PhysicsProcess(double delta) { Vector2 velocity = Velocity; // Add the gravity. if (!IsOnFloor()) - velocity.y += gravity * delta; + velocity.y += gravity * (float)delta; // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) diff --git a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs index 4e978b7549..abed246a1e 100644 --- a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs +++ b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs @@ -11,17 +11,17 @@ public partial class _CLASS_ : _BASE_ // Get the gravity from the project settings to be synced with RigidBody nodes. public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle(); - public override void _PhysicsProcess(float delta) + public override void _PhysicsProcess(double delta) { Vector3 velocity = Velocity; // Add the gravity. if (!IsOnFloor()) - velocity.y -= gravity * delta; + velocity.y -= gravity * (float)delta; // Handle Jump. if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) - velocity.y = JumpVelocity; + velocity.y = JumpVelocity; // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. diff --git a/modules/mono/editor/script_templates/Node/default.cs b/modules/mono/editor/script_templates/Node/default.cs index 4c86d1666f..74ece028fc 100644 --- a/modules/mono/editor/script_templates/Node/default.cs +++ b/modules/mono/editor/script_templates/Node/default.cs @@ -11,7 +11,7 @@ public partial class _CLASS_ : _BASE_ } // Called every frame. 'delta' is the elapsed time since the previous frame. - public override void _Process(float delta) + public override void _Process(double delta) { } } diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs index bb482e0d6a..cd335934db 100644 --- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs +++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs @@ -20,37 +20,37 @@ public partial class VisualShaderNode_CLASS_ : _BASE_ return ""; } - public override int _GetReturnIconType() + public override long _GetReturnIconType() { return 0; } - public override int _GetInputPortCount() + public override long _GetInputPortCount() { return 0; } - public override string _GetInputPortName(int port) + public override string _GetInputPortName(long port) { return ""; } - public override int _GetInputPortType(int port) + public override long _GetInputPortType(long port) { return 0; } - public override int _GetOutputPortCount() + public override long _GetOutputPortCount() { return 1; } - public override string _GetOutputPortName(int port) + public override string _GetOutputPortName(long port) { return "result"; } - public override int _GetOutputPortType(int port) + public override long _GetOutputPortType(long port) { return 0; } diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 4191e46f62..9e5d666a51 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -210,6 +210,20 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const { return map->get_edge_connection_margin(); } +COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius) { + NavMap *map = map_owner.get_or_null(p_map); + ERR_FAIL_COND(map == nullptr); + + map->set_link_connection_radius(p_connection_radius); +} + +real_t GodotNavigationServer::map_get_link_connection_radius(RID p_map) const { + const NavMap *map = map_owner.get_or_null(p_map); + ERR_FAIL_COND_V(map == nullptr, 0); + + return map->get_link_connection_radius(); +} + Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const { const NavMap *map = map_owner.get_or_null(p_map); ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>()); @@ -245,6 +259,20 @@ RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 return map->get_closest_point_owner(p_point); } +TypedArray<RID> GodotNavigationServer::map_get_links(RID p_map) const { + TypedArray<RID> link_rids; + const NavMap *map = map_owner.get_or_null(p_map); + ERR_FAIL_COND_V(map == nullptr, link_rids); + + const LocalVector<NavLink *> links = map->get_links(); + link_rids.resize(links.size()); + + for (uint32_t i = 0; i < links.size(); i++) { + link_rids[i] = links[i]->get_self(); + } + return link_rids; +} + TypedArray<RID> GodotNavigationServer::map_get_regions(RID p_map) const { TypedArray<RID> regions_rids; const NavMap *map = map_owner.get_or_null(p_map); @@ -417,6 +445,131 @@ Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, i return region->get_connection_pathway_end(p_connection_id); } +RID GodotNavigationServer::link_create() const { + GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this); + MutexLock lock(mut_this->operations_mutex); + RID rid = link_owner.make_rid(); + NavLink *link = link_owner.get_or_null(rid); + link->set_self(rid); + return rid; +} + +COMMAND_2(link_set_map, RID, p_link, RID, p_map) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + if (link->get_map() != nullptr) { + if (link->get_map()->get_self() == p_map) { + return; // Pointless + } + + link->get_map()->remove_link(link); + link->set_map(nullptr); + } + + if (p_map.is_valid()) { + NavMap *map = map_owner.get_or_null(p_map); + ERR_FAIL_COND(map == nullptr); + + map->add_link(link); + link->set_map(map); + } +} + +RID GodotNavigationServer::link_get_map(const RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, RID()); + + if (link->get_map()) { + return link->get_map()->get_self(); + } + return RID(); +} + +COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_bidirectional(p_bidirectional); +} + +bool GodotNavigationServer::link_is_bidirectional(RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, false); + + return link->is_bidirectional(); +} + +COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_navigation_layers(p_navigation_layers); +} + +uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, 0); + + return link->get_navigation_layers(); +} + +COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_start_location(p_location); +} + +Vector3 GodotNavigationServer::link_get_start_location(RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, Vector3()); + + return link->get_start_location(); +} + +COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_end_location(p_location); +} + +Vector3 GodotNavigationServer::link_get_end_location(RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, Vector3()); + + return link->get_end_location(); +} + +COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_enter_cost(p_enter_cost); +} + +real_t GodotNavigationServer::link_get_enter_cost(const RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, 0); + + return link->get_enter_cost(); +} + +COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost) { + NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND(link == nullptr); + + link->set_travel_cost(p_travel_cost); +} + +real_t GodotNavigationServer::link_get_travel_cost(const RID p_link) const { + const NavLink *link = link_owner.get_or_null(p_link); + ERR_FAIL_COND_V(link == nullptr, 0); + + return link->get_travel_cost(); +} + RID GodotNavigationServer::agent_create() const { GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this); MutexLock lock(mut_this->operations_mutex); @@ -549,6 +702,13 @@ COMMAND_1(free, RID, p_object) { regions[i]->set_map(nullptr); } + // Removes any assigned links + LocalVector<NavLink *> links = map->get_links(); + for (uint32_t i = 0; i < links.size(); i++) { + map->remove_link(links[i]); + links[i]->set_map(nullptr); + } + // Remove any assigned agent LocalVector<RvoAgent *> agents = map->get_agents(); for (uint32_t i = 0; i < agents.size(); i++) { @@ -572,6 +732,17 @@ COMMAND_1(free, RID, p_object) { region_owner.free(p_object); + } else if (link_owner.owns(p_object)) { + NavLink *link = link_owner.get_or_null(p_object); + + // Removes this link from the map if assigned + if (link->get_map() != nullptr) { + link->get_map()->remove_link(link); + link->set_map(nullptr); + } + + link_owner.free(p_object); + } else if (agent_owner.owns(p_object)) { RvoAgent *agent = agent_owner.get_or_null(p_object); diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 05ba46ede1..e6ef7e3bb1 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -36,6 +36,7 @@ #include "core/templates/rid_owner.h" #include "servers/navigation_server_3d.h" +#include "nav_link.h" #include "nav_map.h" #include "nav_region.h" #include "rvo_agent.h" @@ -71,6 +72,7 @@ class GodotNavigationServer : public NavigationServer3D { LocalVector<SetCommand *> commands; + mutable RID_Owner<NavLink> link_owner; mutable RID_Owner<NavMap> map_owner; mutable RID_Owner<NavRegion> region_owner; mutable RID_Owner<RvoAgent> agent_owner; @@ -100,6 +102,9 @@ public: COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin); virtual real_t map_get_edge_connection_margin(RID p_map) const override; + COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius); + virtual real_t map_get_link_connection_radius(RID p_map) const override; + virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override; virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override; @@ -107,6 +112,7 @@ public: virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override; virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override; + virtual TypedArray<RID> map_get_links(RID p_map) const override; virtual TypedArray<RID> map_get_regions(RID p_map) const override; virtual TypedArray<RID> map_get_agents(RID p_map) const override; @@ -132,6 +138,22 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual RID link_create() const override; + COMMAND_2(link_set_map, RID, p_link, RID, p_map); + virtual RID link_get_map(RID p_link) const override; + COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional); + virtual bool link_is_bidirectional(RID p_link) const override; + COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers); + virtual uint32_t link_get_navigation_layers(RID p_link) const override; + COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location); + virtual Vector3 link_get_start_location(RID p_link) const override; + COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location); + virtual Vector3 link_get_end_location(RID p_link) const override; + COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost); + virtual real_t link_get_enter_cost(RID p_link) const override; + COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost); + virtual real_t link_get_travel_cost(RID p_link) const override; + virtual RID agent_create() const override; COMMAND_2(agent_set_map, RID, p_agent, RID, p_map); virtual RID agent_get_map(RID p_agent) const override; diff --git a/modules/navigation/nav_base.h b/modules/navigation/nav_base.h new file mode 100644 index 0000000000..6dfaaf9af4 --- /dev/null +++ b/modules/navigation/nav_base.h @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* nav_base.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAV_BASE_H +#define NAV_BASE_H + +#include "nav_rid.h" +#include "nav_utils.h" + +class NavMap; + +class NavBase : public NavRid { +protected: + uint32_t navigation_layers = 1; + float enter_cost = 0.0; + float travel_cost = 1.0; + +public: + void set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; } + uint32_t get_navigation_layers() const { return navigation_layers; } + + void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); } + float get_enter_cost() const { return enter_cost; } + + void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); } + float get_travel_cost() const { return travel_cost; } +}; + +#endif // NAV_BASE_H diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp new file mode 100644 index 0000000000..828b131ec6 --- /dev/null +++ b/modules/navigation/nav_link.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* nav_link.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "nav_link.h" + +#include "nav_map.h" + +void NavLink::set_map(NavMap *p_map) { + map = p_map; + link_dirty = true; +} + +void NavLink::set_bidirectional(bool p_bidirectional) { + bidirectional = p_bidirectional; + link_dirty = true; +} + +void NavLink::set_start_location(const Vector3 p_location) { + start_location = p_location; + link_dirty = true; +} + +void NavLink::set_end_location(const Vector3 p_location) { + end_location = p_location; + link_dirty = true; +} + +bool NavLink::check_dirty() { + const bool was_dirty = link_dirty; + + link_dirty = false; + return was_dirty; +} diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h new file mode 100644 index 0000000000..8d57f076c0 --- /dev/null +++ b/modules/navigation/nav_link.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* nav_link.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAV_LINK_H +#define NAV_LINK_H + +#include "nav_base.h" +#include "nav_utils.h" + +class NavLink : public NavBase { + NavMap *map = nullptr; + bool bidirectional = true; + Vector3 start_location = Vector3(); + Vector3 end_location = Vector3(); + + bool link_dirty = true; + +public: + void set_map(NavMap *p_map); + NavMap *get_map() const { + return map; + } + + void set_bidirectional(bool p_bidirectional); + bool is_bidirectional() const { + return bidirectional; + } + + void set_start_location(Vector3 p_location); + Vector3 get_start_location() const { + return start_location; + } + + void set_end_location(Vector3 p_location); + Vector3 get_end_location() const { + return end_location; + } + + bool check_dirty(); +}; + +#endif // NAV_LINK_H diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 49029b5513..100db9bc82 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -31,6 +31,7 @@ #include "nav_map.h" #include "core/object/worker_thread_pool.h" +#include "nav_link.h" #include "nav_region.h" #include "rvo_agent.h" #include <algorithm> @@ -52,6 +53,11 @@ void NavMap::set_edge_connection_margin(float p_edge_connection_margin) { regenerate_links = true; } +void NavMap::set_link_connection_radius(float p_link_connection_radius) { + link_connection_radius = p_link_connection_radius; + regenerate_links = true; +} + gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const { const int x = int(Math::floor(p_pos.x / cell_size)); const int y = int(Math::floor(p_pos.y / cell_size)); @@ -158,17 +164,17 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p continue; } - float region_enter_cost = 0.0; - float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost(); + float poly_enter_cost = 0.0; + float poly_travel_cost = least_cost_poly->poly->owner->get_travel_cost(); - if (prev_least_cost_poly != nullptr && !(prev_least_cost_poly->poly->owner->get_self() == least_cost_poly->poly->owner->get_self())) { - region_enter_cost = least_cost_poly->poly->owner->get_enter_cost(); + if (prev_least_cost_poly != nullptr && (prev_least_cost_poly->poly->owner->get_self() != least_cost_poly->poly->owner->get_self())) { + poly_enter_cost = least_cost_poly->poly->owner->get_enter_cost(); } prev_least_cost_poly = least_cost_poly; Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end }; const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway); - const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance; + const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * poly_travel_cost) + poly_enter_cost + least_cost_poly->traveled_distance; int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon)); @@ -360,10 +366,15 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p // Add mid points int np_id = least_cost_id; while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -1) { - int prev = navigation_polys[np_id].back_navigation_edge; - int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); - Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; - path.push_back(point); + if (navigation_polys[np_id].back_navigation_edge != -1) { + int prev = navigation_polys[np_id].back_navigation_edge; + int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); + Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; + path.push_back(point); + } else { + path.push_back(navigation_polys[np_id].entry); + } + np_id = navigation_polys[np_id].back_navigation_poly_id; } @@ -475,6 +486,19 @@ void NavMap::remove_region(NavRegion *p_region) { } } +void NavMap::add_link(NavLink *p_link) { + links.push_back(p_link); + regenerate_links = true; +} + +void NavMap::remove_link(NavLink *p_link) { + int64_t link_index = links.find(p_link); + if (link_index != -1) { + links.remove_at_unordered(link_index); + regenerate_links = true; + } +} + bool NavMap::has_agent(RvoAgent *agent) const { return (agents.find(agent) != -1); } @@ -526,6 +550,12 @@ void NavMap::sync() { } } + for (uint32_t l = 0; l < links.size(); l++) { + if (links[l]->check_dirty()) { + regenerate_links = true; + } + } + if (regenerate_links) { // Remove regions connections. for (uint32_t r = 0; r < regions.size(); r++) { @@ -651,7 +681,121 @@ void NavMap::sync() { free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection); // Add the connection to the region_connection map. - free_edge.polygon->owner->get_connections().push_back(new_connection); + ((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection); + } + } + + uint32_t link_poly_idx = 0; + link_polygons.resize(links.size()); + + // Search for polygons within range of a nav link. + for (uint32_t l = 0; l < links.size(); l++) { + const NavLink *link = links[l]; + const Vector3 start = link->get_start_location(); + const Vector3 end = link->get_end_location(); + + gd::Polygon *closest_start_polygon = nullptr; + real_t closest_start_distance = link_connection_radius; + Vector3 closest_start_point; + + gd::Polygon *closest_end_polygon = nullptr; + real_t closest_end_distance = link_connection_radius; + Vector3 closest_end_point; + + // Create link to any polygons within the search radius of the start point. + for (uint32_t start_index = 0; start_index < polygons.size(); start_index++) { + gd::Polygon &start_poly = polygons[start_index]; + + // For each face check the distance to the start + for (uint32_t start_point_id = 2; start_point_id < start_poly.points.size(); start_point_id += 1) { + const Face3 start_face(start_poly.points[0].pos, start_poly.points[start_point_id - 1].pos, start_poly.points[start_point_id].pos); + const Vector3 start_point = start_face.get_closest_point_to(start); + const real_t start_distance = start_point.distance_to(start); + + // Pick the polygon that is within our radius and is closer than anything we've seen yet. + if (start_distance <= link_connection_radius && start_distance < closest_start_distance) { + closest_start_distance = start_distance; + closest_start_point = start_point; + closest_start_polygon = &start_poly; + } + } + } + + // Find any polygons within the search radius of the end point. + for (uint32_t end_index = 0; end_index < polygons.size(); end_index++) { + gd::Polygon &end_poly = polygons[end_index]; + + // For each face check the distance to the end + for (uint32_t end_point_id = 2; end_point_id < end_poly.points.size(); end_point_id += 1) { + const Face3 end_face(end_poly.points[0].pos, end_poly.points[end_point_id - 1].pos, end_poly.points[end_point_id].pos); + const Vector3 end_point = end_face.get_closest_point_to(end); + const real_t end_distance = end_point.distance_to(end); + + // Pick the polygon that is within our radius and is closer than anything we've seen yet. + if (end_distance <= link_connection_radius && end_distance < closest_end_distance) { + closest_end_distance = end_distance; + closest_end_point = end_point; + closest_end_polygon = &end_poly; + } + } + } + + // If we have both a start and end point, then create a synthetic polygon to route through. + if (closest_start_polygon && closest_end_polygon) { + gd::Polygon &new_polygon = link_polygons[link_poly_idx++]; + new_polygon.owner = link; + + new_polygon.edges.clear(); + new_polygon.edges.resize(4); + new_polygon.points.clear(); + new_polygon.points.reserve(4); + + // Build a set of vertices that create a thin polygon going from the start to the end point. + new_polygon.points.push_back({ closest_start_point, get_point_key(closest_start_point) }); + new_polygon.points.push_back({ closest_start_point, get_point_key(closest_start_point) }); + new_polygon.points.push_back({ closest_end_point, get_point_key(closest_end_point) }); + new_polygon.points.push_back({ closest_end_point, get_point_key(closest_end_point) }); + + Vector3 center; + for (int p = 0; p < 4; ++p) { + center += new_polygon.points[p].pos; + } + new_polygon.center = center / real_t(new_polygon.points.size()); + new_polygon.clockwise = true; + + // Setup connections to go forward in the link. + { + gd::Edge::Connection entry_connection; + entry_connection.polygon = &new_polygon; + entry_connection.edge = -1; + entry_connection.pathway_start = new_polygon.points[0].pos; + entry_connection.pathway_end = new_polygon.points[1].pos; + closest_start_polygon->edges[0].connections.push_back(entry_connection); + + gd::Edge::Connection exit_connection; + exit_connection.polygon = closest_end_polygon; + exit_connection.edge = -1; + exit_connection.pathway_start = new_polygon.points[2].pos; + exit_connection.pathway_end = new_polygon.points[3].pos; + new_polygon.edges[2].connections.push_back(exit_connection); + } + + // If the link is bi-directional, create connections from the end to the start. + if (link->is_bidirectional()) { + gd::Edge::Connection entry_connection; + entry_connection.polygon = &new_polygon; + entry_connection.edge = -1; + entry_connection.pathway_start = new_polygon.points[2].pos; + entry_connection.pathway_end = new_polygon.points[3].pos; + closest_end_polygon->edges[0].connections.push_back(entry_connection); + + gd::Edge::Connection exit_connection; + exit_connection.polygon = closest_start_polygon; + exit_connection.edge = -1; + exit_connection.pathway_start = new_polygon.points[0].pos; + exit_connection.pathway_end = new_polygon.points[1].pos; + new_polygon.edges[0].connections.push_back(exit_connection); + } } } diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index e50a1afbe9..a3da9fa727 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -40,9 +40,9 @@ #include <KdTree.h> +class NavLink; class NavRegion; class RvoAgent; -class NavRegion; class NavMap : public NavRid { /// Map Up @@ -55,11 +55,19 @@ class NavMap : public NavRid { /// This value is used to detect the near edges to connect. real_t edge_connection_margin = 0.25; + /// This value is used to limit how far links search to find polygons to connect to. + real_t link_connection_radius = 1.0; + bool regenerate_polygons = true; bool regenerate_links = true; + /// Map regions LocalVector<NavRegion *> regions; + /// Map links + LocalVector<NavLink *> links; + LocalVector<gd::Polygon> link_polygons; + /// Map polygons LocalVector<gd::Polygon> polygons; @@ -100,6 +108,11 @@ public: return edge_connection_margin; } + void set_link_connection_radius(float p_link_connection_radius); + float get_link_connection_radius() const { + return link_connection_radius; + } + gd::PointKey get_point_key(const Vector3 &p_pos) const; Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; @@ -115,6 +128,12 @@ public: return regions; } + void add_link(NavLink *p_link); + void remove_link(NavLink *p_link); + const LocalVector<NavLink *> &get_links() const { + return links; + } + bool has_agent(RvoAgent *agent) const; void add_agent(RvoAgent *agent); void remove_agent(RvoAgent *agent); diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 88740807eb..d43f53d1c0 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -40,14 +40,6 @@ void NavRegion::set_map(NavMap *p_map) { } } -void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) { - navigation_layers = p_navigation_layers; -} - -uint32_t NavRegion::get_navigation_layers() const { - return navigation_layers; -} - void NavRegion::set_transform(Transform3D p_transform) { transform = p_transform; polygons_dirty = true; diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index c9d2d80f6c..8d2b5aa9eb 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -33,21 +33,13 @@ #include "scene/resources/navigation_mesh.h" -#include "nav_rid.h" +#include "nav_base.h" #include "nav_utils.h" -#include <vector> - -class NavMap; -class NavRegion; - -class NavRegion : public NavRid { +class NavRegion : public NavBase { NavMap *map = nullptr; Transform3D transform; Ref<NavigationMesh> mesh; - uint32_t navigation_layers = 1; - float enter_cost = 0.0; - float travel_cost = 1.0; Vector<gd::Edge::Connection> connections; bool polygons_dirty = true; @@ -67,15 +59,6 @@ public: return map; } - void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); } - float get_enter_cost() const { return enter_cost; } - - void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); } - float get_travel_cost() const { return travel_cost; } - - void set_navigation_layers(uint32_t p_navigation_layers); - uint32_t get_navigation_layers() const; - void set_transform(Transform3D transform); const Transform3D &get_transform() const { return transform; diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 47f04b6a75..16b96dcfe9 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -35,9 +35,8 @@ #include "core/templates/hash_map.h" #include "core/templates/hashfuncs.h" #include "core/templates/local_vector.h" -#include <vector> -class NavRegion; +class NavBase; namespace gd { struct Polygon; @@ -79,26 +78,33 @@ struct Point { }; struct Edge { - /// This edge ID - int this_edge = -1; - /// The gateway in the edge, as, in some case, the whole edge might not be navigable. struct Connection { + /// Polygon that this connection leads to. Polygon *polygon = nullptr; + + /// Edge of the source polygon where this connection starts from. int edge = -1; + + /// Point on the edge where the gateway leading to the poly starts. Vector3 pathway_start; + + /// Point on the edge where the gateway leading to the poly ends. Vector3 pathway_end; }; + + /// Connections from this edge to other polygons. Vector<Connection> connections; }; struct Polygon { - NavRegion *owner = nullptr; + /// Navigation region or link that contains this polygon. + const NavBase *owner = nullptr; /// The points of this `Polygon` LocalVector<Point> points; - /// Are the points clockwise ? + /// Are the points clockwise? bool clockwise; /// The edges of this `Polygon` @@ -115,7 +121,7 @@ struct NavigationPoly { /// Those 4 variables are used to travel the path backwards. int back_navigation_poly_id = -1; - uint32_t back_navigation_edge = UINT32_MAX; + int back_navigation_edge = -1; Vector3 back_navigation_edge_pathway_start; Vector3 back_navigation_edge_pathway_end; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 8f2abda00f..abef48e2a1 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2622,11 +2622,11 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_ } if (fd->msdf) { - return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - return (gl[p_glyph].advance + ea).round(); + return (gl[p_glyph | mod].advance + ea).round(); } else { - return gl[p_glyph].advance + ea; + return gl[p_glyph | mod].advance + ea; } } @@ -2669,9 +2669,9 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.position; + return gl[p_glyph | mod].rect.position; } } @@ -2714,9 +2714,9 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.size; + return gl[p_glyph | mod].rect.size; } } @@ -2757,7 +2757,7 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].uv_rect; + return gl[p_glyph | mod].uv_rect; } void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -2797,7 +2797,7 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].texture_idx; + return gl[p_glyph | mod].texture_idx; } void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -2837,12 +2837,12 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -2856,7 +2856,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); } } @@ -2885,12 +2885,12 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -2904,7 +2904,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); } } diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 8e4a4ff424..359bb056a8 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -1697,11 +1697,11 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_ } if (fd->msdf) { - return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - return (gl[p_glyph].advance + ea).round(); + return (gl[p_glyph | mod].advance + ea).round(); } else { - return gl[p_glyph].advance + ea; + return gl[p_glyph | mod].advance + ea; } } @@ -1744,9 +1744,9 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.position; + return gl[p_glyph | mod].rect.position; } } @@ -1789,9 +1789,9 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.size; + return gl[p_glyph | mod].rect.size; } } @@ -1832,7 +1832,7 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].uv_rect; + return gl[p_glyph | mod].uv_rect; } void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -1872,7 +1872,7 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].texture_idx; + return gl[p_glyph | mod].texture_idx; } void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -1912,12 +1912,12 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -1931,7 +1931,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); } } @@ -1960,12 +1960,12 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -1979,7 +1979,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); } } diff --git a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml index 5387deaa47..a10ea25b8c 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml @@ -48,7 +48,7 @@ </description> </method> <method name="_get_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="r_buffer" type="const uint8_t **" /> <param index="1" name="r_buffer_size" type="int32_t*" /> <description> @@ -60,12 +60,12 @@ </description> </method> <method name="_get_ready_state" qualifiers="virtual const"> - <return type="int" /> + <return type="int" enum="WebRTCDataChannel.ChannelState" /> <description> </description> </method> <method name="_get_write_mode" qualifiers="virtual const"> - <return type="int" /> + <return type="int" enum="WebRTCDataChannel.WriteMode" /> <description> </description> </method> @@ -80,12 +80,12 @@ </description> </method> <method name="_poll" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <description> </description> </method> <method name="_put_packet" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_buffer" type="const uint8_t*" /> <param index="1" name="p_buffer_size" type="int" /> <description> @@ -93,7 +93,7 @@ </method> <method name="_set_write_mode" qualifiers="virtual"> <return type="void" /> - <param index="0" name="p_write_mode" type="int" /> + <param index="0" name="p_write_mode" type="int" enum="WebRTCDataChannel.WriteMode" /> <description> </description> </method> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml index e22e939a66..3c4bf18a76 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml @@ -8,7 +8,7 @@ </tutorials> <methods> <method name="_add_ice_candidate" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_sdp_mid_name" type="String" /> <param index="1" name="p_sdp_mline_index" type="int" /> <param index="2" name="p_sdp_name" type="String" /> @@ -28,35 +28,35 @@ </description> </method> <method name="_create_offer" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <description> </description> </method> <method name="_get_connection_state" qualifiers="virtual const"> - <return type="int" /> + <return type="int" enum="WebRTCPeerConnection.ConnectionState" /> <description> </description> </method> <method name="_initialize" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_config" type="Dictionary" /> <description> </description> </method> <method name="_poll" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <description> </description> </method> <method name="_set_local_description" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_type" type="String" /> <param index="1" name="p_sdp" type="String" /> <description> </description> </method> <method name="_set_remote_description" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="p_type" type="String" /> <param index="1" name="p_sdp" type="String" /> <description> diff --git a/modules/webrtc/webrtc_data_channel_extension.cpp b/modules/webrtc/webrtc_data_channel_extension.cpp index b7ea8d22bb..4e16b77e81 100644 --- a/modules/webrtc/webrtc_data_channel_extension.cpp +++ b/modules/webrtc/webrtc_data_channel_extension.cpp @@ -56,160 +56,20 @@ void WebRTCDataChannelExtension::_bind_methods() { GDVIRTUAL_BIND(_get_buffered_amount); } -int WebRTCDataChannelExtension::get_available_packet_count() const { - int count; - if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { - return count; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_available_packet_count is unimplemented!"); - return -1; -} - Error WebRTCDataChannelExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_packet_native is unimplemented!"); return FAILED; } Error WebRTCDataChannelExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { - return (Error)err; + return err; } WARN_PRINT_ONCE("WebRTCDataChannelExtension::_put_packet_native is unimplemented!"); return FAILED; } - -int WebRTCDataChannelExtension::get_max_packet_size() const { - int size; - if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { - return size; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_size is unimplemented!"); - return 0; -} - -Error WebRTCDataChannelExtension::poll() { - int err; - if (GDVIRTUAL_CALL(_poll, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_poll is unimplemented!"); - return ERR_UNCONFIGURED; -} - -void WebRTCDataChannelExtension::close() { - if (GDVIRTUAL_CALL(_close)) { - return; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_close is unimplemented!"); -} - -void WebRTCDataChannelExtension::set_write_mode(WriteMode p_mode) { - if (GDVIRTUAL_CALL(_set_write_mode, p_mode)) { - return; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_set_write_mode is unimplemented!"); -} - -WebRTCDataChannel::WriteMode WebRTCDataChannelExtension::get_write_mode() const { - int mode; - if (GDVIRTUAL_CALL(_get_write_mode, mode)) { - return (WriteMode)mode; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_write_mode is unimplemented!"); - return WRITE_MODE_BINARY; -} - -bool WebRTCDataChannelExtension::was_string_packet() const { - bool was_string; - if (GDVIRTUAL_CALL(_was_string_packet, was_string)) { - return was_string; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_was_string_packet is unimplemented!"); - return false; -} - -WebRTCDataChannel::ChannelState WebRTCDataChannelExtension::get_ready_state() const { - int state; - if (GDVIRTUAL_CALL(_get_ready_state, state)) { - return (ChannelState)state; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_ready_state is unimplemented!"); - return STATE_CLOSED; -} - -String WebRTCDataChannelExtension::get_label() const { - String label; - if (GDVIRTUAL_CALL(_get_label, label)) { - return label; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_label is unimplemented!"); - return label; -} - -bool WebRTCDataChannelExtension::is_ordered() const { - bool ordered; - if (GDVIRTUAL_CALL(_is_ordered, ordered)) { - return ordered; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_ordered is unimplemented!"); - return false; -} - -int WebRTCDataChannelExtension::get_id() const { - int id; - if (GDVIRTUAL_CALL(_get_id, id)) { - return id; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_id is unimplemented!"); - return -1; -} - -int WebRTCDataChannelExtension::get_max_packet_life_time() const { - int lifetime; - if (GDVIRTUAL_CALL(_get_max_packet_life_time, lifetime)) { - return lifetime; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_life_time is unimplemented!"); - return -1; -} - -int WebRTCDataChannelExtension::get_max_retransmits() const { - int retransmits; - if (GDVIRTUAL_CALL(_get_max_retransmits, retransmits)) { - return retransmits; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_retransmits is unimplemented!"); - return -1; -} - -String WebRTCDataChannelExtension::get_protocol() const { - String protocol; - if (GDVIRTUAL_CALL(_get_protocol, protocol)) { - return protocol; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_protocol is unimplemented!"); - return protocol; -} - -bool WebRTCDataChannelExtension::is_negotiated() const { - bool negotiated; - if (GDVIRTUAL_CALL(_is_negotiated, negotiated)) { - return negotiated; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_negotiated is unimplemented!"); - return false; -} - -int WebRTCDataChannelExtension::get_buffered_amount() const { - int amount; - if (GDVIRTUAL_CALL(_get_buffered_amount, amount)) { - return amount; - } - WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_buffered_amount is unimplemented!"); - return -1; -} diff --git a/modules/webrtc/webrtc_data_channel_extension.h b/modules/webrtc/webrtc_data_channel_extension.h index 83bb627815..467163ed93 100644 --- a/modules/webrtc/webrtc_data_channel_extension.h +++ b/modules/webrtc/webrtc_data_channel_extension.h @@ -33,6 +33,7 @@ #include "webrtc_data_channel.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -44,53 +45,33 @@ protected: static void _bind_methods(); public: - virtual void set_write_mode(WriteMode mode) override; - virtual WriteMode get_write_mode() const override; - virtual bool was_string_packet() const override; + EXBIND0R(Error, poll); + EXBIND0(close); - virtual ChannelState get_ready_state() const override; - virtual String get_label() const override; - virtual bool is_ordered() const override; - virtual int get_id() const override; - virtual int get_max_packet_life_time() const override; - virtual int get_max_retransmits() const override; - virtual String get_protocol() const override; - virtual bool is_negotiated() const override; - virtual int get_buffered_amount() const override; + EXBIND1(set_write_mode, WriteMode); + EXBIND0RC(WriteMode, get_write_mode); - virtual Error poll() override; - virtual void close() override; + EXBIND0RC(bool, was_string_packet); + + EXBIND0RC(ChannelState, get_ready_state); + EXBIND0RC(String, get_label); + EXBIND0RC(bool, is_ordered); + EXBIND0RC(int, get_id); + EXBIND0RC(int, get_max_packet_life_time); + EXBIND0RC(int, get_max_retransmits); + EXBIND0RC(String, get_protocol); + EXBIND0RC(bool, is_negotiated); + EXBIND0RC(int, get_buffered_amount); /** Inherited from PacketPeer: **/ - virtual int get_available_packet_count() const override; + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; - /** GDExtension **/ - GDVIRTUAL0RC(int, _get_available_packet_count); - GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); - GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); - GDVIRTUAL0RC(int, _get_max_packet_size); - - GDVIRTUAL0R(int, _poll); - GDVIRTUAL0(_close); - - GDVIRTUAL1(_set_write_mode, int); - GDVIRTUAL0RC(int, _get_write_mode); - - GDVIRTUAL0RC(bool, _was_string_packet); - - GDVIRTUAL0RC(int, _get_ready_state); - GDVIRTUAL0RC(String, _get_label); - GDVIRTUAL0RC(bool, _is_ordered); - GDVIRTUAL0RC(int, _get_id); - GDVIRTUAL0RC(int, _get_max_packet_life_time); - GDVIRTUAL0RC(int, _get_max_retransmits); - GDVIRTUAL0RC(String, _get_protocol); - GDVIRTUAL0RC(bool, _is_negotiated); - GDVIRTUAL0RC(int, _get_buffered_amount); + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); WebRTCDataChannelExtension() {} }; diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp index 85c04b3b19..54143e4b79 100644 --- a/modules/webrtc/webrtc_peer_connection_extension.cpp +++ b/modules/webrtc/webrtc_peer_connection_extension.cpp @@ -42,24 +42,6 @@ void WebRTCPeerConnectionExtension::_bind_methods() { GDVIRTUAL_BIND(_close); } -WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const { - int state; - if (GDVIRTUAL_CALL(_get_connection_state, state)) { - return (ConnectionState)state; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_get_connection_state is unimplemented!"); - return STATE_DISCONNECTED; -} - -Error WebRTCPeerConnectionExtension::initialize(Dictionary p_config) { - int err; - if (GDVIRTUAL_CALL(_initialize, p_config, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_initialize is unimplemented!"); - return ERR_UNCONFIGURED; -} - Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String p_label, Dictionary p_options) { Object *ret = nullptr; if (GDVIRTUAL_CALL(_create_data_channel, p_label, p_options, ret)) { @@ -70,55 +52,3 @@ Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_data_channel is unimplemented!"); return nullptr; } - -Error WebRTCPeerConnectionExtension::create_offer() { - int err; - if (GDVIRTUAL_CALL(_create_offer, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_offer is unimplemented!"); - return ERR_UNCONFIGURED; -} - -Error WebRTCPeerConnectionExtension::set_local_description(String p_type, String p_sdp) { - int err; - if (GDVIRTUAL_CALL(_set_local_description, p_type, p_sdp, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_local_description is unimplemented!"); - return ERR_UNCONFIGURED; -} - -Error WebRTCPeerConnectionExtension::set_remote_description(String p_type, String p_sdp) { - int err; - if (GDVIRTUAL_CALL(_set_remote_description, p_type, p_sdp, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_remote_description is unimplemented!"); - return ERR_UNCONFIGURED; -} - -Error WebRTCPeerConnectionExtension::add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) { - int err; - if (GDVIRTUAL_CALL(_add_ice_candidate, p_sdp_mid_name, p_sdp_mline_index, p_sdp_name, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_add_ice_candidate is unimplemented!"); - return ERR_UNCONFIGURED; -} - -Error WebRTCPeerConnectionExtension::poll() { - int err; - if (GDVIRTUAL_CALL(_poll, err)) { - return (Error)err; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_poll is unimplemented!"); - return ERR_UNCONFIGURED; -} - -void WebRTCPeerConnectionExtension::close() { - if (GDVIRTUAL_CALL(_close)) { - return; - } - WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_close is unimplemented!"); -} diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h index bde19c173b..0c324ca45f 100644 --- a/modules/webrtc/webrtc_peer_connection_extension.h +++ b/modules/webrtc/webrtc_peer_connection_extension.h @@ -33,6 +33,7 @@ #include "webrtc_peer_connection.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -44,27 +45,21 @@ protected: static void _bind_methods(); public: - virtual ConnectionState get_connection_state() const override; - - virtual Error initialize(Dictionary p_config = Dictionary()) override; + // FIXME Can't be directly exposed due to issues in exchanging Ref(s) between godot and extensions. + // See godot-cpp GH-652 . virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) override; - virtual Error create_offer() override; - virtual Error set_remote_description(String type, String sdp) override; - virtual Error set_local_description(String type, String sdp) override; - virtual Error add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) override; - virtual Error poll() override; - virtual void close() override; + GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary); + // EXBIND2R(Ref<WebRTCDataChannel>, create_data_channel, String, Dictionary); /** GDExtension **/ - GDVIRTUAL0RC(int, _get_connection_state); - GDVIRTUAL1R(int, _initialize, Dictionary); - GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary); - GDVIRTUAL0R(int, _create_offer); - GDVIRTUAL2R(int, _set_remote_description, String, String); - GDVIRTUAL2R(int, _set_local_description, String, String); - GDVIRTUAL3R(int, _add_ice_candidate, String, int, String); - GDVIRTUAL0R(int, _poll); - GDVIRTUAL0(_close); + EXBIND0RC(ConnectionState, get_connection_state); + EXBIND1R(Error, initialize, Dictionary); + EXBIND0R(Error, create_offer); + EXBIND2R(Error, set_remote_description, String, String); + EXBIND2R(Error, set_local_description, String, String); + EXBIND3R(Error, add_ice_candidate, String, int, String); + EXBIND0R(Error, poll); + EXBIND0(close); WebRTCPeerConnectionExtension() {} }; diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 017fc3e3a2..0d619904bc 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -1823,6 +1823,47 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a return retval; } +bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { + const WindowData &wd = windows[p_window]; + + // Using ICCCM -- Inter-Client Communication Conventions Manual + Atom property = XInternAtom(x11_display, "WM_STATE", True); + if (property == None) { + return false; + } + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = nullptr; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 32, + False, + AnyPropertyType, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success && data) { + long *state = (long *)data; + if (state[0] == WM_IconicState) { + XFree(data); + return true; + } + XFree(data); + } + + return false; +} + bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), false); const WindowData &wd = windows[p_window]; @@ -1869,11 +1910,15 @@ bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { return retval; } -void DisplayServerX11::_validate_fullscreen_on_map(WindowID p_window) { +void DisplayServerX11::_validate_mode_on_map(WindowID p_window) { + // Check if we applied any window modes that didn't take effect while unmapped const WindowData &wd = windows[p_window]; if (wd.fullscreen && !_window_fullscreen_check(p_window)) { - // Unmapped fullscreen attempt didn't take effect, redo... _set_wm_fullscreen(p_window, true); + } else if (wd.maximized && !_window_maximize_check(p_window, "_NET_WM_STATE")) { + _set_wm_maximized(p_window, true); + } else if (wd.minimized && !_window_minimize_check(p_window)) { + _set_wm_minimized(p_window, true); } } @@ -1911,6 +1956,37 @@ void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) { usleep(10000); } } + wd.maximized = p_enabled; +} + +void DisplayServerX11::_set_wm_minimized(WindowID p_window, bool p_enabled) { + WindowData &wd = windows[p_window]; + // Using ICCCM -- Inter-Client Communication Conventions Manual + XEvent xev; + Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_change; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = wm_hidden; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + wd.minimized = p_enabled; } void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { @@ -1992,32 +2068,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - //Un-Minimize - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_NormalState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, false); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2046,31 +2097,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_IconicState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, true); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2105,40 +2132,9 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c return WINDOW_MODE_MAXIMIZED; } - { // Test minimized. - // Using ICCCM -- Inter-Client Communication Conventions Manual - Atom property = XInternAtom(x11_display, "WM_STATE", True); - if (property == None) { - return WINDOW_MODE_WINDOWED; - } - - Atom type; - int format; - unsigned long len; - unsigned long remaining; - unsigned char *data = nullptr; - - int result = XGetWindowProperty( - x11_display, - wd.x11_window, - property, - 0, - 32, - False, - AnyPropertyType, - &type, - &format, - &len, - &remaining, - &data); - - if (result == Success && data) { - long *state = (long *)data; - if (state[0] == WM_IconicState) { - XFree(data); - return WINDOW_MODE_MINIMIZED; - } - XFree(data); + { + if (_window_minimize_check(p_window)) { + return WINDOW_MODE_MINIMIZED; } } @@ -3658,9 +3654,6 @@ void DisplayServerX11::process_events() { const WindowData &wd = windows[window_id]; - // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(window_id); - XWindowAttributes xwa; XSync(x11_display, False); XGetWindowAttributes(x11_display, wd.x11_window, &xwa); @@ -3671,6 +3664,9 @@ void DisplayServerX11::process_events() { if ((xwa.map_state == IsViewable) && !wd.no_focus && !wd.is_popup) { XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime); } + + // Have we failed to set fullscreen while the window was unmapped? + _validate_mode_on_map(window_id); } break; case Expose: { @@ -3690,8 +3686,7 @@ void DisplayServerX11::process_events() { case VisibilityNotify: { DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state); - XVisibilityEvent *visibility = (XVisibilityEvent *)&event; - windows[window_id].minimized = (visibility->state == VisibilityFullyObscured); + windows[window_id].minimized = _window_minimize_check(window_id); } break; case LeaveNotify: { @@ -5003,7 +4998,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode _window_changed(&xevent); } else if (xevent.type == MapNotify) { // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(main_window); + _validate_mode_on_map(main_window); } } diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 6b9f57d78f..650598d243 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -152,6 +152,7 @@ class DisplayServerX11 : public DisplayServer { Vector2i last_position_before_fs; bool focused = true; bool minimized = false; + bool maximized = false; bool is_popup = false; Rect2i parent_safe_rect; @@ -268,10 +269,12 @@ class DisplayServerX11 : public DisplayServer { void _update_real_mouse_position(const WindowData &wd); bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const; bool _window_fullscreen_check(WindowID p_window) const; - void _validate_fullscreen_on_map(WindowID p_window); + bool _window_minimize_check(WindowID p_window) const; + void _validate_mode_on_map(WindowID p_window); void _update_size_hints(WindowID p_window); void _set_wm_fullscreen(WindowID p_window, bool p_enabled); void _set_wm_maximized(WindowID p_window, bool p_enabled); + void _set_wm_minimized(WindowID p_window, bool p_enabled); void _update_context(WindowData &wd); diff --git a/platform/macos/SCsub b/platform/macos/SCsub index d0856c709a..bbd461fba9 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -17,6 +17,8 @@ files = [ "godot_window.mm", "key_mapping_macos.mm", "godot_main_macos.mm", + "godot_menu_delegate.mm", + "godot_menu_item.mm", "dir_access_macos.mm", "tts_macos.mm", "joypad_macos.cpp", diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index a08667a259..cd457836de 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -140,6 +140,7 @@ private: int key_event_pos = 0; id tts = nullptr; + id menu_delegate = nullptr; Point2i im_selection; String im_text; @@ -227,14 +228,14 @@ public: virtual bool has_feature(Feature p_feature) const override; virtual String get_name() const override; - virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; + virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; virtual int global_menu_add_separator(const String &p_menu_root, int p_index = -1) override; virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const override; @@ -244,6 +245,7 @@ public: virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const override; virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const override; virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const override; + virtual Callable global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const override; virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const override; virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const override; virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; @@ -259,6 +261,7 @@ public: virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; + virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) override; virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index b06ae9f27c..c117713c2b 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -31,6 +31,7 @@ #include "display_server_macos.h" #include "godot_content_view.h" +#include "godot_menu_delegate.h" #include "godot_menu_item.h" #include "godot_window.h" #include "godot_window_delegate.h" @@ -95,6 +96,7 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { if (!submenu.has(p_menu_root)) { NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; [n_menu setAutoenablesItems:NO]; + [n_menu setDelegate:menu_delegate]; submenu[p_menu_root] = n_menu; } menu = submenu[p_menu_root]; @@ -754,7 +756,7 @@ NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const return nullptr; } -int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -762,6 +764,7 @@ int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const St if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = 0; @@ -772,7 +775,7 @@ int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const St return out; } -int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -780,6 +783,7 @@ int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, co if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; obj->max_states = 0; @@ -790,7 +794,7 @@ int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, co return out; } -int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -798,6 +802,7 @@ int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, con if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = 0; @@ -817,7 +822,7 @@ int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, con return out; } -int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -825,6 +830,7 @@ int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_roo if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; obj->max_states = 0; @@ -844,7 +850,7 @@ int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_roo return out; } -int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -852,6 +858,7 @@ int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_ro if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; obj->max_states = 0; @@ -862,7 +869,7 @@ int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_ro return out; } -int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ int out = -1; @@ -870,6 +877,7 @@ int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_me if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; obj->max_states = 0; @@ -889,30 +897,15 @@ int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_me return out; } -int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { _THREAD_SAFE_METHOD_ - NSMenu *menu = _get_menu_root(p_menu_root); int out = -1; - if (menu) { - String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; - } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); - } - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; - + NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); + if (menu_item) { GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = p_callback; + obj->key_callback = p_key_callback; obj->meta = p_tag; obj->checkable_type = CHECKABLE_TYPE_NONE; obj->max_states = p_max_states; @@ -1104,6 +1097,27 @@ Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_ return Callable(); } +Callable DisplayServerMacOS::global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const { + _THREAD_SAFE_METHOD_ + + const NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND_V(p_idx < 0, Callable()); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->key_callback; + } + } + } + return Callable(); +} + Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { _THREAD_SAFE_METHOD_ @@ -1401,6 +1415,25 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root } } +void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { + _THREAD_SAFE_METHOD_ + + NSMenu *menu = _get_menu_root(p_menu_root); + if (menu) { + ERR_FAIL_COND(p_idx < 0); + if (menu == [NSApp mainMenu]) { // Skip Apple menu. + p_idx++; + } + ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_COND(!obj); + obj->key_callback = p_key_callback; + } + } +} + void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) { _THREAD_SAFE_METHOD_ @@ -3477,6 +3510,8 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [main_menu setSubmenu:apple_menu forItem:menu_item]; [main_menu setAutoenablesItems:NO]; + menu_delegate = [[GodotMenuDelegate alloc] init]; + //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback rendering_driver = p_rendering_driver; diff --git a/platform/macos/godot_menu_delegate.h b/platform/macos/godot_menu_delegate.h new file mode 100644 index 0000000000..805ac0c4a3 --- /dev/null +++ b/platform/macos/godot_menu_delegate.h @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* godot_menu_delegate.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GODOT_MENU_DELEGATE_H +#define GODOT_MENU_DELEGATE_H + +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> + +@interface GodotMenuDelegate : NSObject <NSMenuDelegate> { +} + +- (void)doNothing:(id)sender; + +@end + +#endif // GODOT_MENU_DELEGATE_H diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm new file mode 100644 index 0000000000..bd394d8415 --- /dev/null +++ b/platform/macos/godot_menu_delegate.mm @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* godot_menu_delegate.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "godot_menu_delegate.h" + +#include "display_server_macos.h" +#include "godot_menu_item.h" +#include "key_mapping_macos.h" + +@implementation GodotMenuDelegate + +- (void)doNothing:(id)sender { +} + +- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { + NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString]; + NSUInteger ev_modifiers = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + for (int i = 0; i < [menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [menu itemAtIndex:i]; + if ([menu_item isEnabled] && [[menu_item keyEquivalent] compare:ev_key] == NSOrderedSame) { + NSUInteger item_modifiers = [menu_item keyEquivalentModifierMask]; + + if (ev_modifiers == item_modifiers) { + GodotMenuItem *value = [menu_item representedObject]; + if (value->key_callback != Callable()) { + // If custom callback is set, use it. + Variant tag = value->meta; + Variant *tagp = &tag; + Variant ret; + Callable::CallError ce; + value->key_callback.callp((const Variant **)&tagp, 1, ret, ce); + } else { + // Otherwise redirect event to the engine. + if (DisplayServer::get_singleton()) { + DisplayServerMacOS::KeyEvent ke; + + ke.window_id = DisplayServer::MAIN_WINDOW_ID; + ke.macos_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.keycode = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags]); + ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]); + ke.raw = false; + ke.unicode = 0; + + reinterpret_cast<DisplayServerMacOS *>(DisplayServer::get_singleton())->push_to_key_event_buffer(ke); + } + } + + // Suppress default menu action. + *target = self; + *action = @selector(doNothing:); + return YES; + } + } + } + return NO; +} + +@end diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h index e0b9f41632..e96f5dc1cf 100644 --- a/platform/macos/godot_menu_item.h +++ b/platform/macos/godot_menu_item.h @@ -45,6 +45,7 @@ enum GlobalMenuCheckType { @interface GodotMenuItem : NSObject { @public Callable callback; + Callable key_callback; Variant meta; GlobalMenuCheckType checkable_type; int max_states; @@ -54,7 +55,4 @@ enum GlobalMenuCheckType { @end -@implementation GodotMenuItem -@end - #endif // GODOT_MENU_ITEM_H diff --git a/platform/macos/godot_menu_item.mm b/platform/macos/godot_menu_item.mm new file mode 100644 index 0000000000..ea35e35d19 --- /dev/null +++ b/platform/macos/godot_menu_item.mm @@ -0,0 +1,34 @@ +/*************************************************************************/ +/* godot_menu_item.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "godot_menu_item.h" + +@implementation GodotMenuItem +@end diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp new file mode 100644 index 0000000000..38a03aaf97 --- /dev/null +++ b/scene/2d/navigation_link_2d.cpp @@ -0,0 +1,284 @@ +/*************************************************************************/ +/* navigation_link_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_link_2d.h" + +#include "core/math/geometry_2d.h" +#include "scene/resources/world_2d.h" +#include "servers/navigation_server_2d.h" +#include "servers/navigation_server_3d.h" + +void NavigationLink2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled); + + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional); + ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional); + + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationLink2D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationLink2D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink2D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink2D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink2D::set_start_location); + ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink2D::get_start_location); + + ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink2D::set_end_location); + ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink2D::get_end_location); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink2D::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink2D::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationLink2D::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationLink2D::get_travel_cost); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_location"), "set_start_location", "get_start_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); +} + +void NavigationLink2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (enabled) { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + + // Update global positions for the link. + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + // Update global positions for the link. + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } break; + case NOTIFICATION_EXIT_TREE: { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); + } break; + case NOTIFICATION_DRAW: { +#ifdef DEBUG_ENABLED + if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) { + Color color; + if (enabled) { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color(); + } else { + color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); + } + + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + draw_line(get_start_location(), get_end_location(), color); + draw_arc(get_start_location(), radius, 0, Math_TAU, 10, color); + draw_arc(get_end_location(), radius, 0, Math_TAU, 10, color); + } +#endif // DEBUG_ENABLED + } break; + } +} + +#ifdef TOOLS_ENABLED +Rect2 NavigationLink2D::_edit_get_rect() const { + real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map()); + + Rect2 rect(get_start_location(), Size2()); + rect.expand_to(get_end_location()); + rect.grow_by(radius); + return rect; +} + +bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + Point2 segment[2] = { get_start_location(), get_end_location() }; + + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment); + return p_point.distance_to(closest_point) < p_tolerance; +} +#endif // TOOLS_ENABLED + +void NavigationLink2D::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + + enabled = p_enabled; + + if (!is_inside_tree()) { + return; + } + + if (!enabled) { + NavigationServer2D::get_singleton()->link_set_map(link, RID()); + } else { + NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map()); + } + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_bidirectional(bool p_bidirectional) { + if (bidirectional == p_bidirectional) { + return; + } + + bidirectional = p_bidirectional; + + NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional); +} + +void NavigationLink2D::set_navigation_layers(uint32_t p_navigation_layers) { + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer2D::get_singleton()->link_set_navigation_layers(link, navigation_layers); +} + +void NavigationLink2D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationLink2D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationLink2D::set_start_location(Vector2 p_location) { + if (start_location.is_equal_approx(p_location)) { + return; + } + + start_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + + update_configuration_warnings(); + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_end_location(Vector2 p_location) { + if (end_location.is_equal_approx(p_location)) { + return; + } + + end_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform2D gt = get_global_transform(); + NavigationServer2D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + + update_configuration_warnings(); + +#ifdef DEBUG_ENABLED + if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) { + queue_redraw(); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink2D::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer2D::get_singleton()->link_set_enter_cost(link, enter_cost); +} + +void NavigationLink2D::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost); +} + +TypedArray<String> NavigationLink2D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (start_location.is_equal_approx(end_location)) { + warnings.push_back(RTR("NavigationLink2D start location should be different than the end location to be useful.")); + } + + return warnings; +} + +NavigationLink2D::NavigationLink2D() { + link = NavigationServer2D::get_singleton()->link_create(); + set_notify_transform(true); +} + +NavigationLink2D::~NavigationLink2D() { + NavigationServer2D::get_singleton()->free(link); + link = RID(); +} diff --git a/scene/resources/camera_effects.h b/scene/2d/navigation_link_2d.h index 7353931d16..5990ea082c 100644 --- a/scene/resources/camera_effects.h +++ b/scene/2d/navigation_link_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* camera_effects.h */ +/* navigation_link_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,68 +28,61 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CAMERA_EFFECTS_H -#define CAMERA_EFFECTS_H +#ifndef NAVIGATION_LINK_2D_H +#define NAVIGATION_LINK_2D_H -#include "core/io/resource.h" -#include "core/templates/rid.h" +#include "scene/2d/node_2d.h" -class CameraEffects : public Resource { - GDCLASS(CameraEffects, Resource); +class NavigationLink2D : public Node2D { + GDCLASS(NavigationLink2D, Node2D); -private: - RID camera_effects; + bool enabled = true; + RID link = RID(); + bool bidirectional = true; + uint32_t navigation_layers = 1; + Vector2 end_location = Vector2(); + Vector2 start_location = Vector2(); + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; - // DOF blur - bool dof_blur_far_enabled = false; - float dof_blur_far_distance = 10.0; - float dof_blur_far_transition = 5.0; +protected: + static void _bind_methods(); + void _notification(int p_what); - bool dof_blur_near_enabled = false; - float dof_blur_near_distance = 2.0; - float dof_blur_near_transition = 1.0; +public: +#ifdef TOOLS_ENABLED + virtual Rect2 _edit_get_rect() const override; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override; +#endif - float dof_blur_amount = 0.1; - void _update_dof_blur(); + void set_enabled(bool p_enabled); + bool is_enabled() const { return enabled; } - // Override exposure - bool override_exposure_enabled = false; - float override_exposure = 1.0; - void _update_override_exposure(); + void set_bidirectional(bool p_bidirectional); + bool is_bidirectional() const { return bidirectional; } -protected: - static void _bind_methods(); - void _validate_property(PropertyInfo &p_property) const; + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const { return navigation_layers; } -public: - virtual RID get_rid() const override; - - // DOF blur - void set_dof_blur_far_enabled(bool p_enabled); - bool is_dof_blur_far_enabled() const; - void set_dof_blur_far_distance(float p_distance); - float get_dof_blur_far_distance() const; - void set_dof_blur_far_transition(float p_distance); - float get_dof_blur_far_transition() const; - - void set_dof_blur_near_enabled(bool p_enabled); - bool is_dof_blur_near_enabled() const; - void set_dof_blur_near_distance(float p_distance); - float get_dof_blur_near_distance() const; - void set_dof_blur_near_transition(float p_distance); - float get_dof_blur_near_transition() const; - - void set_dof_blur_amount(float p_amount); - float get_dof_blur_amount() const; - - // Override exposure - void set_override_exposure_enabled(bool p_enabled); - bool is_override_exposure_enabled() const; - void set_override_exposure(float p_exposure); - float get_override_exposure() const; - - CameraEffects(); - ~CameraEffects(); + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + void set_start_location(Vector2 p_location); + Vector2 get_start_location() const { return start_location; } + + void set_end_location(Vector2 p_location); + Vector2 get_end_location() const { return end_location; } + + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const { return enter_cost; } + + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const { return travel_cost; } + + TypedArray<String> get_configuration_warnings() const override; + + NavigationLink2D(); + ~NavigationLink2D(); }; -#endif // CAMERA_EFFECTS_H +#endif // NAVIGATION_LINK_2D_H diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 43158344b4..61fe00669a 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1142,7 +1142,7 @@ bool CharacterBody2D::move_and_slide() { on_ceiling = false; on_wall = false; - if (!current_platform_velocity.is_equal_approx(Vector2())) { + if (!current_platform_velocity.is_zero_approx()) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.exclude_bodies.insert(platform_rid); @@ -1241,7 +1241,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo break; } - if (result.remainder.is_equal_approx(Vector2())) { + if (result.remainder.is_zero_approx()) { motion = Vector2(); break; } @@ -1325,7 +1325,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo sliding_enabled = true; first_slide = false; - if (!collided || motion.is_equal_approx(Vector2())) { + if (!collided || motion.is_zero_approx()) { break; } } @@ -1371,7 +1371,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { motion_results.push_back(result); _set_collision_direction(result); - if (result.remainder.is_equal_approx(Vector2())) { + if (result.remainder.is_zero_approx()) { motion = Vector2(); break; } @@ -1390,7 +1390,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { } } - if (!collided || motion.is_equal_approx(Vector2())) { + if (!collided || motion.is_zero_approx()) { break; } diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index b620f58ed7..a02f322ef1 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -264,7 +264,7 @@ bool TouchScreenButton::_is_point_inside(const Point2 &p_point) { if (bitmask.is_valid()) { check_rect = false; if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) { - if (bitmask->get_bit(coord)) { + if (bitmask->get_bitv(coord)) { touched = true; } } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index b8b6296c45..304e56326d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,6 +31,7 @@ #include "camera_3d.h" #include "collision_object_3d.h" +#include "core/core_string_names.h" #include "core/math/projection.h" #include "scene/main/viewport.h" @@ -71,6 +72,17 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + + if (attributes.is_valid()) { + const CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + if (p_property.name == "near" || p_property.name == "far" || p_property.name == "fov" || p_property.name == "keep_aspect") { + p_property.usage = PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR; + } + } + } + + Node3D::_validate_property(p_property); } void Camera3D::_update_camera() { @@ -400,18 +412,44 @@ Ref<Environment> Camera3D::get_environment() const { return environment; } -void Camera3D::set_effects(const Ref<CameraEffects> &p_effects) { - effects = p_effects; - if (effects.is_valid()) { - RS::get_singleton()->camera_set_camera_effects(camera, effects->get_rid()); +void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) { + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + } + } + + attributes = p_attributes; + + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + _attributes_changed(); + } + + RS::get_singleton()->camera_set_camera_attributes(camera, attributes->get_rid()); } else { - RS::get_singleton()->camera_set_camera_effects(camera, RID()); + RS::get_singleton()->camera_set_camera_attributes(camera, RID()); } - _update_camera_mode(); + + notify_property_list_changed(); } -Ref<CameraEffects> Camera3D::get_effects() const { - return effects; +Ref<CameraAttributes> Camera3D::get_attributes() const { + return attributes; +} + +void Camera3D::_attributes_changed() { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + ERR_FAIL_COND(!physical_attributes); + + fov = physical_attributes->get_fov(); + near = physical_attributes->get_near(); + far = physical_attributes->get_far(); + keep_aspect = KEEP_HEIGHT; + _update_camera_mode(); } void Camera3D::set_keep_aspect_mode(KeepAspect p_aspect) { @@ -479,8 +517,8 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask); ClassDB::bind_method(D_METHOD("set_environment", "env"), &Camera3D::set_environment); ClassDB::bind_method(D_METHOD("get_environment"), &Camera3D::get_environment); - ClassDB::bind_method(D_METHOD("set_effects", "env"), &Camera3D::set_effects); - ClassDB::bind_method(D_METHOD("get_effects"), &Camera3D::get_effects); + ClassDB::bind_method(D_METHOD("set_attributes", "env"), &Camera3D::set_attributes); + ClassDB::bind_method(D_METHOD("get_attributes"), &Camera3D::get_attributes); ClassDB::bind_method(D_METHOD("set_keep_aspect_mode", "mode"), &Camera3D::set_keep_aspect_mode); ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera3D::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); @@ -498,7 +536,7 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_attributes", "get_attributes"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index bba9b7d1e4..f150a23e27 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -33,7 +33,7 @@ #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class Camera3D : public Node3D { @@ -64,11 +64,11 @@ private: ProjectionType mode = PROJECTION_PERSPECTIVE; - real_t fov = 0.0; + real_t fov = 75.0; real_t size = 1.0; Vector2 frustum_offset; - real_t near = 0.0; - real_t far = 0.0; + real_t near = 0.05; + real_t far = 4000.0; real_t v_offset = 0.0; real_t h_offset = 0.0; KeepAspect keep_aspect = KEEP_HEIGHT; @@ -81,7 +81,8 @@ private: uint32_t layers = 0xfffff; Ref<Environment> environment; - Ref<CameraEffects> effects; + Ref<CameraAttributes> attributes; + void _attributes_changed(); // void _camera_make_current(Node *p_camera); friend class Viewport; @@ -159,8 +160,8 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_effects(const Ref<CameraEffects> &p_effects); - Ref<CameraEffects> get_effects() const; + void set_attributes(const Ref<CameraAttributes> &p_effects); + Ref<CameraAttributes> get_attributes() const; void set_keep_aspect_mode(KeepAspect p_aspect); KeepAspect get_keep_aspect_mode() const; diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 319129603e..cfee7028d4 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "fog_volume.h" +#include "scene/resources/environment.h" /////////////////////////// diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index e4a7cf6ee5..d977874911 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -486,8 +486,9 @@ void Label3D::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); float max_line_w = 0.0; for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 66f4fa2bcc..e51f06e083 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/config/project_settings.h" + #include "light_3d.h" void Light3D::set_param(Param p_param, real_t p_value) { @@ -122,7 +124,14 @@ uint32_t Light3D::get_cull_mask() const { void Light3D::set_color(const Color &p_color) { color = p_color; - RS::get_singleton()->light_set_color(light, p_color); + + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + Color combined = color.srgb_to_linear(); + combined *= correlated_color.srgb_to_linear(); + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + } else { + RS::get_singleton()->light_set_color(light, color); + } // The gizmo color depends on the light color, so update it. update_gizmos(); } @@ -181,6 +190,56 @@ void Light3D::owner_changed_notify() { _update_visibility(); } +// Temperature expressed in Kelvins. Valid range 1000 - 15000 +// First converts to CIE 1960 then to sRGB +// As explained in the Filament documentation: https://google.github.io/filament/Filament.md.html#lighting/directlighting/lightsparameterization +Color _color_from_temperature(float p_temperature) { + float T2 = p_temperature * p_temperature; + float u = (0.860117757f + 1.54118254e-4f * p_temperature + 1.28641212e-7f * T2) / + (1.0f + 8.42420235e-4f * p_temperature + 7.08145163e-7f * T2); + float v = (0.317398726f + 4.22806245e-5f * p_temperature + 4.20481691e-8f * T2) / + (1.0f - 2.89741816e-5f * p_temperature + 1.61456053e-7f * T2); + + // Convert to xyY space. + float d = 1.0f / (2.0f * u - 8.0f * v + 4.0f); + float x = 3.0f * u * d; + float y = 2.0f * v * d; + + // Convert to XYZ space + const float a = 1.0 / MAX(y, 1e-5f); + Vector3 xyz = Vector3(x * a, 1.0, (1.0f - x - y) * a); + + // Convert from XYZ to sRGB(linear) + Vector3 linear = Vector3(3.2404542f * xyz.x - 1.5371385f * xyz.y - 0.4985314f * xyz.z, + -0.9692660f * xyz.x + 1.8760108f * xyz.y + 0.0415560f * xyz.z, + 0.0556434f * xyz.x - 0.2040259f * xyz.y + 1.0572252f * xyz.z); + linear /= MAX(1e-5f, linear[linear.max_axis_index()]); + // Normalize, clamp, and convert to sRGB. + return Color(linear.x, linear.y, linear.z).clamp().linear_to_srgb(); +} + +void Light3D::set_temperature(const float p_temperature) { + temperature = p_temperature; + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + return; + } + correlated_color = _color_from_temperature(temperature); + + Color combined = color.srgb_to_linear() * correlated_color.srgb_to_linear(); + + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + // The gizmo color depends on the light color, so update it. + update_gizmos(); +} + +Color Light3D::get_correlated_color() const { + return correlated_color; +} + +float Light3D::get_temperature() const { + return temperature; +} + void Light3D::_update_visibility() { if (!is_inside_tree()) { return; @@ -228,8 +287,14 @@ void Light3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (get_light_type() != RS::LIGHT_DIRECTIONAL && p_property.name == "light_angular_distance") { - // Angular distance is only used in DirectionalLight3D. + if (get_light_type() != RS::LIGHT_DIRECTIONAL && (p_property.name == "light_angular_distance" || p_property.name == "light_intensity_lux")) { + // Angular distance and Light Intensity Lux are only used in DirectionalLight3D. + p_property.usage = PROPERTY_USAGE_NONE; + } else if (get_light_type() == RS::LIGHT_DIRECTIONAL && p_property.name == "light_intensity_lumens") { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (p_property.name == "light_intensity_lumens" || p_property.name == "light_intensity_lux" || p_property.name == "light_temperature")) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -278,7 +343,14 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector); ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector); + ClassDB::bind_method(D_METHOD("set_temperature", "temperature"), &Light3D::set_temperature); + ClassDB::bind_method(D_METHOD("get_temperature"), &Light3D::get_temperature); + ClassDB::bind_method(D_METHOD("get_correlated_color"), &Light3D::get_correlated_color); + ADD_GROUP("Light", "light_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lumens", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:lm"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lux", PROPERTY_HINT_RANGE, "0,150000.0,0.01,or_greater,suffix:lx"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "light_temperature", PROPERTY_HINT_RANGE, "1000,15000.0,1.0,suffix:k"), "set_temperature", "get_temperature"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); @@ -331,6 +403,7 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_OPACITY); BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR); BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS); + BIND_ENUM_CONSTANT(PARAM_INTENSITY); BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(BAKE_DISABLED); @@ -382,6 +455,9 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); set_param(PARAM_SHADOW_FADE_START, 1); + // For OmniLight3D and SpotLight3D, specified in Lumens. + set_param(PARAM_INTENSITY, 1000.0); + set_temperature(6500.0); // Nearly white. set_disable_scale(true); } @@ -487,6 +563,7 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_FADE_START, 0.8); // Increase the default shadow bias to better suit most scenes. set_param(PARAM_SHADOW_BIAS, 0.1); + set_param(PARAM_INTENSITY, 100000.0); // Specified in Lux, approximate mid-day sun. set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); blend_splits = false; set_sky_mode(SKY_MODE_LIGHT_AND_SKY); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 0792f21c6e..e43d6f0419 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -58,6 +58,7 @@ public: PARAM_SHADOW_OPACITY = RS::LIGHT_PARAM_SHADOW_OPACITY, PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR, PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS, + PARAM_INTENSITY = RS::LIGHT_PARAM_INTENSITY, PARAM_MAX = RS::LIGHT_PARAM_MAX }; @@ -83,6 +84,8 @@ private: void _update_visibility(); BakeMode bake_mode = BAKE_DYNAMIC; Ref<Texture2D> projector; + Color correlated_color = Color(1.0, 1.0, 1.0); + float temperature = 6500.0; // bind helpers @@ -139,6 +142,10 @@ public: void set_projector(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_projector() const; + void set_temperature(const float p_temperature); + float get_temperature() const; + Color get_correlated_color() const; + virtual AABB get_aabb() const override; Light3D(); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 7efda6db32..41a537f7cb 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -30,10 +30,14 @@ #include "lightmap_gi.h" +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/math/delaunay_3d.h" #include "lightmap_probe.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" +#include "scene/resources/sky.h" void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { User user; @@ -207,7 +211,7 @@ bool LightmapGIData::is_using_spherical_harmonics() const { return uses_spherical_harmonics; } -void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { +void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure) { if (p_points.size()) { int pc = p_points.size(); ERR_FAIL_COND(pc * 9 != p_point_sh.size()); @@ -221,6 +225,8 @@ void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, con RS::get_singleton()->lightmap_set_probe_bounds(lightmap, AABB()); RS::get_singleton()->lightmap_set_probe_interior(lightmap, false); } + RS::get_singleton()->lightmap_set_baked_exposure_normalization(lightmap, p_baked_exposure); + baked_exposure = p_baked_exposure; interior = p_interior; bounds = p_bounds; } @@ -249,6 +255,10 @@ bool LightmapGIData::is_interior() const { return interior; } +float LightmapGIData::get_baked_exposure() const { + return baked_exposure; +} + void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("points")); @@ -256,7 +266,8 @@ void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bsp")); ERR_FAIL_COND(!p_data.has("sh")); ERR_FAIL_COND(!p_data.has("interior")); - set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"]); + ERR_FAIL_COND(!p_data.has("baked_exposure")); + set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"], p_data["baked_exposure"]); } Dictionary LightmapGIData::_get_probe_data() const { @@ -267,6 +278,7 @@ Dictionary LightmapGIData::_get_probe_data() const { d["bsp"] = get_capture_bsp_tree(); d["sh"] = get_capture_sh(); d["interior"] = is_interior(); + d["baked_exposure"] = get_baked_exposure(); return d; } @@ -977,15 +989,21 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Transform3D xf = lights_found[i].xform; Color linear_color = light->get_color().srgb_to_linear(); + float energy = light->get_param(Light3D::PARAM_ENERGY); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + energy *= light->get_param(Light3D::PARAM_INTENSITY); + linear_color *= light->get_correlated_color().srgb_to_linear(); + } + if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<OmniLight3D>(light)) { OmniLight3D *l = Object::cast_to<OmniLight3D>(light); - lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy * (1.0 / (Math_PI * 4.0)), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<SpotLight3D>(light)) { SpotLight3D *l = Object::cast_to<SpotLight3D>(light); - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy * (1.0 / Math_PI), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -1040,7 +1058,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } } - Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->calculate_exposure_normalization() * camera_attributes->get_exposure_multiplier(); + } + + Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) { return BAKE_ERROR_MESHES_INVALID; @@ -1214,7 +1237,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa /* Obtain the colors from the images, they will be re-created as cubemaps on the server, depending on the driver */ - data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array); + data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array, exposure_normalization); /* Compute a BSP tree of the simplices, so it's easy to find the exact one */ } @@ -1410,6 +1433,14 @@ LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { return gen_probes; } +void LightmapGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { + return camera_attributes; +} + void LightmapGI::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { p_property.usage = PROPERTY_USAGE_NONE; @@ -1462,6 +1493,9 @@ void LightmapGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &LightmapGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &LightmapGI::get_camera_attributes); + // ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant())); ADD_GROUP("Tweaks", ""); @@ -1477,6 +1511,7 @@ void LightmapGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment_custom_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_environment_custom_sky", "get_environment_custom_sky"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "environment_custom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_environment_custom_color", "get_environment_custom_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "environment_custom_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_environment_custom_energy", "get_environment_custom_energy"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_GROUP("Gen Probes", "generate_probes_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "generate_probes_subdiv", PROPERTY_HINT_ENUM, "Disabled,4,8,16,32"), "set_generate_probes", "get_generate_probes"); ADD_GROUP("Data", ""); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 87add9facc..0062a4a093 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -36,6 +36,9 @@ #include "scene/3d/lightmapper.h" #include "scene/3d/visual_instance_3d.h" +class Sky; +class CameraAttributes; + class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") @@ -47,6 +50,7 @@ class LightmapGIData : public Resource { RID lightmap; AABB bounds; + float baked_exposure = 1.0; struct User { NodePath path; @@ -83,8 +87,9 @@ public: bool is_using_spherical_harmonics() const; bool is_interior() const; + float get_baked_exposure() const; - void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree); + void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure); PackedVector3Array get_capture_points() const; PackedColorArray get_capture_sh() const; PackedInt32Array get_capture_tetrahedra() const; @@ -147,6 +152,7 @@ private: float environment_custom_energy = 1.0; bool directional = false; GenerateProbes gen_probes = GENERATE_PROBES_DISABLED; + Ref<CameraAttributes> camera_attributes; Ref<LightmapGIData> light_data; @@ -260,6 +266,9 @@ public: void set_generate_probes(GenerateProbes p_generate_probes); GenerateProbes get_generate_probes() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + AABB get_aabb() const override; BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 9b973fd6bc..5b5c6cf53a 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -180,7 +180,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_probe(const Vector3 &p_position) = 0; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; virtual int get_bake_texture_count() const = 0; virtual Ref<Image> get_bake_texture(int p_index) const = 0; diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp new file mode 100644 index 0000000000..47b602c966 --- /dev/null +++ b/scene/3d/navigation_link_3d.cpp @@ -0,0 +1,389 @@ +/*************************************************************************/ +/* navigation_link_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_link_3d.h" + +#include "mesh_instance_3d.h" +#include "servers/navigation_server_3d.h" + +#ifdef DEBUG_ENABLED +void NavigationLink3D::_update_debug_mesh() { + if (!is_inside_tree()) { + return; + } + + if (Engine::get_singleton()->is_editor_hint()) { + // don't update inside Editor as node 3d gizmo takes care of this + // as collisions and selections for Editor Viewport need to be updated + return; + } + + if (!NavigationServer3D::get_singleton()->get_debug_enabled()) { + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + return; + } + + if (!debug_instance.is_valid()) { + debug_instance = RenderingServer::get_singleton()->instance_create(); + } + + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + RID nav_map = get_world_3d()->get_navigation_map(); + real_t search_radius = NavigationServer3D::get_singleton()->map_get_link_connection_radius(nav_map); + Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); + Vector3::Axis up_axis = up_vector.max_axis_index(); + + debug_mesh->clear_surfaces(); + + Vector<Vector3> lines; + + // Draw line between the points. + lines.push_back(start_location); + lines.push_back(end_location); + + // Draw start location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(start_location + Vector3(0, a.x, a.y)); + lines.append(start_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(start_location + Vector3(a.x, 0, a.y)); + lines.append(start_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(start_location + Vector3(a.x, a.y, 0)); + lines.append(start_location + Vector3(b.x, b.y, 0)); + break; + } + } + + // Draw end location search radius + for (int i = 0; i < 30; i++) { + // Create a circle + const float ra = Math::deg_to_rad((float)(i * 12)); + const float rb = Math::deg_to_rad((float)((i + 1) * 12)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * search_radius; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * search_radius; + + // Draw axis-aligned circle + switch (up_axis) { + case Vector3::AXIS_X: + lines.append(end_location + Vector3(0, a.x, a.y)); + lines.append(end_location + Vector3(0, b.x, b.y)); + break; + case Vector3::AXIS_Y: + lines.append(end_location + Vector3(a.x, 0, a.y)); + lines.append(end_location + Vector3(b.x, 0, b.y)); + break; + case Vector3::AXIS_Z: + lines.append(end_location + Vector3(a.x, a.y, 0)); + lines.append(end_location + Vector3(b.x, b.y, 0)); + break; + } + } + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = lines; + + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); + + RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); + RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree()); + + Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material(); + Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material(); + + if (enabled) { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid()); + } else { + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid()); + } +} +#endif // DEBUG_ENABLED + +void NavigationLink3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled); + + ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional); + ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional); + + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationLink3D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationLink3D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink3D::set_navigation_layer_value); + ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink3D::get_navigation_layer_value); + + ClassDB::bind_method(D_METHOD("set_start_location", "location"), &NavigationLink3D::set_start_location); + ClassDB::bind_method(D_METHOD("get_start_location"), &NavigationLink3D::get_start_location); + + ClassDB::bind_method(D_METHOD("set_end_location", "location"), &NavigationLink3D::set_end_location); + ClassDB::bind_method(D_METHOD("get_end_location"), &NavigationLink3D::get_end_location); + + ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink3D::set_enter_cost); + ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink3D::get_enter_cost); + + ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationLink3D::set_travel_cost); + ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationLink3D::get_travel_cost); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_location"), "set_start_location", "get_start_location"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end_location"), "set_end_location", "get_end_location"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost"); +} + +void NavigationLink3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (enabled) { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + + // Update global positions for the link. + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + } + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + // Update global positions for the link. + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + +#ifdef DEBUG_ENABLED + if (is_inside_tree() && debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + } +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_EXIT_TREE: { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RS::get_singleton()->instance_set_scenario(debug_instance, RID()); + RS::get_singleton()->instance_set_visible(debug_instance, false); + } +#endif // DEBUG_ENABLED + } break; + } +} + +NavigationLink3D::NavigationLink3D() { + link = NavigationServer3D::get_singleton()->link_create(); + set_notify_transform(true); +} + +NavigationLink3D::~NavigationLink3D() { + NavigationServer3D::get_singleton()->free(link); + link = RID(); + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid()) { + RenderingServer::get_singleton()->free(debug_instance); + } + if (debug_mesh.is_valid()) { + RenderingServer::get_singleton()->free(debug_mesh->get_rid()); + } +#endif // DEBUG_ENABLED +} + +void NavigationLink3D::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + + enabled = p_enabled; + + if (!is_inside_tree()) { + return; + } + + if (enabled) { + NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map()); + } else { + NavigationServer3D::get_singleton()->link_set_map(link, RID()); + } + +#ifdef DEBUG_ENABLED + if (debug_instance.is_valid() && debug_mesh.is_valid()) { + if (enabled) { + Ref<StandardMaterial3D> link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_material(); + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, link_material->get_rid()); + } else { + Ref<StandardMaterial3D> disabled_link_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_link_connections_disabled_material(); + RS::get_singleton()->instance_set_surface_override_material(debug_instance, 0, disabled_link_material->get_rid()); + } + } +#endif // DEBUG_ENABLED + + update_gizmos(); +} + +void NavigationLink3D::set_bidirectional(bool p_bidirectional) { + if (bidirectional == p_bidirectional) { + return; + } + + bidirectional = p_bidirectional; + + NavigationServer3D::get_singleton()->link_set_bidirectional(link, bidirectional); +} + +void NavigationLink3D::set_navigation_layers(uint32_t p_navigation_layers) { + if (navigation_layers == p_navigation_layers) { + return; + } + + navigation_layers = p_navigation_layers; + + NavigationServer3D::get_singleton()->link_set_navigation_layers(link, navigation_layers); +} + +void NavigationLink3D::set_navigation_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive."); + + uint32_t _navigation_layers = get_navigation_layers(); + + if (p_value) { + _navigation_layers |= 1 << (p_layer_number - 1); + } else { + _navigation_layers &= ~(1 << (p_layer_number - 1)); + } + + set_navigation_layers(_navigation_layers); +} + +bool NavigationLink3D::get_navigation_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive."); + + return get_navigation_layers() & (1 << (p_layer_number - 1)); +} + +void NavigationLink3D::set_start_location(Vector3 p_location) { + if (start_location.is_equal_approx(p_location)) { + return; + } + + start_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_start_location(link, gt.xform(start_location)); + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + + update_gizmos(); + update_configuration_warnings(); +} + +void NavigationLink3D::set_end_location(Vector3 p_location) { + if (end_location.is_equal_approx(p_location)) { + return; + } + + end_location = p_location; + + if (!is_inside_tree()) { + return; + } + + Transform3D gt = get_global_transform(); + NavigationServer3D::get_singleton()->link_set_end_location(link, gt.xform(end_location)); + +#ifdef DEBUG_ENABLED + _update_debug_mesh(); +#endif // DEBUG_ENABLED + + update_gizmos(); + update_configuration_warnings(); +} + +void NavigationLink3D::set_enter_cost(real_t p_enter_cost) { + ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive."); + if (Math::is_equal_approx(enter_cost, p_enter_cost)) { + return; + } + + enter_cost = p_enter_cost; + + NavigationServer3D::get_singleton()->link_set_enter_cost(link, enter_cost); +} + +void NavigationLink3D::set_travel_cost(real_t p_travel_cost) { + ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); + if (Math::is_equal_approx(travel_cost, p_travel_cost)) { + return; + } + + travel_cost = p_travel_cost; + + NavigationServer3D::get_singleton()->link_set_travel_cost(link, travel_cost); +} + +TypedArray<String> NavigationLink3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (start_location.is_equal_approx(end_location)) { + warnings.push_back(RTR("NavigationLink3D start location should be different than the end location to be useful.")); + } + + return warnings; +} diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h new file mode 100644 index 0000000000..1f88075527 --- /dev/null +++ b/scene/3d/navigation_link_3d.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* navigation_link_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_LINK_3D_H +#define NAVIGATION_LINK_3D_H + +#include "scene/3d/node_3d.h" + +class NavigationLink3D : public Node3D { + GDCLASS(NavigationLink3D, Node3D); + + bool enabled = true; + RID link = RID(); + bool bidirectional = true; + uint32_t navigation_layers = 1; + Vector3 end_location = Vector3(); + Vector3 start_location = Vector3(); + real_t enter_cost = 0.0; + real_t travel_cost = 1.0; + +#ifdef DEBUG_ENABLED + RID debug_instance; + Ref<ArrayMesh> debug_mesh; + + void _update_debug_mesh(); +#endif // DEBUG_ENABLED + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + NavigationLink3D(); + ~NavigationLink3D(); + + void set_enabled(bool p_enabled); + bool is_enabled() const { return enabled; } + + void set_bidirectional(bool p_bidirectional); + bool is_bidirectional() const { return bidirectional; } + + void set_navigation_layers(uint32_t p_navigation_layers); + uint32_t get_navigation_layers() const { return navigation_layers; } + + void set_navigation_layer_value(int p_layer_number, bool p_value); + bool get_navigation_layer_value(int p_layer_number) const; + + void set_start_location(Vector3 p_location); + Vector3 get_start_location() const { return start_location; } + + void set_end_location(Vector3 p_location); + Vector3 get_end_location() const { return end_location; } + + void set_enter_cost(real_t p_enter_cost); + real_t get_enter_cost() const { return enter_cost; } + + void set_travel_cost(real_t p_travel_cost); + real_t get_travel_cost() const { return travel_cost; } + + TypedArray<String> get_configuration_warnings() const override; +}; + +#endif // NAVIGATION_LINK_3D_H diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 426a8c1684..095aeef560 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -792,8 +792,8 @@ void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); - ERR_FAIL_COND_MSG(p_up.is_equal_approx(Vector3()), "The up vector can't be zero, look_at() failed."); - ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_equal_approx(Vector3()), "Up vector and direction between node origin and target are aligned, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed."); Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos); Vector3 original_scale = get_scale(); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index c1c309fdbe..015cb9a21d 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -186,21 +186,21 @@ ArrayOccluder3D::~ArrayOccluder3D() { ///////////////////////////////////////////////// -void QuadOccluder3D::set_size(const Vector2 &p_size) { +void QuadOccluder3D::set_size(const Size2 &p_size) { if (size == p_size) { return; } - size = p_size.max(Vector2()); + size = p_size.max(Size2()); _update(); } -Vector2 QuadOccluder3D::get_size() const { +Size2 QuadOccluder3D::get_size() const { return size; } void QuadOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { - Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); + Size2 _size = Size2(size.x / 2.0f, size.y / 2.0f); r_vertices = { Vector3(-_size.x, -_size.y, 0), diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index 11d731b989..69a80e63fc 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -88,15 +88,15 @@ class QuadOccluder3D : public Occluder3D { GDCLASS(QuadOccluder3D, Occluder3D); private: - Vector2 size = Vector2(1.0f, 1.0f); + Size2 size = Vector2(1.0f, 1.0f); protected: virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override; static void _bind_methods(); public: - Vector2 get_size() const; - void set_size(const Vector2 &p_size); + Size2 get_size() const; + void set_size(const Size2 &p_size); QuadOccluder3D(); ~QuadOccluder3D(); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 5534bc28f1..86dbcc2306 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1208,7 +1208,7 @@ bool CharacterBody3D::move_and_slide() { last_motion = Vector3(); - if (!current_platform_velocity.is_equal_approx(Vector3())) { + if (!current_platform_velocity.is_zero_approx()) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. @@ -1315,7 +1315,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo break; } - if (result.remainder.is_equal_approx(Vector3())) { + if (result.remainder.is_zero_approx()) { motion = Vector3(); break; } @@ -1428,7 +1428,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; Vector3 slide_motion = result.remainder.slide(collision.normal); - if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_equal_approx(Vector3())) { + if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_zero_approx()) { // Slide using the intersection between the motion plane and the floor plane, // in order to keep the direction intact. real_t motion_length = slide_motion.length(); @@ -1469,7 +1469,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo total_travel += result.travel; // Apply Constant Speed. - if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { + if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_zero_approx()) { Vector3 travel_slide_up = total_travel.slide(up_direction); motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length())); } @@ -1492,7 +1492,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo collided = true; } - if (!collided || motion.is_equal_approx(Vector3())) { + if (!collided || motion.is_zero_approx()) { break; } @@ -1533,7 +1533,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { CollisionState result_state; _set_collision_direction(result, result_state); - if (result.remainder.is_equal_approx(Vector3())) { + if (result.remainder.is_zero_approx()) { motion = Vector3(); break; } @@ -1557,7 +1557,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { } } - if (!collided || motion.is_equal_approx(Vector3())) { + if (!collided || motion.is_zero_approx()) { break; } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 669c3eb7fd..4515277dc3 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1174,7 +1174,7 @@ void AnimatedSprite3D::_res_changed() { _queue_update(); } -void AnimatedSprite3D::_set_playing(bool p_playing) { +void AnimatedSprite3D::set_playing(bool p_playing) { if (playing == p_playing) { return; } @@ -1183,7 +1183,7 @@ void AnimatedSprite3D::_set_playing(bool p_playing) { set_process_internal(playing); } -bool AnimatedSprite3D::_is_playing() const { +bool AnimatedSprite3D::is_playing() const { return playing; } @@ -1191,15 +1191,11 @@ void AnimatedSprite3D::play(const StringName &p_animation) { if (p_animation) { set_animation(p_animation); } - _set_playing(true); + set_playing(true); } void AnimatedSprite3D::stop() { - _set_playing(false); -} - -bool AnimatedSprite3D::is_playing() const { - return playing; + set_playing(false); } void AnimatedSprite3D::_reset_timeout() { @@ -1262,12 +1258,11 @@ void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite3D::set_animation); ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite3D::get_animation); - ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite3D::_set_playing); - ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite3D::_is_playing); + ClassDB::bind_method(D_METHOD("set_playing", "playing"), &AnimatedSprite3D::set_playing); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing); ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite3D::play, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing); ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame); @@ -1280,7 +1275,7 @@ void AnimatedSprite3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "set_playing", "is_playing"); } AnimatedSprite3D::AnimatedSprite3D() { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 03688cf787..84244a2476 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -219,8 +219,6 @@ class AnimatedSprite3D : public SpriteBase3D { void _res_changed(); void _reset_timeout(); - void _set_playing(bool p_playing); - bool _is_playing() const; RID last_shader; RID last_texture; @@ -237,6 +235,8 @@ public: void play(const StringName &p_animation = StringName()); void stop(); + + void set_playing(bool p_playing); bool is_playing() const; void set_animation(const StringName &p_animation); diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index ae231026a7..c97af087bf 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -30,8 +30,10 @@ #include "voxel_gi.h" +#include "core/core_string_names.h" #include "mesh_instance_3d.h" #include "multimesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" #include "voxelizer.h" void VoxelGIData::_set_data(const Dictionary &p_data) { @@ -281,6 +283,14 @@ Vector3 VoxelGI::get_extents() const { return extents; } +void VoxelGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> VoxelGI::get_camera_attributes() const { + return camera_attributes; +} + void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { @@ -370,9 +380,14 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { p_from_node = p_from_node ? p_from_node : get_parent(); ERR_FAIL_NULL(p_from_node); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->calculate_exposure_normalization() * camera_attributes->get_exposure_multiplier(); + } + Voxelizer baker; - baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0)); + baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0), exposure_normalization); List<PlotMesh> mesh_list; @@ -428,6 +443,8 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { Vector<uint8_t> df = baker.get_sdf_3d_image(); + RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data->get_rid(), exposure_normalization); + probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); set_probe_data(probe_data); @@ -472,12 +489,16 @@ void VoxelGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &VoxelGI::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &VoxelGI::get_extents); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &VoxelGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &VoxelGI::get_camera_attributes); + ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &VoxelGI::bake, DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("debug_bake"), &VoxelGI::_debug_bake); ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 6d173dea87..b31ae4cd95 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -33,6 +33,8 @@ #include "scene/3d/visual_instance_3d.h" +class CameraAttributes; + class VoxelGIData : public Resource { GDCLASS(VoxelGIData, Resource); @@ -117,6 +119,7 @@ private: Subdiv subdiv = SUBDIV_128; Vector3 extents = Vector3(10, 10, 10); + Ref<CameraAttributes> camera_attributes; struct PlotMesh { Ref<Material> override_material; @@ -144,6 +147,10 @@ public: void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; + + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + Vector3i get_estimated_cell_size() const; void bake(Node *p_from_node = nullptr, bool p_create_visual_debug = false); diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 9380d1cf32..6daa9e0aec 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -30,6 +30,8 @@ #include "voxelizer.h" +#include "core/config/project_settings.h" + static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { if (p_pos.is_equal_approx(p_vtx[0])) { r_uv = p_uv[0]; @@ -348,7 +350,10 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Texture2D> emission_tex = mat->get_texture(BaseMaterial3D::TEXTURE_EMISSION); Color emission_col = mat->get_emission(); - float emission_energy = mat->get_emission_energy(); + float emission_energy = mat->get_emission_energy_multiplier() * exposure_normalization; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + emission_energy *= mat->get_emission_intensity(); + } Ref<Image> img_emission; @@ -608,10 +613,11 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { } } -void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { +void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization) { sorted = false; original_bounds = p_bounds; cell_subdiv = p_subdiv; + exposure_normalization = p_exposure_normalization; bake_cells.resize(1); material_cache.clear(); diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 68bce768b7..f5bb9af107 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -87,6 +87,7 @@ private: }; HashMap<Ref<Material>, MaterialCache> material_cache; + float exposure_normalization = 1.0; AABB original_bounds; AABB po2_bounds; int axis_cell_size[3] = {}; @@ -111,7 +112,7 @@ private: void _sort(); public: - void begin_bake(int p_subdiv, const AABB &p_bounds); + void begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization); void plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); void end_bake(); diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index fe9d9ae4dd..ae7d79e8b0 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -42,9 +42,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; @@ -55,9 +55,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; } @@ -74,15 +74,15 @@ void WorldEnvironment::_update_current_environment() { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } -void WorldEnvironment::_update_current_camera_effects() { - WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); +void WorldEnvironment::_update_current_camera_attributes() { + WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); if (first) { - get_viewport()->find_world_3d()->set_camera_effects(first->camera_effects); + get_viewport()->find_world_3d()->set_camera_attributes(first->camera_attributes); } else { - get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); + get_viewport()->find_world_3d()->set_camera_attributes(Ref<CameraAttributes>()); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { @@ -110,36 +110,36 @@ Ref<Environment> WorldEnvironment::get_environment() const { return environment; } -void WorldEnvironment::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - if (camera_effects == p_camera_effects) { +void WorldEnvironment::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + if (camera_attributes == p_camera_attributes) { return; } - if (is_inside_tree() && camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() == camera_effects) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + if (is_inside_tree() && camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() == camera_attributes) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } - camera_effects = p_camera_effects; - if (is_inside_tree() && camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + camera_attributes = p_camera_attributes; + if (is_inside_tree() && camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } if (is_inside_tree()) { - _update_current_camera_effects(); + _update_current_camera_attributes(); } else { update_configuration_warnings(); } } -Ref<CameraEffects> WorldEnvironment::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> WorldEnvironment::get_camera_attributes() const { + return camera_attributes; } TypedArray<String> WorldEnvironment::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if (!environment.is_valid() && !camera_effects.is_valid()) { - warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both.")); + if (!environment.is_valid() && !camera_attributes.is_valid()) { + warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Attributes\" property to contain a CameraAttributes resource, or both.")); } if (!is_inside_tree()) { @@ -150,7 +150,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { warnings.push_back(("Only the first Environment has an effect in a scene (or set of instantiated scenes).")); } - if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { + if (camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() != camera_attributes) { warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } @@ -162,9 +162,9 @@ void WorldEnvironment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &WorldEnvironment::get_environment); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &WorldEnvironment::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &WorldEnvironment::get_camera_effects); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &WorldEnvironment::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &WorldEnvironment::get_camera_attributes); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); } WorldEnvironment::WorldEnvironment() { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 9955aa72a8..07f243c750 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -32,17 +32,17 @@ #define WORLD_ENVIRONMENT_H #include "scene/main/node.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class WorldEnvironment : public Node { GDCLASS(WorldEnvironment, Node); Ref<Environment> environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; void _update_current_environment(); - void _update_current_camera_effects(); + void _update_current_camera_attributes(); protected: void _notification(int p_what); @@ -52,8 +52,8 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index a56a51a547..151b0b93d4 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -44,7 +44,6 @@ void BoxContainer::_resort() { Size2i new_size = get_size(); - int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer"); bool rtl = is_layout_rtl(); bool first = true; @@ -90,7 +89,7 @@ void BoxContainer::_resort() { return; } - int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * sep; + int stretch_max = (vertical ? new_size.height : new_size.width) - (children_count - 1) * theme_cache.separation; int stretch_diff = stretch_max - stretch_min; if (stretch_diff < 0) { //avoid negative stretch space @@ -214,7 +213,7 @@ void BoxContainer::_resort() { if (first) { first = false; } else { - ofs += sep; + ofs += theme_cache.separation; } int from = ofs; @@ -248,7 +247,6 @@ Size2 BoxContainer::get_minimum_size() const { /* Calculate MINIMUM SIZE */ Size2i minimum; - int sep = get_theme_constant(SNAME("separation")); //,vertical?"VBoxContainer":"HBoxContainer"); bool first = true; @@ -273,7 +271,7 @@ Size2 BoxContainer::get_minimum_size() const { minimum.width = size.width; } - minimum.height += size.height + (first ? 0 : sep); + minimum.height += size.height + (first ? 0 : theme_cache.separation); } else { /* HORIZONTAL */ @@ -281,7 +279,7 @@ Size2 BoxContainer::get_minimum_size() const { minimum.height = size.height; } - minimum.width += size.width + (first ? 0 : sep); + minimum.width += size.width + (first ? 0 : theme_cache.separation); } first = false; @@ -290,6 +288,12 @@ Size2 BoxContainer::get_minimum_size() const { return minimum; } +void BoxContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); +} + void BoxContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -307,6 +311,12 @@ void BoxContainer::_notification(int p_what) { } } +void BoxContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void BoxContainer::set_alignment(AlignmentMode p_alignment) { if (alignment == p_alignment) { return; @@ -319,6 +329,17 @@ BoxContainer::AlignmentMode BoxContainer::get_alignment() const { return alignment; } +void BoxContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool BoxContainer::is_vertical() const { + return vertical; +} + Control *BoxContainer::add_spacer(bool p_begin) { Control *c = memnew(Control); c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events @@ -367,14 +388,17 @@ BoxContainer::BoxContainer(bool p_vertical) { void BoxContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("add_spacer", "begin"), &BoxContainer::add_spacer); - ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment); ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment); + ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment); + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &BoxContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &BoxContainer::is_vertical); BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); BIND_ENUM_CONSTANT(ALIGNMENT_END); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control *p_control, bool p_expand) { diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index 3043c3ea45..0ee5bd1772 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -47,11 +47,19 @@ private: bool vertical = false; AlignmentMode alignment = ALIGNMENT_BEGIN; + struct ThemeCache { + int separation = 0; + } theme_cache; + void _resort(); protected: - void _notification(int p_what); + bool is_fixed = false; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -60,6 +68,9 @@ public: void set_alignment(AlignmentMode p_alignment); AlignmentMode get_alignment() const; + void set_vertical(bool p_vertical); + bool is_vertical() const; + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; @@ -73,7 +84,7 @@ class HBoxContainer : public BoxContainer { public: HBoxContainer() : - BoxContainer(false) {} + BoxContainer(false) { is_fixed = true; } }; class MarginContainer; @@ -84,7 +95,7 @@ public: MarginContainer *add_margin_child(const String &p_label, Control *p_control, bool p_expand = false); VBoxContainer() : - BoxContainer(true) {} + BoxContainer(true) { is_fixed = true; } }; VARIANT_ENUM_CAST(BoxContainer::AlignmentMode); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 0b7280619e..c2b82e01d1 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -36,7 +36,7 @@ Size2 Button::get_minimum_size() const { Ref<Texture2D> _icon = icon; if (_icon.is_null() && has_theme_icon(SNAME("icon"))) { - _icon = Control::get_theme_icon(SNAME("icon")); + _icon = theme_cache.icon; } return get_minimum_size_for_text_and_icon("", _icon); @@ -46,6 +46,45 @@ void Button::_set_internal_margin(Side p_side, float p_value) { _internal_margin[p_side] = p_value; } +void Button::_update_theme_item_cache() { + BaseButton::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored")); + theme_cache.pressed = get_theme_stylebox(SNAME("pressed")); + theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored")); + theme_cache.hover = get_theme_stylebox(SNAME("hover")); + theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored")); + theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed")); + theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + theme_cache.disabled = get_theme_stylebox(SNAME("disabled")); + theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored")); + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.icon_normal_color = get_theme_color(SNAME("icon_normal_color")); + theme_cache.icon_focus_color = get_theme_color(SNAME("icon_focus_color")); + theme_cache.icon_pressed_color = get_theme_color(SNAME("icon_pressed_color")); + theme_cache.icon_hover_color = get_theme_color(SNAME("icon_hover_color")); + theme_cache.icon_hover_pressed_color = get_theme_color(SNAME("icon_hover_pressed_color")); + theme_cache.icon_disabled_color = get_theme_color(SNAME("icon_disabled_color")); + + theme_cache.icon = get_theme_icon(SNAME("icon")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); +} + void Button::_notification(int p_what) { switch (p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { @@ -73,15 +112,15 @@ void Button::_notification(int p_what) { Color color; Color color_icon(1, 1, 1, 1); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); switch (get_draw_mode()) { case DRAW_NORMAL: { if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { - style = get_theme_stylebox(SNAME("normal_mirrored")); + style = theme_cache.normal_mirrored; } else { - style = get_theme_stylebox(SNAME("normal")); + style = theme_cache.normal; } if (!flat) { @@ -90,14 +129,14 @@ void Button::_notification(int p_what) { // Focus colors only take precedence over normal state. if (has_focus()) { - color = get_theme_color(SNAME("font_focus_color")); + color = theme_cache.font_focus_color; if (has_theme_color(SNAME("icon_focus_color"))) { - color_icon = get_theme_color(SNAME("icon_focus_color")); + color_icon = theme_cache.icon_focus_color; } } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; if (has_theme_color(SNAME("icon_normal_color"))) { - color_icon = get_theme_color(SNAME("icon_normal_color")); + color_icon = theme_cache.icon_normal_color; } } } break; @@ -105,19 +144,19 @@ void Button::_notification(int p_what) { // Edge case for CheckButton and CheckBox. if (has_theme_stylebox("hover_pressed")) { if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + style = theme_cache.hover_pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("hover_pressed")); + style = theme_cache.hover_pressed; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } if (has_theme_color(SNAME("font_hover_pressed_color"))) { - color = get_theme_color(SNAME("font_hover_pressed_color")); + color = theme_cache.font_hover_pressed_color; } if (has_theme_color(SNAME("icon_hover_pressed_color"))) { - color_icon = get_theme_color(SNAME("icon_hover_pressed_color")); + color_icon = theme_cache.icon_hover_pressed_color; } break; @@ -126,53 +165,53 @@ void Button::_notification(int p_what) { } case DRAW_PRESSED: { if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("pressed_mirrored")); + style = theme_cache.pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("pressed")); + style = theme_cache.pressed; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } if (has_theme_color(SNAME("font_pressed_color"))) { - color = get_theme_color(SNAME("font_pressed_color")); + color = theme_cache.font_pressed_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } if (has_theme_color(SNAME("icon_pressed_color"))) { - color_icon = get_theme_color(SNAME("icon_pressed_color")); + color_icon = theme_cache.icon_pressed_color; } } break; case DRAW_HOVER: { if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_mirrored")); + style = theme_cache.hover_mirrored; } else { - style = get_theme_stylebox(SNAME("hover")); + style = theme_cache.hover; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } - color = get_theme_color(SNAME("font_hover_color")); + color = theme_cache.font_hover_color; if (has_theme_color(SNAME("icon_hover_color"))) { - color_icon = get_theme_color(SNAME("icon_hover_color")); + color_icon = theme_cache.icon_hover_color; } } break; case DRAW_DISABLED: { if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { - style = get_theme_stylebox(SNAME("disabled_mirrored")); + style = theme_cache.disabled_mirrored; } else { - style = get_theme_stylebox(SNAME("disabled")); + style = theme_cache.disabled; } if (!flat) { style->draw(ci, Rect2(Point2(0, 0), size)); } - color = get_theme_color(SNAME("font_disabled_color")); + color = theme_cache.font_disabled_color; if (has_theme_color(SNAME("icon_disabled_color"))) { - color_icon = get_theme_color(SNAME("icon_disabled_color")); + color_icon = theme_cache.icon_disabled_color; } else { color_icon.a = 0.4; } @@ -181,13 +220,13 @@ void Button::_notification(int p_what) { } if (has_focus()) { - Ref<StyleBox> style2 = get_theme_stylebox(SNAME("focus")); + Ref<StyleBox> style2 = theme_cache.focus; style2->draw(ci, Rect2(Point2(), size)); } Ref<Texture2D> _icon; if (icon.is_null() && has_theme_icon(SNAME("icon"))) { - _icon = Control::get_theme_icon(SNAME("icon")); + _icon = theme_cache.icon; } else { _icon = icon; } @@ -217,21 +256,21 @@ void Button::_notification(int p_what) { if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { style_offset.x = style->get_margin(SIDE_LEFT); if (_internal_margin[SIDE_LEFT] > 0) { - icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + icon_ofs_region = _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { style_offset.x = 0.0; } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) { style_offset.x = -style->get_margin(SIDE_RIGHT); if (_internal_margin[SIDE_RIGHT] > 0) { - icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); + icon_ofs_region = -_internal_margin[SIDE_RIGHT] - theme_cache.h_separation; } } style_offset.y = style->get_margin(SIDE_TOP); if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; - int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation")); + int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation; _size.width -= icon_text_separation + icon_ofs_region; if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { _size.width -= text_buf->get_size().width; @@ -261,7 +300,7 @@ void Button::_notification(int p_what) { } } - Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("h_separation")), 0) : Point2(); + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + theme_cache.h_separation, 0) : Point2(); if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { icon_ofs.x = 0.0; } @@ -271,10 +310,10 @@ void Button::_notification(int p_what) { int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x); if (_internal_margin[SIDE_LEFT] > 0) { - text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + text_clip -= _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } if (_internal_margin[SIDE_RIGHT] > 0) { - text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("h_separation")); + text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation; } Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; @@ -288,7 +327,7 @@ void Button::_notification(int p_what) { icon_ofs.x = 0.0; } if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + theme_cache.h_separation; } else { text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; } @@ -305,7 +344,7 @@ void Button::_notification(int p_what) { } break; case HORIZONTAL_ALIGNMENT_RIGHT: { if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - theme_cache.h_separation; } else { text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; } @@ -316,8 +355,8 @@ void Button::_notification(int p_what) { } break; } - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; if (outline_size > 0 && font_outline_color.a > 0) { text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); } @@ -346,7 +385,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { minsize.width += p_icon->get_width(); if (!xl_text.is_empty() || !p_text.is_empty()) { - minsize.width += MAX(0, get_theme_constant(SNAME("h_separation"))); + minsize.width += MAX(0, theme_cache.h_separation); } } else { minsize.width = MAX(minsize.width, p_icon->get_width()); @@ -354,12 +393,12 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu } if (!xl_text.is_empty() || !p_text.is_empty()) { - Ref<Font> font = get_theme_font(SNAME("font")); - float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); + Ref<Font> font = theme_cache.font; + float font_height = font->get_height(theme_cache.font_size); minsize.height = MAX(font_height, minsize.height); } - return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize; + return theme_cache.normal->get_minimum_size() + minsize; } void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { @@ -371,10 +410,15 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { p_text = xl_text; } - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - p_paragraph->clear(); + + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; + if (font.is_null() || font_size == 0) { + // Can't shape without a valid font and a non-zero size. + return; + } + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { @@ -544,7 +588,7 @@ void Button::_bind_methods() { Button::Button(const String &p_text) { text_buf.instantiate(); - text_buf->set_break_flags(TextServer::BREAK_MANDATORY); + text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES); set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); diff --git a/scene/gui/button.h b/scene/gui/button.h index 23b5c78166..9d9f9763db 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -54,10 +54,48 @@ private: HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT; float _internal_margin[4] = {}; + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> normal_mirrored; + Ref<StyleBox> pressed; + Ref<StyleBox> pressed_mirrored; + Ref<StyleBox> hover; + Ref<StyleBox> hover_mirrored; + Ref<StyleBox> hover_pressed; + Ref<StyleBox> hover_pressed_mirrored; + Ref<StyleBox> disabled; + Ref<StyleBox> disabled_mirrored; + Ref<StyleBox> focus; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + Color icon_normal_color; + Color icon_focus_color; + Color icon_pressed_color; + Color icon_hover_color; + Color icon_hover_pressed_color; + Color icon_disabled_color; + + Ref<Texture2D> icon; + + int h_separation = 0; + } theme_cache; + void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); protected: void _set_internal_margin(Side p_side, float p_value); + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index 26edc1f1b0..37db7d53f0 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -33,39 +33,30 @@ #include "servers/rendering_server.h" Size2 CheckBox::get_icon_size() const { - Ref<Texture2D> checked = Control::get_theme_icon(SNAME("checked")); - Ref<Texture2D> unchecked = Control::get_theme_icon(SNAME("unchecked")); - Ref<Texture2D> radio_checked = Control::get_theme_icon(SNAME("radio_checked")); - Ref<Texture2D> radio_unchecked = Control::get_theme_icon(SNAME("radio_unchecked")); - Ref<Texture2D> checked_disabled = Control::get_theme_icon(SNAME("checked_disabled")); - Ref<Texture2D> unchecked_disabled = Control::get_theme_icon(SNAME("unchecked_disabled")); - Ref<Texture2D> radio_checked_disabled = Control::get_theme_icon(SNAME("radio_checked_disabled")); - Ref<Texture2D> radio_unchecked_disabled = Control::get_theme_icon(SNAME("radio_unchecked_disabled")); - Size2 tex_size = Size2(0, 0); - if (!checked.is_null()) { - tex_size = Size2(checked->get_width(), checked->get_height()); + if (!theme_cache.checked.is_null()) { + tex_size = Size2(theme_cache.checked->get_width(), theme_cache.checked->get_height()); } - if (!unchecked.is_null()) { - tex_size = Size2(MAX(tex_size.width, unchecked->get_width()), MAX(tex_size.height, unchecked->get_height())); + if (!theme_cache.unchecked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked->get_width()), MAX(tex_size.height, theme_cache.unchecked->get_height())); } - if (!radio_checked.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_checked->get_width()), MAX(tex_size.height, radio_checked->get_height())); + if (!theme_cache.radio_checked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked->get_width()), MAX(tex_size.height, theme_cache.radio_checked->get_height())); } - if (!radio_unchecked.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height())); + if (!theme_cache.radio_unchecked.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked->get_height())); } - if (!checked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, checked_disabled->get_width()), MAX(tex_size.height, checked_disabled->get_height())); + if (!theme_cache.checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.checked_disabled->get_width()), MAX(tex_size.height, theme_cache.checked_disabled->get_height())); } - if (!unchecked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, unchecked_disabled->get_width()), MAX(tex_size.height, unchecked_disabled->get_height())); + if (!theme_cache.unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.unchecked_disabled->get_height())); } - if (!radio_checked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_checked_disabled->get_width()), MAX(tex_size.height, radio_checked_disabled->get_height())); + if (!theme_cache.radio_checked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_checked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_checked_disabled->get_height())); } - if (!radio_unchecked_disabled.is_null()) { - tex_size = Size2(MAX(tex_size.width, radio_unchecked_disabled->get_width()), MAX(tex_size.height, radio_unchecked_disabled->get_height())); + if (!theme_cache.radio_unchecked_disabled.is_null()) { + tex_size = Size2(MAX(tex_size.width, theme_cache.radio_unchecked_disabled->get_width()), MAX(tex_size.height, theme_cache.radio_unchecked_disabled->get_height())); } return tex_size; } @@ -75,14 +66,30 @@ Size2 CheckBox::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += MAX(0, get_theme_constant(SNAME("h_separation"))); + minsize.width += MAX(0, theme_cache.h_separation); } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM)); return minsize; } +void CheckBox::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.check_v_adjust = get_theme_constant(SNAME("check_v_adjust")); + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.radio_checked = get_theme_icon(SNAME("radio_checked")); + theme_cache.radio_unchecked = get_theme_icon(SNAME("radio_unchecked")); + theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled")); + theme_cache.radio_checked_disabled = get_theme_icon(SNAME("radio_checked_disabled")); + theme_cache.radio_unchecked_disabled = get_theme_icon(SNAME("radio_unchecked_disabled")); +} + void CheckBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: @@ -100,22 +107,39 @@ void CheckBox::_notification(int p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : "")); - Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : "")); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + + if (is_radio()) { + if (is_disabled()) { + on_tex = theme_cache.radio_checked_disabled; + off_tex = theme_cache.radio_unchecked_disabled; + } else { + on_tex = theme_cache.radio_checked; + off_tex = theme_cache.radio_unchecked; + } + } else { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } + } Vector2 ofs; if (is_layout_rtl()) { - ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width; + ofs.x = get_size().x - theme_cache.normal_style->get_margin(SIDE_RIGHT) - get_icon_size().width; } else { - ofs.x = sb->get_margin(SIDE_LEFT); + ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT); } - ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_v_adjust")); + ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_adjust; if (is_pressed()) { - on->draw(ci, ofs); + on_tex->draw(ci, ofs); } else { - off->draw(ci, ofs); + off_tex->draw(ci, ofs); } } break; } diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h index fcdb2ce08c..beafece3dc 100644 --- a/scene/gui/check_box.h +++ b/scene/gui/check_box.h @@ -36,9 +36,26 @@ class CheckBox : public Button { GDCLASS(CheckBox, Button); + struct ThemeCache { + int h_separation = 0; + int check_v_adjust = 0; + Ref<StyleBox> normal_style; + + Ref<Texture2D> checked; + Ref<Texture2D> unchecked; + Ref<Texture2D> radio_checked; + Ref<Texture2D> radio_unchecked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> radio_checked_disabled; + Ref<Texture2D> radio_unchecked_disabled; + } theme_cache; + protected: Size2 get_icon_size() const; Size2 get_minimum_size() const override; + + virtual void _update_theme_item_cache() override; void _notification(int p_what); bool is_radio(); diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index b9674ca41e..b01081e31b 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -34,14 +34,33 @@ #include "servers/rendering_server.h" Size2 CheckButton::get_icon_size() const { - Ref<Texture2D> on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - Ref<Texture2D> off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + + if (is_layout_rtl()) { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled_mirrored; + off_tex = theme_cache.unchecked_disabled_mirrored; + } else { + on_tex = theme_cache.checked_mirrored; + off_tex = theme_cache.unchecked_mirrored; + } + } else { + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } + } + Size2 tex_size = Size2(0, 0); - if (!on.is_null()) { - tex_size = Size2(on->get_width(), on->get_height()); + if (!on_tex.is_null()) { + tex_size = Size2(on_tex->get_width(), on_tex->get_height()); } - if (!off.is_null()) { - tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height())); + if (!off_tex.is_null()) { + tex_size = Size2(MAX(tex_size.width, off_tex->get_width()), MAX(tex_size.height, off_tex->get_height())); } return tex_size; @@ -52,14 +71,30 @@ Size2 CheckButton::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += MAX(0, get_theme_constant(SNAME("h_separation"))); + minsize.width += MAX(0, theme_cache.h_separation); } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); + minsize.height = MAX(minsize.height, tex_size.height + theme_cache.normal_style->get_margin(SIDE_TOP) + theme_cache.normal_style->get_margin(SIDE_BOTTOM)); return minsize; } +void CheckButton::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.check_v_adjust = get_theme_constant(SNAME("check_v_adjust")); + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + + theme_cache.checked = get_theme_icon(SNAME("on")); + theme_cache.unchecked = get_theme_icon(SNAME("off")); + theme_cache.checked_disabled = get_theme_icon(SNAME("on_disabled")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("off_disabled")); + theme_cache.checked_mirrored = get_theme_icon(SNAME("on_mirrored")); + theme_cache.unchecked_mirrored = get_theme_icon(SNAME("off_mirrored")); + theme_cache.checked_disabled_mirrored = get_theme_icon(SNAME("on_disabled_mirrored")); + theme_cache.unchecked_disabled_mirrored = get_theme_icon(SNAME("off_disabled_mirrored")); +} + void CheckButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: @@ -78,34 +113,41 @@ void CheckButton::_notification(int p_what) { RID ci = get_canvas_item(); bool rtl = is_layout_rtl(); - Ref<Texture2D> on; - if (rtl) { - on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored"); - } else { - on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - } - Ref<Texture2D> off; + Ref<Texture2D> on_tex; + Ref<Texture2D> off_tex; + if (rtl) { - off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored"); + if (is_disabled()) { + on_tex = theme_cache.checked_disabled_mirrored; + off_tex = theme_cache.unchecked_disabled_mirrored; + } else { + on_tex = theme_cache.checked_mirrored; + off_tex = theme_cache.unchecked_mirrored; + } } else { - off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + if (is_disabled()) { + on_tex = theme_cache.checked_disabled; + off_tex = theme_cache.unchecked_disabled; + } else { + on_tex = theme_cache.checked; + off_tex = theme_cache.unchecked; + } } - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); Vector2 ofs; Size2 tex_size = get_icon_size(); if (rtl) { - ofs.x = sb->get_margin(SIDE_LEFT); + ofs.x = theme_cache.normal_style->get_margin(SIDE_LEFT); } else { - ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); + ofs.x = get_size().width - (tex_size.width + theme_cache.normal_style->get_margin(SIDE_RIGHT)); } - ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_v_adjust")); + ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_adjust; if (is_pressed()) { - on->draw(ci, ofs); + on_tex->draw(ci, ofs); } else { - off->draw(ci, ofs); + off_tex->draw(ci, ofs); } } break; } diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index 7d4bb8bdfc..e71472c870 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -36,9 +36,26 @@ class CheckButton : public Button { GDCLASS(CheckButton, Button); + struct ThemeCache { + int h_separation = 0; + int check_v_adjust = 0; + Ref<StyleBox> normal_style; + + Ref<Texture2D> checked; + Ref<Texture2D> unchecked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> checked_mirrored; + Ref<Texture2D> unchecked_mirrored; + Ref<Texture2D> checked_disabled_mirrored; + Ref<Texture2D> unchecked_disabled_mirrored; + } theme_cache; + protected: Size2 get_icon_size() const; virtual Size2 get_minimum_size() const override; + + virtual void _update_theme_item_cache() override; void _notification(int p_what); public: diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 5e8d5a567f..06819283fa 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2330,6 +2330,9 @@ void Control::_invalidate_theme_cache() { data.theme_constant_cache.clear(); } +void Control::_update_theme_item_cache() { +} + void Control::set_theme(const Ref<Theme> &p_theme) { if (data.theme == p_theme) { return; @@ -3103,6 +3106,11 @@ void Control::remove_child_notify(Node *p_child) { void Control::_notification(int p_notification) { switch (p_notification) { + case NOTIFICATION_POSTINITIALIZE: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + } break; + case NOTIFICATION_ENTER_TREE: { // Need to defer here, because theme owner information might be set in // add_child_notify, which doesn't get called until right after this. @@ -3236,6 +3244,7 @@ void Control::_notification(int p_notification) { case NOTIFICATION_THEME_CHANGED: { emit_signal(SceneStringNames::get_singleton()->theme_changed); _invalidate_theme_cache(); + _update_theme_item_cache(); update_minimum_size(); queue_redraw(); } break; @@ -3257,6 +3266,7 @@ void Control::_notification(int p_notification) { if (is_inside_tree()) { data.is_rtl_dirty = true; _invalidate_theme_cache(); + _update_theme_item_cache(); _size_changed(); } } break; diff --git a/scene/gui/control.h b/scene/gui/control.h index 6215594ae0..ac5d481f3a 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -325,6 +325,10 @@ protected: bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + // Theming. + + virtual void _update_theme_item_cache(); + // Internationalization. virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index b4e0747ab8..0a1aeeda71 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -50,6 +50,14 @@ void AcceptDialog::_parent_focused() { } } +void AcceptDialog::_update_theme_item_cache() { + Window::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.margin = get_theme_constant(SNAME("margin")); + theme_cache.button_margin = get_theme_constant(SNAME("button_margin")); +} + void AcceptDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -69,7 +77,10 @@ void AcceptDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - bg->add_theme_style_override("panel", bg->get_theme_stylebox(SNAME("panel"), SNAME("AcceptDialog"))); + bg->add_theme_style_override("panel", theme_cache.panel_style); + + label->set_begin(Point2(theme_cache.margin, theme_cache.margin)); + label->set_end(Point2(-theme_cache.margin, -theme_cache.button_margin - 10)); } break; case NOTIFICATION_EXIT_TREE: { @@ -185,12 +196,12 @@ void AcceptDialog::_update_child_rects() { if (label->get_text().is_empty()) { label_size.height = 0; } - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); + Size2 size = get_size(); Size2 hminsize = hbc->get_combined_minimum_size(); - Vector2 cpos(margin, margin + label_size.height); - Vector2 csize(size.x - margin * 2, size.y - margin * 3 - hminsize.y - label_size.height); + Vector2 cpos(theme_cache.margin, theme_cache.margin + label_size.height); + Vector2 csize(size.x - theme_cache.margin * 2, size.y - theme_cache.margin * 3 - hminsize.y - label_size.height); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -206,7 +217,7 @@ void AcceptDialog::_update_child_rects() { c->set_size(csize); } - cpos.y += csize.y + margin; + cpos.y += csize.y + theme_cache.margin; csize.y = hminsize.y; hbc->set_position(cpos); @@ -217,7 +228,6 @@ void AcceptDialog::_update_child_rects() { } Size2 AcceptDialog::_get_contents_minimum_size() const { - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); Size2 minsize = label->get_combined_minimum_size(); for (int i = 0; i < get_child_count(); i++) { @@ -238,8 +248,8 @@ Size2 AcceptDialog::_get_contents_minimum_size() const { Size2 hminsize = hbc->get_combined_minimum_size(); minsize.x = MAX(hminsize.x, minsize.x); minsize.y += hminsize.y; - minsize.x += margin * 2; - minsize.y += margin * 3; //one as separation between hbc and child + minsize.x += theme_cache.margin * 2; + minsize.y += theme_cache.margin * 3; //one as separation between hbc and child Size2 wmsize = get_min_size(); minsize.x = MAX(wmsize.x, minsize.x); @@ -350,14 +360,9 @@ AcceptDialog::AcceptDialog() { hbc = memnew(HBoxContainer); - int margin = hbc->get_theme_constant(SNAME("margin"), SNAME("Dialogs")); - int button_margin = hbc->get_theme_constant(SNAME("button_margin"), SNAME("Dialogs")); - label = memnew(Label); label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); - label->set_begin(Point2(margin, margin)); - label->set_end(Point2(-margin, -button_margin - 10)); add_child(label, false, INTERNAL_MODE_FRONT); add_child(hbc, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 9ebf5ddfb2..8ba9c93861 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -52,6 +52,12 @@ class AcceptDialog : public Window { bool hide_on_ok = true; bool close_on_escape = true; + struct ThemeCache { + Ref<StyleBox> panel_style; + int margin = 0; + int button_margin = 0; + } theme_cache; + void _custom_action(const String &p_action); void _update_child_rects(); @@ -62,6 +68,7 @@ class AcceptDialog : public Window { protected: virtual Size2 _get_contents_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 2a56d6d222..a0cf5f5970 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -59,36 +59,26 @@ VBoxContainer *FileDialog::get_vbox() { return vbox; } -void FileDialog::_theme_changed() { - Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button")); - Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button")); - Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button")); - Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); - - dir_up->add_theme_color_override("icon_normal_color", font_color); - dir_up->add_theme_color_override("icon_hover_color", font_hover_color); - dir_up->add_theme_color_override("icon_focus_color", font_focus_color); - dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); - - dir_prev->add_theme_color_override("icon_color_normal", font_color); - dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); - dir_prev->add_theme_color_override("icon_focus_color", font_focus_color); - dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); - - dir_next->add_theme_color_override("icon_color_normal", font_color); - dir_next->add_theme_color_override("icon_color_hover", font_hover_color); - dir_next->add_theme_color_override("icon_focus_color", font_focus_color); - dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); - - refresh->add_theme_color_override("icon_normal_color", font_color); - refresh->add_theme_color_override("icon_hover_color", font_hover_color); - refresh->add_theme_color_override("icon_focus_color", font_focus_color); - refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); - - show_hidden->add_theme_color_override("icon_normal_color", font_color); - show_hidden->add_theme_color_override("icon_hover_color", font_hover_color); - show_hidden->add_theme_color_override("icon_focus_color", font_focus_color); - show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color); +void FileDialog::_update_theme_item_cache() { + ConfirmationDialog::_update_theme_item_cache(); + + theme_cache.parent_folder = get_theme_icon(SNAME("parent_folder")); + theme_cache.forward_folder = get_theme_icon(SNAME("forward_folder")); + theme_cache.back_folder = get_theme_icon(SNAME("back_folder")); + theme_cache.reload = get_theme_icon(SNAME("reload")); + theme_cache.toggle_hidden = get_theme_icon(SNAME("toggle_hidden")); + theme_cache.folder = get_theme_icon(SNAME("folder")); + theme_cache.file = get_theme_icon(SNAME("file")); + + theme_cache.folder_icon_modulate = get_theme_color(SNAME("folder_icon_modulate")); + theme_cache.file_icon_modulate = get_theme_color(SNAME("file_icon_modulate")); + theme_cache.files_disabled = get_theme_color(SNAME("files_disabled")); + + // TODO: Define own colors? + theme_cache.icon_normal_color = get_theme_color(SNAME("font_color"), SNAME("Button")); + theme_cache.icon_hover_color = get_theme_color(SNAME("font_hover_color"), SNAME("Button")); + theme_cache.icon_focus_color = get_theme_color(SNAME("font_focus_color"), SNAME("Button")); + theme_cache.icon_pressed_color = get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); } void FileDialog::_notification(int p_what) { @@ -99,18 +89,42 @@ void FileDialog::_notification(int p_what) { } } break; - case NOTIFICATION_ENTER_TREE: { - dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); + case NOTIFICATION_THEME_CHANGED: { + dir_up->set_icon(theme_cache.parent_folder); if (vbox->is_layout_rtl()) { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(theme_cache.forward_folder); + dir_next->set_icon(theme_cache.back_folder); } else { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(theme_cache.back_folder); + dir_next->set_icon(theme_cache.forward_folder); } - refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); - show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); - _theme_changed(); + refresh->set_icon(theme_cache.reload); + show_hidden->set_icon(theme_cache.toggle_hidden); + + dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + dir_up->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + dir_up->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_up->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + + dir_prev->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); + dir_prev->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); + dir_prev->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_prev->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + + dir_next->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); + dir_next->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); + dir_next->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + dir_next->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + + refresh->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + refresh->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + refresh->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + refresh->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + + show_hidden->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); + show_hidden->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); + show_hidden->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); + show_hidden->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); } break; case NOTIFICATION_TRANSLATION_CHANGED: { @@ -506,10 +520,6 @@ void FileDialog::update_file_list() { } TreeItem *root = tree->create_item(); - Ref<Texture2D> folder = vbox->get_theme_icon(SNAME("folder"), SNAME("FileDialog")); - Ref<Texture2D> file_icon = vbox->get_theme_icon(SNAME("file"), SNAME("FileDialog")); - const Color folder_color = vbox->get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog")); - const Color file_color = vbox->get_theme_color(SNAME("file_icon_modulate"), SNAME("FileDialog")); List<String> files; List<String> dirs; @@ -541,8 +551,8 @@ void FileDialog::update_file_list() { String &dir_name = dirs.front()->get(); TreeItem *ti = tree->create_item(root); ti->set_text(0, dir_name); - ti->set_icon(0, folder); - ti->set_icon_modulate(0, folder_color); + ti->set_icon(0, theme_cache.folder); + ti->set_icon_modulate(0, theme_cache.folder_icon_modulate); Dictionary d; d["name"] = dir_name; @@ -601,12 +611,12 @@ void FileDialog::update_file_list() { Ref<Texture2D> icon = get_icon_func(base_dir.path_join(files.front()->get())); ti->set_icon(0, icon); } else { - ti->set_icon(0, file_icon); + ti->set_icon(0, theme_cache.file); } - ti->set_icon_modulate(0, file_color); + ti->set_icon_modulate(0, theme_cache.file_icon_modulate); if (mode == FILE_MODE_OPEN_DIR) { - ti->set_custom_color(0, vbox->get_theme_color(SNAME("files_disabled"), SNAME("FileDialog"))); + ti->set_custom_color(0, theme_cache.files_disabled); ti->set_selectable(0, false); } Dictionary d; @@ -1006,7 +1016,6 @@ FileDialog::FileDialog() { vbox = memnew(VBoxContainer); add_child(vbox, false, INTERNAL_MODE_FRONT); - vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed)); mode = FILE_MODE_SAVE_FILE; set_title(TTRC("Save a File")); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 4945094086..5c892288b5 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -109,6 +109,25 @@ private: bool invalidated = true; + struct ThemeCache { + Ref<Texture2D> parent_folder; + Ref<Texture2D> forward_folder; + Ref<Texture2D> back_folder; + Ref<Texture2D> reload; + Ref<Texture2D> toggle_hidden; + Ref<Texture2D> folder; + Ref<Texture2D> file; + + Color folder_icon_modulate; + Color file_icon_modulate; + Color files_disabled; + + Color icon_normal_color; + Color icon_hover_color; + Color icon_focus_color; + Color icon_pressed_color; + } theme_cache; + void update_dir(); void update_file_name(); void update_file_list(); @@ -143,7 +162,7 @@ private: virtual void _post_popup() override; protected: - void _theme_changed(); + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 30b694da76..b0d15aa7f4 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -44,9 +44,6 @@ void FlowContainer::_resort() { return; } - int separation_horizontal = get_theme_constant(SNAME("h_separation")); - int separation_vertical = get_theme_constant(SNAME("v_separation")); - bool rtl = is_layout_rtl(); HashMap<Control *, Size2i> children_minsize_cache; @@ -74,14 +71,14 @@ void FlowContainer::_resort() { if (vertical) { /* VERTICAL */ if (children_in_current_line > 0) { - ofs.y += separation_vertical; + ofs.y += theme_cache.v_separation; } if (ofs.y + child_msc.y > current_container_size) { - line_length = ofs.y - separation_vertical; + line_length = ofs.y - theme_cache.v_separation; lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); // Move in new column (vertical line). - ofs.x += line_height + separation_horizontal; + ofs.x += line_height + theme_cache.h_separation; ofs.y = 0; line_height = 0; line_stretch_ratio_total = 0; @@ -96,14 +93,14 @@ void FlowContainer::_resort() { } else { /* HORIZONTAL */ if (children_in_current_line > 0) { - ofs.x += separation_horizontal; + ofs.x += theme_cache.h_separation; } if (ofs.x + child_msc.x > current_container_size) { - line_length = ofs.x - separation_horizontal; + line_length = ofs.x - theme_cache.h_separation; lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); // Move in new line. - ofs.y += line_height + separation_vertical; + ofs.y += line_height + theme_cache.v_separation; ofs.x = 0; line_height = 0; line_stretch_ratio_total = 0; @@ -146,11 +143,11 @@ void FlowContainer::_resort() { current_line_idx++; child_idx_in_line = 0; if (vertical) { - ofs.x += line_data.min_line_height + separation_horizontal; + ofs.x += line_data.min_line_height + theme_cache.h_separation; ofs.y = 0; } else { ofs.x = 0; - ofs.y += line_data.min_line_height + separation_vertical; + ofs.y += line_data.min_line_height + theme_cache.v_separation; } line_data = lines_data[current_line_idx]; } @@ -184,9 +181,9 @@ void FlowContainer::_resort() { fit_child_in_rect(child, child_rect); if (vertical) { /* VERTICAL */ - ofs.y += child_size.height + separation_vertical; + ofs.y += child_size.height + theme_cache.v_separation; } else { /* HORIZONTAL */ - ofs.x += child_size.width + separation_horizontal; + ofs.x += child_size.width + theme_cache.h_separation; } child_idx_in_line++; @@ -250,6 +247,13 @@ Vector<int> FlowContainer::get_allowed_size_flags_vertical() const { return flags; } +void FlowContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); +} + void FlowContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -268,14 +272,36 @@ void FlowContainer::_notification(int p_what) { } } +void FlowContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + int FlowContainer::get_line_count() const { return cached_line_count; } +void FlowContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool FlowContainer::is_vertical() const { + return vertical; +} + FlowContainer::FlowContainer(bool p_vertical) { vertical = p_vertical; } void FlowContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count); + + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index a2da43e071..536df27ad6 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -42,16 +42,28 @@ private: bool vertical = false; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + } theme_cache; + void _resort(); protected: - void _notification(int p_what); + bool is_fixed = false; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: int get_line_count() const; + void set_vertical(bool p_vertical); + bool is_vertical() const; + virtual Size2 get_minimum_size() const override; virtual Vector<int> get_allowed_size_flags_horizontal() const override; @@ -65,7 +77,7 @@ class HFlowContainer : public FlowContainer { public: HFlowContainer() : - FlowContainer(false) {} + FlowContainer(false) { is_fixed = true; } }; class VFlowContainer : public FlowContainer { @@ -73,7 +85,7 @@ class VFlowContainer : public FlowContainer { public: VFlowContainer() : - FlowContainer(true) {} + FlowContainer(true) { is_fixed = true; } }; #endif // FLOW_CONTAINER_H diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 3163d17846..8dc890eccb 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -31,6 +31,13 @@ #include "grid_container.h" #include "core/templates/rb_set.h" +void GridContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); +} + void GridContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -39,9 +46,6 @@ void GridContainer::_notification(int p_what) { RBSet<int> col_expanded; // Columns which have the SIZE_EXPAND flag set. RBSet<int> row_expanded; // Rows which have the SIZE_EXPAND flag set. - int hsep = get_theme_constant(SNAME("h_separation")); - int vsep = get_theme_constant(SNAME("v_separation")); - // Compute the per-column/per-row data. int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { @@ -98,8 +102,8 @@ void GridContainer::_notification(int p_what) { remaining_space.height -= E.value; } } - remaining_space.height -= vsep * MAX(max_row - 1, 0); - remaining_space.width -= hsep * MAX(max_col - 1, 0); + remaining_space.height -= theme_cache.v_separation * MAX(max_row - 1, 0); + remaining_space.width -= theme_cache.h_separation * MAX(max_col - 1, 0); bool can_fit = false; while (!can_fit && col_expanded.size() > 0) { @@ -202,7 +206,7 @@ void GridContainer::_notification(int p_what) { col_ofs = 0; } if (row > 0) { - row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep; + row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + theme_cache.v_separation; if (row_expanded.has(row - 1) && row - 1 < row_remaining_pixel_index) { // Apply the remaining pixel of the previous row. @@ -224,11 +228,11 @@ void GridContainer::_notification(int p_what) { if (rtl) { Point2 p(col_ofs - s.width, row_ofs); fit_child_in_rect(c, Rect2(p, s)); - col_ofs -= s.width + hsep; + col_ofs -= s.width + theme_cache.h_separation; } else { Point2 p(col_ofs, row_ofs); fit_child_in_rect(c, Rect2(p, s)); - col_ofs += s.width + hsep; + col_ofs += s.width + theme_cache.h_separation; } } } break; @@ -271,9 +275,6 @@ Size2 GridContainer::get_minimum_size() const { RBMap<int, int> col_minw; RBMap<int, int> row_minh; - int hsep = get_theme_constant(SNAME("h_separation")); - int vsep = get_theme_constant(SNAME("v_separation")); - int max_row = 0; int max_col = 0; @@ -313,8 +314,8 @@ Size2 GridContainer::get_minimum_size() const { ms.height += E.value; } - ms.height += vsep * max_row; - ms.width += hsep * max_col; + ms.height += theme_cache.v_separation * max_row; + ms.width += theme_cache.h_separation * max_col; return ms; } diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index 9d77f90ab3..522046f694 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -38,7 +38,14 @@ class GridContainer : public Container { int columns = 1; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index e3a27ba7d5..8c49353105 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -43,9 +43,9 @@ void ItemList::_shape(int p_idx) { } else { item.text_buf->set_direction((TextServer::Direction)item.text_direction); } - item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language); + item.text_buf->add_string(item.text, theme_cache.font, theme_cache.font_size, item.language); if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { item.text_buf->set_break_flags(TextServer::BREAK_NONE); } @@ -532,7 +532,7 @@ void ItemList::set_max_text_lines(int p_lines) { max_text_lines = p_lines; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); items.write[i].text_buf->set_max_lines_visible(p_lines); } else { items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); @@ -582,7 +582,7 @@ void ItemList::set_icon_mode(IconMode p_mode) { icon_mode = p_mode; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); } @@ -655,8 +655,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && mb->is_pressed()) { search_string = ""; //any mousepress cancels Vector2 pos = mb->get_position(); - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.bg_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { @@ -980,6 +979,31 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) { return Rect2(ofs_x, ofs_y, tex_width, tex_height); } +void ItemList::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); + + theme_cache.bg_style = get_theme_stylebox(SNAME("bg")); + theme_cache.bg_focus_style = get_theme_stylebox(SNAME("bg_focus")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.line_separation = get_theme_constant(SNAME("line_separation")); + theme_cache.icon_margin = get_theme_constant(SNAME("icon_margin")); + theme_cache.selected_style = get_theme_stylebox(SNAME("selected")); + theme_cache.selected_focus_style = get_theme_stylebox(SNAME("selected_focus")); + theme_cache.cursor_style = get_theme_stylebox(SNAME("cursor_unfocused")); + theme_cache.cursor_focus_style = get_theme_stylebox(SNAME("cursor")); + theme_cache.guide_color = get_theme_color(SNAME("guide_color")); +} + void ItemList::_notification(int p_what) { switch (p_what) { case NOTIFICATION_RESIZED: { @@ -998,37 +1022,32 @@ void ItemList::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - int mw = scroll_bar->get_minimum_size().x; scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw); scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); - scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP)); - scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM)); + scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.bg_style->get_margin(SIDE_TOP)); + scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.bg_style->get_margin(SIDE_BOTTOM)); Size2 size = get_size(); - int width = size.width - bg->get_minimum_size().width; + int width = size.width - theme_cache.bg_style->get_minimum_size().width; - draw_style_box(bg, Rect2(Point2(), size)); + draw_style_box(theme_cache.bg_style, Rect2(Point2(), size)); - int hseparation = get_theme_constant(SNAME("h_separation")); - int vseparation = get_theme_constant(SNAME("v_separation")); - int icon_margin = get_theme_constant(SNAME("icon_margin")); - int line_separation = get_theme_constant(SNAME("line_separation")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Ref<StyleBox> sbsel; + Ref<StyleBox> cursor; - Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected")); - Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused")); + if (has_focus()) { + sbsel = theme_cache.selected_focus_style; + cursor = theme_cache.cursor_focus_style; + } else { + sbsel = theme_cache.selected_style; + cursor = theme_cache.cursor_style; + } bool rtl = is_layout_rtl(); - Color guide_color = get_theme_color(SNAME("guide_color")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true); - draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size)); + draw_style_box(theme_cache.bg_focus_style, Rect2(Point2(), size)); RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false); } @@ -1047,9 +1066,9 @@ void ItemList::_notification(int p_what) { if (!items[i].text.is_empty()) { if (icon_mode == ICON_MODE_TOP) { - minsize.y += icon_margin; + minsize.y += theme_cache.icon_margin; } else { - minsize.x += icon_margin; + minsize.x += theme_cache.icon_margin; } } } @@ -1067,7 +1086,7 @@ void ItemList::_notification(int p_what) { if (icon_mode == ICON_MODE_TOP) { minsize.x = MAX(minsize.x, s.width); if (max_text_lines > 0) { - minsize.y += s.height + line_separation * max_text_lines; + minsize.y += s.height + theme_cache.line_separation * max_text_lines; } else { minsize.y += s.height; } @@ -1084,13 +1103,13 @@ void ItemList::_notification(int p_what) { max_column_width = MAX(max_column_width, minsize.x); // elements need to adapt to the selected size - minsize.y += vseparation; - minsize.x += hseparation; + minsize.y += theme_cache.v_separation; + minsize.x += theme_cache.h_separation; items.write[i].rect_cache.size = minsize; items.write[i].min_rect_cache.size = minsize; } - int fit_size = size.x - bg->get_minimum_size().width - mw; + int fit_size = size.x - theme_cache.bg_style->get_minimum_size().width - mw; //2-attempt best fit current_columns = 0x7FFFFFFF; @@ -1118,11 +1137,11 @@ void ItemList::_notification(int p_what) { } items.write[i].rect_cache.position = ofs; max_h = MAX(max_h, items[i].rect_cache.size.y); - ofs.x += items[i].rect_cache.size.x + hseparation; + ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation; col++; if (col == current_columns) { if (i < items.size() - 1) { - separators.push_back(ofs.y + max_h + vseparation / 2); + separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2); } for (int j = i; j >= 0 && col > 0; j--, col--) { @@ -1130,7 +1149,7 @@ void ItemList::_notification(int p_what) { } ofs.x = 0; - ofs.y += max_h + vseparation; + ofs.y += max_h + theme_cache.v_separation; col = 0; max_h = 0; } @@ -1141,10 +1160,10 @@ void ItemList::_notification(int p_what) { } if (all_fit) { - float page = MAX(0, size.height - bg->get_minimum_size().height); + float page = MAX(0, size.height - theme_cache.bg_style->get_minimum_size().height); float max = MAX(page, ofs.y + max_h); if (auto_height) { - auto_height_value = ofs.y + max_h + bg->get_minimum_size().height; + auto_height_value = ofs.y + max_h + theme_cache.bg_style->get_minimum_size().height; } scroll_bar->set_max(max); scroll_bar->set_page(page); @@ -1185,7 +1204,7 @@ void ItemList::_notification(int p_what) { ensure_selected_visible = false; - Vector2 base_ofs = bg->get_offset(); + Vector2 base_ofs = theme_cache.bg_style->get_offset(); base_ofs.y -= int(scroll_bar->get_value()); const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there @@ -1229,10 +1248,10 @@ void ItemList::_notification(int p_what) { if (items[i].selected) { Rect2 r = rcache; r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1245,10 +1264,10 @@ void ItemList::_notification(int p_what) { r.position += base_ofs; // Size rect to make the align the temperature colors - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1274,11 +1293,11 @@ void ItemList::_notification(int p_what) { if (icon_mode == ICON_MODE_TOP) { pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2); - pos.y += icon_margin; - text_ofs.y = icon_size.height + icon_margin * 2; + pos.y += theme_cache.icon_margin; + text_ofs.y = icon_size.height + theme_cache.icon_margin * 2; } else { pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2); - text_ofs.x = icon_size.width + icon_margin; + text_ofs.x = icon_size.width + theme_cache.icon_margin; } Rect2 draw_rect = Rect2(pos, icon_size); @@ -1329,7 +1348,7 @@ void ItemList::_notification(int p_what) { max_len = size2.x; } - Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color); + Color modulate = items[i].selected ? theme_cache.font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : theme_cache.font_color); if (items[i].disabled) { modulate.a *= 0.5; } @@ -1344,8 +1363,8 @@ void ItemList::_notification(int p_what) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color); } items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); @@ -1375,8 +1394,8 @@ void ItemList::_notification(int p_what) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT); } - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color); } if (width - text_ofs.x > 0) { @@ -1388,10 +1407,10 @@ void ItemList::_notification(int p_what) { if (select_mode == SELECT_MULTI && i == current) { Rect2 r = rcache; r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; + r.position.y -= theme_cache.v_separation / 2; + r.size.y += theme_cache.v_separation; + r.position.x -= theme_cache.h_separation / 2; + r.size.x += theme_cache.h_separation; if (rtl) { r.position.x = size.width - r.position.x - r.size.x; @@ -1423,7 +1442,7 @@ void ItemList::_notification(int p_what) { } const int y = base_ofs.y + separators[i]; - draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color); + draw_line(Vector2(theme_cache.bg_style->get_margin(SIDE_LEFT), y), Vector2(width, y), theme_cache.guide_color); } } break; } @@ -1435,8 +1454,7 @@ void ItemList::_scroll_changed(double) { int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const { Vector2 pos = p_pos; - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.bg_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { @@ -1473,8 +1491,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const { } Vector2 pos = p_pos; - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - pos -= bg->get_offset(); + pos -= theme_cache.bg_style->get_offset(); pos.y += scroll_bar->get_value(); if (is_layout_rtl()) { diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 21bd22759c..63bc771185 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -109,23 +109,45 @@ private: int max_columns = 1; Size2 fixed_icon_size; - Size2 max_item_size_cache; int defer_select_single = -1; - bool allow_rmb_select = false; - bool allow_reselect = false; real_t icon_scale = 1.0; bool do_autoscroll_to_bottom = false; + struct ThemeCache { + int h_separation = 0; + int v_separation = 0; + + Ref<StyleBox> bg_style; + Ref<StyleBox> bg_focus_style; + + Ref<Font> font; + int font_size = 0; + Color font_color; + Color font_selected_color; + int font_outline_size = 0; + Color font_outline_color; + + int line_separation = 0; + int icon_margin = 0; + Ref<StyleBox> selected_style; + Ref<StyleBox> selected_focus_style; + Ref<StyleBox> cursor_style; + Ref<StyleBox> cursor_focus_style; + Color guide_color; + } theme_cache; + void _scroll_changed(double); void _shape(int p_idx); protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index cd68d1f001..306ca3d340 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -71,7 +71,7 @@ bool Label::is_uppercase() const { } int Label::get_line_height(int p_line) const { - Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); + Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; if (p_line >= 0 && p_line < lines_rid.size()) { return TS->shaped_text_get_size(lines_rid[p_line]).y; } else if (lines_rid.size() > 0) { @@ -81,13 +81,13 @@ int Label::get_line_height(int p_line) const { } return h; } else { - int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; return font->get_height(font_size); } } void Label::_shape() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); + Ref<StyleBox> style = theme_cache.normal_style; int width = (get_size().width - style->get_minimum_size().width); if (dirty || font_dirty) { @@ -99,8 +99,8 @@ void Label::_shape() { } else { TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); } - const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); - int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; ERR_FAIL_COND(font.is_null()); String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { @@ -143,8 +143,9 @@ void Label::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); lines_rid.push_back(line); @@ -231,8 +232,8 @@ void Label::_shape() { } void Label::_update_visible() { - int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"), SNAME("Label")); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; + Ref<StyleBox> style = theme_cache.normal_style; int lines_visible = lines_rid.size(); if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { @@ -271,6 +272,22 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col } } +void Label::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal_style = get_theme_stylebox(SNAME("normal")); + theme_cache.font = get_theme_font(SNAME("font")); + + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.line_spacing = get_theme_constant(SNAME("line_spacing")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_shadow_color = get_theme_color(SNAME("font_shadow_color")); + theme_cache.font_shadow_offset = Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); +} + void Label::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { @@ -306,15 +323,15 @@ void Label::_notification(int p_what) { Size2 string_size; Size2 size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); - Color font_color = has_settings ? settings->get_font_color() : get_theme_color(SNAME("font_color")); - Color font_shadow_color = has_settings ? settings->get_shadow_color() : get_theme_color(SNAME("font_shadow_color")); - Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); - int line_spacing = has_settings ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing")); - Color font_outline_color = has_settings ? settings->get_outline_color() : get_theme_color(SNAME("font_outline_color")); - int outline_size = has_settings ? settings->get_outline_size() : get_theme_constant(SNAME("outline_size")); - int shadow_outline_size = has_settings ? settings->get_shadow_size() : get_theme_constant(SNAME("shadow_outline_size")); + Ref<StyleBox> style = theme_cache.normal_style; + Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + Color font_color = has_settings ? settings->get_font_color() : theme_cache.font_color; + Color font_shadow_color = has_settings ? settings->get_shadow_color() : theme_cache.font_shadow_color; + Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : theme_cache.font_shadow_offset; + int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; + Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color; + int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size; + int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size; bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); bool rtl_layout = is_layout_rtl(); @@ -561,12 +578,12 @@ Size2 Label::get_minimum_size() const { Size2 min_size = minsize; - const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); - int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; + int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); - Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); + Size2 min_style = theme_cache.normal_style->get_minimum_size(); if (autowrap_mode != TextServer::AUTOWRAP_OFF) { return Size2(1, (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style; } else { @@ -589,8 +606,8 @@ int Label::get_line_count() const { } int Label::get_visible_line_count() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing")); + Ref<StyleBox> style = theme_cache.normal_style; + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; int lines_visible = 0; float total_h = 0.0; for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { @@ -788,10 +805,10 @@ int Label::get_visible_characters() const { void Label::set_visible_ratio(float p_ratio) { if (visible_ratio != p_ratio) { - if (visible_ratio >= 1.0) { + if (p_ratio >= 1.0) { visible_chars = -1; visible_ratio = 1.0; - } else if (visible_ratio < 0.0) { + } else if (p_ratio < 0.0) { visible_chars = 0; visible_ratio = 0.0; } else { diff --git a/scene/gui/label.h b/scene/gui/label.h index 65831cab6e..b79c94a5ba 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -67,13 +67,28 @@ private: Ref<LabelSettings> settings; + struct ThemeCache { + Ref<StyleBox> normal_style; + Ref<Font> font; + + int font_size = 0; + int line_spacing = 0; + Color font_color; + Color font_shadow_color; + Point2 font_shadow_offset; + Color font_outline_color; + int font_outline_size; + int font_shadow_outline_size; + } theme_cache; + void _update_visible(); void _shape(); void _invalidate(); protected: - void _notification(int p_what); + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index aa8a825014..c2ce4bdb83 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -448,7 +448,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { _ensure_menu(); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); menu->set_position(get_screen_position() + pos); menu->reset_size(); menu->popup(); @@ -696,11 +696,38 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const { if (!clear_button_enabled || !has_point(p_pos)) { return false; } - Ref<Texture2D> icon = Control::get_theme_icon(SNAME("clear")); - int x_ofs = get_theme_stylebox(SNAME("normal"))->get_margin(SIDE_RIGHT); + Ref<Texture2D> icon = theme_cache.clear_icon; + int x_ofs = theme_cache.normal->get_margin(SIDE_RIGHT); return p_pos.x > get_size().width - icon->get_width() - x_ofs; } +void LineEdit::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.read_only = get_theme_stylebox(SNAME("read_only")); + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_uneditable_color = get_theme_color(SNAME("font_uneditable_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_placeholder_color = get_theme_color(SNAME("font_placeholder_color")); + theme_cache.caret_width = get_theme_constant(SNAME("caret_width")); + theme_cache.caret_color = get_theme_color(SNAME("caret_color")); + theme_cache.minimum_character_width = get_theme_constant(SNAME("minimum_character_width")); + theme_cache.selection_color = get_theme_color(SNAME("selection_color")); + + theme_cache.clear_icon = get_theme_icon(SNAME("clear")); + theme_cache.clear_button_color = get_theme_color(SNAME("clear_button_color")); + theme_cache.clear_button_color_pressed = get_theme_color(SNAME("clear_button_color_pressed")); + + theme_cache.base_scale = get_theme_default_base_scale(); +} + void LineEdit::_notification(int p_what) { switch (p_what) { #ifdef TOOLS_ENABLED @@ -771,19 +798,19 @@ void LineEdit::_notification(int p_what) { RID ci = get_canvas_item(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; if (!is_editable()) { - style = get_theme_stylebox(SNAME("read_only")); + style = theme_cache.read_only; draw_caret = false; } - Ref<Font> font = get_theme_font(SNAME("font")); + Ref<Font> font = theme_cache.font; if (!flat) { style->draw(ci, Rect2(Point2(), size)); } if (has_focus()) { - get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size)); + theme_cache.focus->draw(ci, Rect2(Point2(), size)); } int x_ofs = 0; @@ -821,25 +848,30 @@ void LineEdit::_notification(int p_what) { int y_area = height - style->get_minimum_size().height; int y_ofs = style->get_offset().y + (y_area - text_height) / 2; - Color selection_color = get_theme_color(SNAME("selection_color")); - Color font_color = get_theme_color(is_editable() ? SNAME("font_color") : SNAME("font_uneditable_color")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color caret_color = get_theme_color(SNAME("caret_color")); + Color selection_color = theme_cache.selection_color; + Color font_color; + if (is_editable()) { + font_color = theme_cache.font_color; + } else { + font_color = theme_cache.font_uneditable_color; + } + Color font_selected_color = theme_cache.font_selected_color; + Color caret_color = theme_cache.caret_color; // Draw placeholder color. if (using_placeholder) { - font_color = get_theme_color(SNAME("font_placeholder_color")); + font_color = theme_cache.font_placeholder_color; } bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9); if (display_clear_icon) { if (clear_button_status.press_attempt && clear_button_status.pressing_inside) { - color_icon = get_theme_color(SNAME("clear_button_color_pressed")); + color_icon = theme_cache.clear_button_color_pressed; } else { - color_icon = get_theme_color(SNAME("clear_button_color")); + color_icon = theme_cache.clear_button_color; } } @@ -879,8 +911,8 @@ void LineEdit::_notification(int p_what) { // Draw text. ofs.y += TS->shaped_text_get_ascent(text_rid); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.font_outline_size; if (outline_size > 0 && font_outline_color.a > 0) { Vector2 oofs = ofs; for (int i = 0; i < gl_size; i++) { @@ -918,7 +950,7 @@ void LineEdit::_notification(int p_what) { ofs.x = x_ofs + scroll_offset; if (draw_caret || drag_caret_force_displayed) { // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). - const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale()); + const int caret_width = theme_cache.caret_width * MAX(1, theme_cache.base_scale); if (ime_text.length() == 0) { // Normal caret. @@ -926,7 +958,7 @@ void LineEdit::_notification(int p_what) { if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) { // No carets, add one at the start. - int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); + int h = theme_cache.font->get_height(theme_cache.font_size); int y = style->get_offset().y + (y_area - h) / 2; if (rtl) { caret.l_dir = TextServer::DIRECTION_RTL; @@ -1193,7 +1225,7 @@ void LineEdit::shift_selection_check_post(bool p_shift) { } void LineEdit::set_caret_at_pixel_pos(int p_x) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1226,7 +1258,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); @@ -1241,7 +1273,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { } Vector2 LineEdit::get_caret_pixel_pos() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1274,7 +1306,7 @@ Vector2 LineEdit::get_caret_pixel_pos() { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); @@ -1559,7 +1591,7 @@ void LineEdit::set_caret_column(int p_column) { return; } - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); int x_ofs = 0; @@ -1593,7 +1625,7 @@ void LineEdit::set_caret_column(int p_column) { bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); @@ -1664,15 +1696,15 @@ void LineEdit::clear_internal() { } Size2 LineEdit::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + Ref<StyleBox> style = theme_cache.normal; + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; Size2 min_size; // Minimum size of text. float em_space_size = font->get_char_size('M', font_size).x; - min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size; + min_size.width = theme_cache.minimum_character_width * em_space_size; if (expand_to_text_length) { // Add a space because some fonts are too exact, and because caret needs a bit more when at the end. @@ -1688,9 +1720,8 @@ Size2 LineEdit::get_minimum_size() const { icon_max_width = right_icon->get_width(); } if (clear_button_enabled) { - Ref<Texture2D> clear_icon = Control::get_theme_icon(SNAME("clear")); - min_size.height = MAX(min_size.height, clear_icon->get_height()); - icon_max_width = MAX(icon_max_width, clear_icon->get_width()); + min_size.height = MAX(min_size.height, theme_cache.clear_icon->get_height()); + icon_max_width = MAX(icon_max_width, theme_cache.clear_icon->get_width()); } min_size.width += icon_max_width; @@ -2155,8 +2186,8 @@ void LineEdit::_shape() { } TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); - const Ref<Font> &font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = theme_cache.font; + int font_size = theme_cache.font_size; ERR_FAIL_COND(font.is_null()); TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language); for (int i = 0; i < TextServer::SPACING_MAX; i++) { @@ -2176,12 +2207,12 @@ void LineEdit::_shape() { void LineEdit::_fit_to_width() { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT); bool using_placeholder = text.is_empty() && ime_text.is_empty(); bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { - Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon; + Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; t_width -= r_icon->get_width(); } TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index dabdaa3395..38863e805c 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -174,6 +174,31 @@ private: double caret_blink_timer = 0.0; bool caret_blinking = false; + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> read_only; + Ref<StyleBox> focus; + + Ref<Font> font; + int font_size = 0; + Color font_color; + Color font_uneditable_color; + Color font_selected_color; + int font_outline_size; + Color font_outline_color; + Color font_placeholder_color; + int caret_width = 0; + Color caret_color; + int minimum_character_width = 0; + Color selection_color; + + Ref<Texture2D> clear_icon; + Color clear_button_color; + Color clear_button_color_pressed; + + int base_scale = 0; + } theme_cache; + bool _is_over_clear_button(const Point2 &p_pos) const; void _clear_undo_stack(); @@ -215,6 +240,7 @@ private: void _ensure_menu(); protected: + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index b0252ac685..7219e86f52 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -33,8 +33,8 @@ #include "core/string/translation.h" void LinkButton::_shape() { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + Ref<Font> font = theme_cache.font; + int font_size = theme_cache.font_size; text_buf->clear(); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { @@ -125,6 +125,26 @@ Size2 LinkButton::get_minimum_size() const { return text_buf->get_size(); } +void LinkButton::_update_theme_item_cache() { + BaseButton::_update_theme_item_cache(); + + theme_cache.focus = get_theme_stylebox(SNAME("focus")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.underline_spacing = get_theme_constant(SNAME("underline_spacing")); +} + void LinkButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { @@ -153,9 +173,9 @@ void LinkButton::_notification(int p_what) { switch (get_draw_mode()) { case DRAW_NORMAL: { if (has_focus()) { - color = get_theme_color(SNAME("font_focus_color")); + color = theme_cache.font_focus_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; @@ -163,35 +183,35 @@ void LinkButton::_notification(int p_what) { case DRAW_HOVER_PRESSED: case DRAW_PRESSED: { if (has_theme_color(SNAME("font_pressed_color"))) { - color = get_theme_color(SNAME("font_pressed_color")); + color = theme_cache.font_pressed_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } do_underline = underline_mode != UNDERLINE_MODE_NEVER; } break; case DRAW_HOVER: { - color = get_theme_color(SNAME("font_hover_color")); + color = theme_cache.font_hover_color; do_underline = underline_mode != UNDERLINE_MODE_NEVER; } break; case DRAW_DISABLED: { - color = get_theme_color(SNAME("font_disabled_color")); + color = theme_cache.font_disabled_color; do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; } break; } if (has_focus()) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("focus")); + Ref<StyleBox> style = theme_cache.focus; style->draw(ci, Rect2(Point2(), size)); } int width = text_buf->get_line_width(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; if (is_layout_rtl()) { if (outline_size > 0 && font_outline_color.a > 0) { text_buf->draw_outline(get_canvas_item(), Vector2(size.width - width, 0), outline_size, font_outline_color); @@ -205,7 +225,7 @@ void LinkButton::_notification(int p_what) { } if (do_underline) { - int underline_spacing = get_theme_constant(SNAME("underline_spacing")) + text_buf->get_line_underline_position(); + int underline_spacing = theme_cache.underline_spacing + text_buf->get_line_underline_position(); int y = text_buf->get_line_ascent() + underline_spacing; if (is_layout_rtl()) { diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 12a6a7618f..accd848163 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -55,10 +55,29 @@ private: TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; + struct ThemeCache { + Ref<StyleBox> focus; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + int underline_spacing = 0; + } theme_cache; + void _shape(); protected: virtual Size2 get_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index fac37a8634..60fe681824 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -30,12 +30,16 @@ #include "margin_container.h" -Size2 MarginContainer::get_minimum_size() const { - int margin_left = get_theme_constant(SNAME("margin_left")); - int margin_top = get_theme_constant(SNAME("margin_top")); - int margin_right = get_theme_constant(SNAME("margin_right")); - int margin_bottom = get_theme_constant(SNAME("margin_bottom")); +void MarginContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.margin_left = get_theme_constant(SNAME("margin_left")); + theme_cache.margin_top = get_theme_constant(SNAME("margin_top")); + theme_cache.margin_right = get_theme_constant(SNAME("margin_right")); + theme_cache.margin_bottom = get_theme_constant(SNAME("margin_bottom")); +} +Size2 MarginContainer::get_minimum_size() const { Size2 max; for (int i = 0; i < get_child_count(); i++) { @@ -59,8 +63,8 @@ Size2 MarginContainer::get_minimum_size() const { } } - max.width += (margin_left + margin_right); - max.height += (margin_top + margin_bottom); + max.width += (theme_cache.margin_left + theme_cache.margin_right); + max.height += (theme_cache.margin_top + theme_cache.margin_bottom); return max; } @@ -86,11 +90,6 @@ Vector<int> MarginContainer::get_allowed_size_flags_vertical() const { void MarginContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { - int margin_left = get_theme_constant(SNAME("margin_left")); - int margin_top = get_theme_constant(SNAME("margin_top")); - int margin_right = get_theme_constant(SNAME("margin_right")); - int margin_bottom = get_theme_constant(SNAME("margin_bottom")); - Size2 s = get_size(); for (int i = 0; i < get_child_count(); i++) { @@ -102,9 +101,9 @@ void MarginContainer::_notification(int p_what) { continue; } - int w = s.width - margin_left - margin_right; - int h = s.height - margin_top - margin_bottom; - fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h)); + int w = s.width - theme_cache.margin_left - theme_cache.margin_right; + int h = s.height - theme_cache.margin_top - theme_cache.margin_bottom; + fit_child_in_rect(c, Rect2(theme_cache.margin_left, theme_cache.margin_top, w, h)); } } break; diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index f8a3c5bb11..5c33785170 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -36,7 +36,16 @@ class MarginContainer : public Container { GDCLASS(MarginContainer, Container); + struct ThemeCache { + int margin_left = 0; + int margin_top = 0; + int margin_right = 0; + int margin_bottom = 0; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 8aad14e1ec..0613652bdf 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -149,10 +149,6 @@ void MenuBar::_open_popup(int p_index, bool p_focus_item) { void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (is_native_menu()) { - return; - } - if (!_is_focus_owner_in_shortcut_context()) { return; } @@ -253,7 +249,7 @@ void MenuBar::_update_submenu(const String &p_menu_name, PopupMenu *p_child) { DisplayServer::get_singleton()->global_menu_add_submenu_item(p_menu_name, p_child->get_item_text(i), p_menu_name + "/" + itos(i)); _update_submenu(p_menu_name + "/" + itos(i), pm); } else { - int index = DisplayServer::get_singleton()->global_menu_add_item(p_menu_name, p_child->get_item_text(i), callable_mp(p_child, &PopupMenu::activate_item), i); + int index = DisplayServer::get_singleton()->global_menu_add_item(p_menu_name, p_child->get_item_text(i), callable_mp(p_child, &PopupMenu::activate_item), Callable(), i); if (p_child->is_item_checkable(i)) { DisplayServer::get_singleton()->global_menu_set_item_checkable(p_menu_name, index, true); @@ -340,6 +336,35 @@ void MenuBar::_update_menu() { queue_redraw(); } +void MenuBar::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + theme_cache.normal_mirrored = get_theme_stylebox(SNAME("normal_mirrored")); + theme_cache.disabled = get_theme_stylebox(SNAME("disabled")); + theme_cache.disabled_mirrored = get_theme_stylebox(SNAME("disabled_mirrored")); + theme_cache.pressed = get_theme_stylebox(SNAME("pressed")); + theme_cache.pressed_mirrored = get_theme_stylebox(SNAME("pressed_mirrored")); + theme_cache.hover = get_theme_stylebox(SNAME("hover")); + theme_cache.hover_mirrored = get_theme_stylebox(SNAME("hover_mirrored")); + theme_cache.hover_pressed = get_theme_stylebox(SNAME("hover_pressed")); + theme_cache.hover_pressed_mirrored = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); +} + void MenuBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -376,12 +401,24 @@ void MenuBar::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { MutexLock lock(mutex); + if (is_native_menu()) { + // Handled by OS. + return; + } + Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted - get_global_position(); + if (pos == old_mouse_pos) { + return; + } + old_mouse_pos = pos; + int index = _get_index_at_point(pos); if (index >= 0 && index != active_menu) { selected_menu = index; focused_menu = selected_menu; - get_menu_popup(active_menu)->hide(); + if (active_menu >= 0) { + get_menu_popup(active_menu)->hide(); + } _open_popup(index); } } break; @@ -389,8 +426,7 @@ void MenuBar::_notification(int p_what) { } int MenuBar::_get_index_at_point(const Point2 &p_point) const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - int hsep = get_theme_constant(SNAME("h_separation")); + Ref<StyleBox> style = theme_cache.normal; int offset = 0; for (int i = 0; i < menu_cache.size(); i++) { if (menu_cache[i].hidden) { @@ -402,7 +438,7 @@ int MenuBar::_get_index_at_point(const Point2 &p_point) const { return i; } } - offset += size.x + hsep; + offset += size.x + theme_cache.h_separation; } return -1; } @@ -410,8 +446,7 @@ int MenuBar::_get_index_at_point(const Point2 &p_point) const { Rect2 MenuBar::_get_menu_item_rect(int p_index) const { ERR_FAIL_INDEX_V(p_index, menu_cache.size(), Rect2()); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - int hsep = get_theme_constant(SNAME("h_separation")); + Ref<StyleBox> style = theme_cache.normal; int offset = 0; for (int i = 0; i < p_index; i++) { @@ -419,7 +454,7 @@ Rect2 MenuBar::_get_menu_item_rect(int p_index) const { continue; } Size2 size = menu_cache[i].text_buf->get_size() + style->get_minimum_size(); - offset += size.x + hsep; + offset += size.x + theme_cache.h_separation; } return Rect2(Point2(offset, 0), menu_cache[p_index].text_buf->get_size() + style->get_minimum_size()); @@ -438,76 +473,76 @@ void MenuBar::_draw_menu_item(int p_index) { } Color color; - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; Rect2 item_rect = _get_menu_item_rect(p_index); if (menu_cache[p_index].disabled) { if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { - style = get_theme_stylebox(SNAME("disabled_mirrored")); + style = theme_cache.disabled_mirrored; } else { - style = get_theme_stylebox(SNAME("disabled")); + style = theme_cache.disabled; } if (!flat) { style->draw(ci, item_rect); } - color = get_theme_color(SNAME("font_disabled_color")); + color = theme_cache.font_disabled_color; } else if (hovered && pressed && has_theme_stylebox("hover_pressed")) { if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_pressed_mirrored")); + style = theme_cache.hover_pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("hover_pressed")); + style = theme_cache.hover_pressed; } if (!flat) { style->draw(ci, item_rect); } if (has_theme_color(SNAME("font_hover_pressed_color"))) { - color = get_theme_color(SNAME("font_hover_pressed_color")); + color = theme_cache.font_hover_pressed_color; } } else if (pressed) { if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { - style = get_theme_stylebox(SNAME("pressed_mirrored")); + style = theme_cache.pressed_mirrored; } else { - style = get_theme_stylebox(SNAME("pressed")); + style = theme_cache.pressed; } if (!flat) { style->draw(ci, item_rect); } if (has_theme_color(SNAME("font_pressed_color"))) { - color = get_theme_color(SNAME("font_pressed_color")); + color = theme_cache.font_pressed_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } } else if (hovered) { if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { - style = get_theme_stylebox(SNAME("hover_mirrored")); + style = theme_cache.hover_mirrored; } else { - style = get_theme_stylebox(SNAME("hover")); + style = theme_cache.hover; } if (!flat) { style->draw(ci, item_rect); } - color = get_theme_color(SNAME("font_hover_color")); + color = theme_cache.font_hover_color; } else { if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { - style = get_theme_stylebox(SNAME("normal_mirrored")); + style = theme_cache.normal_mirrored; } else { - style = get_theme_stylebox(SNAME("normal")); + style = theme_cache.normal; } if (!flat) { style->draw(ci, item_rect); } // Focus colors only take precedence over normal state. if (has_focus()) { - color = get_theme_color(SNAME("font_focus_color")); + color = theme_cache.font_focus_color; } else { - color = get_theme_color(SNAME("font_color")); + color = theme_cache.font_color; } } Point2 text_ofs = item_rect.position + Point2(style->get_margin(SIDE_LEFT), style->get_margin(SIDE_TOP)); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.outline_size; if (outline_size > 0 && font_outline_color.a > 0) { menu_cache[p_index].text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); } @@ -515,16 +550,13 @@ void MenuBar::_draw_menu_item(int p_index) { } void MenuBar::shape(Menu &p_menu) { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - p_menu.text_buf->clear(); if (text_direction == Control::TEXT_DIRECTION_INHERITED) { p_menu.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { p_menu.text_buf->set_direction((TextServer::Direction)text_direction); } - p_menu.text_buf->add_string(p_menu.name, font, font_size, language); + p_menu.text_buf->add_string(p_menu.name, theme_cache.font, theme_cache.font_size, language); } void MenuBar::_refresh_menu_names() { @@ -754,7 +786,7 @@ Size2 MenuBar::get_minimum_size() const { return Size2(); } - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<StyleBox> style = theme_cache.normal; Vector2 size; for (int i = 0; i < menu_cache.size(); i++) { @@ -766,7 +798,7 @@ Size2 MenuBar::get_minimum_size() const { size.x += sz.x; } if (menu_cache.size() > 1) { - size.x += get_theme_constant(SNAME("h_separation")) * (menu_cache.size() - 1); + size.x += theme_cache.h_separation * (menu_cache.size() - 1); } return size; } diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index b7d9933ab2..f7ef19e98b 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -73,8 +73,36 @@ class MenuBar : public Control { int active_menu = -1; Vector2i mouse_pos_adjusted; + Vector2i old_mouse_pos; ObjectID shortcut_context; + struct ThemeCache { + Ref<StyleBox> normal; + Ref<StyleBox> normal_mirrored; + Ref<StyleBox> disabled; + Ref<StyleBox> disabled_mirrored; + Ref<StyleBox> pressed; + Ref<StyleBox> pressed_mirrored; + Ref<StyleBox> hover; + Ref<StyleBox> hover_mirrored; + Ref<StyleBox> hover_pressed; + Ref<StyleBox> hover_pressed_mirrored; + + Ref<Font> font; + int font_size = 0; + int outline_size = 0; + Color font_outline_color; + + Color font_color; + Color font_disabled_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_focus_color; + + int h_separation = 0; + } theme_cache; + int _get_index_at_point(const Point2 &p_point) const; Rect2 _get_menu_item_rect(int p_index) const; void _draw_menu_item(int p_index); @@ -95,6 +123,7 @@ class MenuBar : public Control { protected: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index a87c46e8ac..0dd9666858 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -43,11 +43,11 @@ Size2 OptionButton::get_minimum_size() const { } if (has_theme_icon(SNAME("arrow"))) { - const Size2 padding = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); - const Size2 arrow_size = Control::get_theme_icon(SNAME("arrow"))->get_size(); + const Size2 padding = theme_cache.normal->get_minimum_size(); + const Size2 arrow_size = theme_cache.arrow_icon->get_size(); Size2 content_size = minsize - padding; - content_size.width += arrow_size.width + MAX(0, get_theme_constant(SNAME("h_separation"))); + content_size.width += arrow_size.width + MAX(0, theme_cache.h_separation); content_size.height = MAX(content_size.height, arrow_size.height); minsize = content_size + padding; @@ -56,35 +56,63 @@ Size2 OptionButton::get_minimum_size() const { return minsize; } +void OptionButton::_update_theme_item_cache() { + Button::_update_theme_item_cache(); + + theme_cache.normal = get_theme_stylebox(SNAME("normal")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_focus_color = get_theme_color(SNAME("font_focus_color")); + theme_cache.font_pressed_color = get_theme_color(SNAME("font_pressed_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_hover_pressed_color = get_theme_color(SNAME("font_hover_pressed_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + + theme_cache.arrow_icon = get_theme_icon(SNAME("arrow")); + theme_cache.arrow_margin = get_theme_constant(SNAME("arrow_margin")); + theme_cache.modulate_arrow = get_theme_constant(SNAME("modulate_arrow")); +} + void OptionButton::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + if (has_theme_icon(SNAME("arrow"))) { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width()); + } else { + _set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width()); + } + } + } break; + case NOTIFICATION_DRAW: { if (!has_theme_icon(SNAME("arrow"))) { return; } RID ci = get_canvas_item(); - Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow")); Color clr = Color(1, 1, 1); - if (get_theme_constant(SNAME("modulate_arrow"))) { + if (theme_cache.modulate_arrow) { switch (get_draw_mode()) { case DRAW_PRESSED: - clr = get_theme_color(SNAME("font_pressed_color")); + clr = theme_cache.font_pressed_color; break; case DRAW_HOVER: - clr = get_theme_color(SNAME("font_hover_color")); + clr = theme_cache.font_hover_color; break; case DRAW_HOVER_PRESSED: - clr = get_theme_color(SNAME("font_hover_pressed_color")); + clr = theme_cache.font_hover_pressed_color; break; case DRAW_DISABLED: - clr = get_theme_color(SNAME("font_disabled_color")); + clr = theme_cache.font_disabled_color; break; default: if (has_focus()) { - clr = get_theme_color(SNAME("font_focus_color")); + clr = theme_cache.font_focus_color; } else { - clr = get_theme_color(SNAME("font_color")); + clr = theme_cache.font_color; } } } @@ -93,11 +121,11 @@ void OptionButton::_notification(int p_what) { Point2 ofs; if (is_layout_rtl()) { - ofs = Point2(get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2))); + ofs = Point2(theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2))); } else { - ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin")), int(Math::abs((size.height - arrow->get_height()) / 2))); + ofs = Point2(size.width - theme_cache.arrow_icon->get_width() - theme_cache.arrow_margin, int(Math::abs((size.height - theme_cache.arrow_icon->get_height()) / 2))); } - arrow->draw(ci, ofs, clr); + theme_cache.arrow_icon->draw(ci, ofs, clr); } break; case NOTIFICATION_TRANSLATION_CHANGED: @@ -108,11 +136,11 @@ void OptionButton::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { if (has_theme_icon(SNAME("arrow"))) { if (is_layout_rtl()) { - _set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width()); + _set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width()); _set_internal_margin(SIDE_RIGHT, 0.f); } else { _set_internal_margin(SIDE_LEFT, 0.f); - _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width()); + _set_internal_margin(SIDE_RIGHT, theme_cache.arrow_icon->get_width()); } } _refresh_size_cache(); @@ -540,15 +568,6 @@ OptionButton::OptionButton(const String &p_text) : Button(p_text) { set_toggle_mode(true); set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); - if (is_layout_rtl()) { - if (has_theme_icon(SNAME("arrow"))) { - _set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width()); - } - } else { - if (has_theme_icon(SNAME("arrow"))) { - _set_internal_margin(SIDE_RIGHT, Control::get_theme_icon(SNAME("arrow"))->get_width()); - } - } set_action_mode(ACTION_MODE_BUTTON_PRESS); popup = memnew(PopupMenu); diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index cd709b8f5f..2c7e0510f5 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -43,6 +43,23 @@ class OptionButton : public Button { Vector2 _cached_size; bool cache_refresh_pending = false; + struct ThemeCache { + Ref<StyleBox> normal; + + Color font_color; + Color font_focus_color; + Color font_pressed_color; + Color font_hover_color; + Color font_hover_pressed_color; + Color font_disabled_color; + + int h_separation = 0; + + Ref<Texture2D> arrow_icon; + int arrow_margin = 0; + int modulate_arrow = 0; + } theme_cache; + void _focused(int p_which); void _selected(int p_which); void _select(int p_which, bool p_emit = false); @@ -54,6 +71,7 @@ class OptionButton : public Button { protected: Size2 get_minimum_size() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 1ac6cf57ab..09bc295513 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -30,12 +30,17 @@ #include "panel.h" +void Panel::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void Panel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - style->draw(ci, Rect2(Point2(), get_size())); + theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size())); } break; } } diff --git a/scene/gui/panel.h b/scene/gui/panel.h index 5d2e912680..f9bd721681 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -36,7 +36,13 @@ class Panel : public Control { GDCLASS(Panel, Control); + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index fe01712a89..eeaded1788 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -31,14 +31,6 @@ #include "panel_container.h" Size2 PanelContainer::get_minimum_size() const { - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - Size2 ms; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -54,8 +46,8 @@ Size2 PanelContainer::get_minimum_size() const { ms.height = MAX(ms.height, minsize.height); } - if (style.is_valid()) { - ms += style->get_minimum_size(); + if (theme_cache.panel_style.is_valid()) { + ms += theme_cache.panel_style->get_minimum_size(); } return ms; } @@ -78,35 +70,25 @@ Vector<int> PanelContainer::get_allowed_size_flags_vertical() const { return flags; } +void PanelContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void PanelContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - - style->draw(ci, Rect2(Point2(), get_size())); + theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size())); } break; case NOTIFICATION_SORT_CHILDREN: { - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } - Size2 size = get_size(); Point2 ofs; - if (style.is_valid()) { - size -= style->get_minimum_size(); - ofs += style->get_offset(); + if (theme_cache.panel_style.is_valid()) { + size -= theme_cache.panel_style->get_minimum_size(); + ofs += theme_cache.panel_style->get_offset(); } for (int i = 0; i < get_child_count(); i++) { diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index 8f07ce38eb..97d0cdc872 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -36,7 +36,12 @@ class PanelContainer : public Container { GDCLASS(PanelContainer, Container); + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; void _notification(int p_what); public: diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index c4396f636a..ceae3791f3 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -68,6 +68,12 @@ void Popup::_deinitialize_visible_parents() { } } +void Popup::_update_theme_item_cache() { + Window::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void Popup::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -186,8 +192,6 @@ Popup::~Popup() { } Size2 PopupPanel::_get_contents_minimum_size() const { - Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); - Size2 ms; for (int i = 0; i < get_child_count(); i++) { @@ -205,14 +209,12 @@ Size2 PopupPanel::_get_contents_minimum_size() const { ms.y = MAX(cms.y, ms.y); } - return ms + p->get_minimum_size(); + return ms + theme_cache.panel_style->get_minimum_size(); } void PopupPanel::_update_child_rects() { - Ref<StyleBox> p = get_theme_stylebox(SNAME("panel"), get_class_name()); - - Vector2 cpos(p->get_offset()); - Vector2 csize(get_size() - p->get_minimum_size()); + Vector2 cpos(theme_cache.panel_style->get_offset()); + Vector2 csize(get_size() - theme_cache.panel_style->get_minimum_size()); for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -234,15 +236,17 @@ void PopupPanel::_update_child_rects() { } } +void PopupPanel::_update_theme_item_cache() { + Popup::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); +} + void PopupPanel::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: case NOTIFICATION_THEME_CHANGED: { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); - } break; - - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_READY: { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); + panel->add_theme_style_override("panel", theme_cache.panel_style); _update_child_rects(); } break; diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 70eb8722d0..0d6ca25c18 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -43,6 +43,10 @@ class Popup : public Window { LocalVector<Window *> visible_parents; bool popped_up = false; + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + void _input_from_window(const Ref<InputEvent> &p_event); void _initialize_visible_parents(); @@ -52,6 +56,7 @@ protected: void _close_pressed(); virtual Rect2i _popup_adjust_rect() const override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); @@ -69,8 +74,14 @@ class PopupPanel : public Popup { Panel *panel = nullptr; + struct ThemeCache { + Ref<StyleBox> panel_style; + } theme_cache; + protected: void _update_child_rects(); + + virtual void _update_theme_item_cache() override; void _notification(int p_what); virtual Size2 _get_contents_minimum_size() const override; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index c3060bf242..9afcd566b9 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -48,15 +48,12 @@ String PopupMenu::_get_accel_text(const Item &p_item) const { } Size2 PopupMenu::_get_contents_minimum_size() const { - int vseparation = get_theme_constant(SNAME("v_separation")); - int hseparation = get_theme_constant(SNAME("h_separation")); - - Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container + Size2 minsize = theme_cache.panel_style->get_minimum_size(); // Accounts for margin in the margin container minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content float max_w = 0.0; float icon_w = 0.0; - int check_w = MAX(get_theme_icon(SNAME("checked"))->get_width(), get_theme_icon(SNAME("radio_checked"))->get_width()) + hseparation; + int check_w = MAX(theme_cache.checked->get_width(), theme_cache.radio_checked->get_width()) + theme_cache.h_separation; int accel_max_w = 0; bool has_check = false; @@ -67,23 +64,23 @@ Size2 PopupMenu::_get_contents_minimum_size() const { size.height = _get_item_height(i); icon_w = MAX(icon_size.width, icon_w); - size.width += items[i].indent * get_theme_constant(SNAME("indent")); + size.width += items[i].indent * theme_cache.indent; if (items[i].checkable_type && !items[i].separator) { has_check = true; } size.width += items[i].text_buf->get_size().x; - size.height += vseparation; + size.height += theme_cache.v_separation; if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { - int accel_w = hseparation * 2; + int accel_w = theme_cache.h_separation * 2; accel_w += items[i].accel_text_buf->get_size().x; accel_max_w = MAX(accel_w, accel_max_w); } if (!items[i].submenu.is_empty()) { - size.width += get_theme_icon(SNAME("submenu"))->get_width(); + size.width += theme_cache.submenu->get_width(); } max_w = MAX(max_w, size.width); @@ -91,7 +88,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { minsize.height += size.height; } - int item_side_padding = get_theme_constant(SNAME("item_start_padding")) + get_theme_constant(SNAME("item_end_padding")); + int item_side_padding = theme_cache.item_start_padding + theme_cache.item_end_padding; minsize.width += max_w + icon_w + accel_max_w + item_side_padding; if (has_check) { @@ -113,33 +110,31 @@ int PopupMenu::_get_item_height(int p_item) const { int icon_height = items[p_item].get_icon_size().height; if (items[p_item].checkable_type && !items[p_item].separator) { - icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height())); + icon_height = MAX(icon_height, MAX(theme_cache.checked->get_height(), theme_cache.radio_checked->get_height())); } int text_height = items[p_item].text_buf->get_size().height; if (text_height == 0 && !items[p_item].separator) { - text_height = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size"))); + text_height = theme_cache.font->get_height(theme_cache.font_size); } int separator_height = 0; if (items[p_item].separator) { - separator_height = MAX(get_theme_stylebox(SNAME("separator"))->get_minimum_size().height, MAX(get_theme_stylebox(SNAME("labeled_separator_left"))->get_minimum_size().height, get_theme_stylebox(SNAME("labeled_separator_right"))->get_minimum_size().height)); + separator_height = MAX(theme_cache.separator_style->get_minimum_size().height, MAX(theme_cache.labeled_separator_left->get_minimum_size().height, theme_cache.labeled_separator_right->get_minimum_size().height)); } return MAX(separator_height, MAX(text_height, icon_height)); } int PopupMenu::_get_items_total_height() const { - int vsep = get_theme_constant(SNAME("v_separation")); - // Get total height of all items by taking max of icon height and font height int items_total_height = 0; for (int i = 0; i < items.size(); i++) { - items_total_height += _get_item_height(i) + vsep; + items_total_height += _get_item_height(i) + theme_cache.v_separation; } // Subtract a separator which is not needed for the last item. - return items_total_height - vsep; + return items_total_height - theme_cache.v_separation; } int PopupMenu::_get_mouse_over(const Point2 &p_over) const { @@ -147,18 +142,15 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { return -1; } - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); // Accounts for margin in the margin container - - int vseparation = get_theme_constant(SNAME("v_separation")); - - Point2 ofs = style->get_offset() + Point2(0, vseparation / 2); + // Accounts for margin in the margin container + Point2 ofs = theme_cache.panel_style->get_offset() + Point2(0, theme_cache.v_separation / 2); if (ofs.y > p_over.y) { return -1; } for (int i = 0; i < items.size(); i++) { - ofs.y += i > 0 ? vseparation : (float)vseparation / 2; + ofs.y += i > 0 ? theme_cache.v_separation : (float)theme_cache.v_separation / 2; ofs.y += _get_item_height(i); @@ -179,9 +171,6 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { return; // Already visible. } - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - int vsep = get_theme_constant(SNAME("v_separation")); - Point2 this_pos = get_position(); Rect2 this_rect(this_pos, get_size()); @@ -231,7 +220,7 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Set autohide areas. Rect2 safe_area = this_rect; - safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2; + safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2; safe_area.size.y = items[p_over]._height_cache; DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area); @@ -240,11 +229,11 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { // Autohide area above the submenu item. submenu_pum->clear_autohide_areas(); - submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2)); + submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2)); // If there is an area below the submenu item, add an autohide area there. if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) { - int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height; + int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height; submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from)); } } @@ -442,7 +431,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (m->get_velocity().is_equal_approx(Vector2())) { + if (m->get_velocity().is_zero_approx()) { return; } activated_by_keyboard = false; @@ -528,34 +517,17 @@ void PopupMenu::_draw_items() { margin_size.height = margin_container->get_theme_constant(SNAME("margin_top")) + margin_container->get_theme_constant(SNAME("margin_bottom")); // Space between the item content and the sides of popup menu. - int item_start_padding = get_theme_constant(SNAME("item_start_padding")); - int item_end_padding = get_theme_constant(SNAME("item_end_padding")); - bool rtl = control->is_layout_rtl(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover")); // In Item::checkable_type enum order (less the non-checkable member), with disabled repeated at the end. - Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")), get_theme_icon(SNAME("checked_disabled")), get_theme_icon(SNAME("radio_checked_disabled")) }; - Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")), get_theme_icon(SNAME("unchecked_disabled")), get_theme_icon(SNAME("radio_unchecked_disabled")) }; + Ref<Texture2D> check[] = { theme_cache.checked, theme_cache.radio_checked, theme_cache.checked_disabled, theme_cache.radio_checked_disabled }; + Ref<Texture2D> uncheck[] = { theme_cache.unchecked, theme_cache.radio_unchecked, theme_cache.unchecked_disabled, theme_cache.radio_unchecked_disabled }; Ref<Texture2D> submenu; if (rtl) { - submenu = get_theme_icon(SNAME("submenu_mirrored")); + submenu = theme_cache.submenu_mirrored; } else { - submenu = get_theme_icon(SNAME("submenu")); + submenu = theme_cache.submenu; } - Ref<StyleBox> separator = get_theme_stylebox(SNAME("separator")); - Ref<StyleBox> labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left")); - Ref<StyleBox> labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right")); - - int vseparation = get_theme_constant(SNAME("v_separation")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - Color font_accelerator_color = get_theme_color(SNAME("font_accelerator_color")); - Color font_hover_color = get_theme_color(SNAME("font_hover_color")); - Color font_separator_color = get_theme_color(SNAME("font_separator_color")); - float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0; float display_width = control->get_size().width - scroll_width; @@ -574,7 +546,7 @@ void PopupMenu::_draw_items() { } } if (icon_ofs > 0.0) { - icon_ofs += hseparation; + icon_ofs += theme_cache.h_separation; } float check_ofs = 0.0; @@ -583,7 +555,7 @@ void PopupMenu::_draw_items() { check_ofs = MAX(check_ofs, check[i]->get_width()); check_ofs = MAX(check_ofs, uncheck[i]->get_width()); } - check_ofs += hseparation; + check_ofs += theme_cache.h_separation; } Point2 ofs = Point2(); @@ -591,7 +563,7 @@ void PopupMenu::_draw_items() { // Loop through all items and draw each. for (int i = 0; i < items.size(); i++) { // For the first item only add half a separation. For all other items, add a whole separation to the offset. - ofs.y += i > 0 ? vseparation : (float)vseparation / 2; + ofs.y += i > 0 ? theme_cache.v_separation : (float)theme_cache.v_separation / 2; _shape_item(i); @@ -601,47 +573,47 @@ void PopupMenu::_draw_items() { if (i == mouse_over) { if (rtl) { - hover->draw(ci, Rect2(item_ofs + Point2(scroll_width, -vseparation / 2), Size2(display_width, h + vseparation))); + theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(scroll_width, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation))); } else { - hover->draw(ci, Rect2(item_ofs + Point2(0, -vseparation / 2), Size2(display_width, h + vseparation))); + theme_cache.hover_style->draw(ci, Rect2(item_ofs + Point2(0, -theme_cache.v_separation / 2), Size2(display_width, h + theme_cache.v_separation))); } } String text = items[i].xl_text; // Separator - item_ofs.x += items[i].indent * get_theme_constant(SNAME("indent")); + item_ofs.x += items[i].indent * theme_cache.indent; if (items[i].separator) { if (!text.is_empty() || !items[i].icon.is_null()) { - int content_size = items[i].text_buf->get_size().width + hseparation * 2; + int content_size = items[i].text_buf->get_size().width + theme_cache.h_separation * 2; if (!items[i].icon.is_null()) { - content_size += icon_size.width + hseparation; + content_size += icon_size.width + theme_cache.h_separation; } int content_center = display_width / 2; int content_left = content_center - content_size / 2; int content_right = content_center + content_size / 2; if (content_left > item_ofs.x) { - int sep_h = labeled_separator_left->get_center_size().height + labeled_separator_left->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_left->get_center_size().height + theme_cache.labeled_separator_left->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); + theme_cache.labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); } if (content_right < display_width) { - int sep_h = labeled_separator_right->get_center_size().height + labeled_separator_right->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_right->get_center_size().height + theme_cache.labeled_separator_right->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); + theme_cache.labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); } } else { - int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; + int sep_h = theme_cache.separator_style->get_center_size().height + theme_cache.separator_style->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); + theme_cache.separator_style->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } } Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1); // For non-separator items, add some padding for the content. - item_ofs.x += item_start_padding; + item_ofs.x += theme_cache.item_start_padding; // Checkboxes if (items[i].checkable_type && !items[i].separator) { @@ -659,7 +631,7 @@ void PopupMenu::_draw_items() { // Icon if (!items[i].icon.is_null()) { if (items[i].separator) { - separator_ofs -= (icon_size.width + hseparation) / 2; + separator_ofs -= (icon_size.width + theme_cache.h_separation) / 2; if (rtl) { items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); @@ -678,61 +650,55 @@ void PopupMenu::_draw_items() { // Submenu arrow on right hand side. if (!items[i].submenu.is_empty()) { if (rtl) { - submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } else { - submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width() - item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - submenu->get_width() - theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } } - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - // Text if (items[i].separator) { - Color font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color")); - int separator_outline_size = get_theme_constant(SNAME("separator_outline_size")); - if (!text.is_empty()) { Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); if (!rtl && !items[i].icon.is_null()) { - text_pos.x += icon_size.width + hseparation; + text_pos.x += icon_size.width + theme_cache.h_separation; } - if (separator_outline_size > 0 && font_separator_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, separator_outline_size, font_separator_outline_color); + if (theme_cache.font_separator_outline_size > 0 && theme_cache.font_separator_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_separator_outline_size, theme_cache.font_separator_outline_color); } - items[i].text_buf->draw(ci, text_pos, font_separator_color); + items[i].text_buf->draw(ci, text_pos, theme_cache.font_separator_color); } } else { item_ofs.x += icon_ofs + check_ofs; if (rtl) { Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color)); + items[i].text_buf->draw(ci, text_pos, items[i].disabled ? theme_cache.font_disabled_color : (i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_color)); } else { Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color)); + items[i].text_buf->draw(ci, text_pos, items[i].disabled ? theme_cache.font_disabled_color : (i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_color)); } } // Accelerator / Shortcut if (items[i].accel != Key::NONE || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { if (rtl) { - item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding; + item_ofs.x = scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding; } else { - item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - item_end_padding; + item_ofs.x = display_width - theme_cache.panel_style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - theme_cache.item_end_padding; } Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].accel_text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + items[i].accel_text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color); + items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? theme_cache.font_hover_color : theme_cache.font_accelerator_color); } // Cache the item vertical offset from the first item and the height. @@ -744,9 +710,8 @@ void PopupMenu::_draw_items() { } void PopupMenu::_draw_background() { - Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); RID ci2 = margin_container->get_canvas_item(); - style->draw(ci2, Rect2(Point2(), margin_container->get_size())); + theme_cache.panel_style->draw(ci2, Rect2(Point2(), margin_container->get_size())); } void PopupMenu::_minimum_lifetime_timeout() { @@ -778,8 +743,8 @@ void PopupMenu::_shape_item(int p_item) { if (items.write[p_item].dirty) { items.write[p_item].text_buf->clear(); - Ref<Font> font = get_theme_font(items[p_item].separator ? SNAME("font_separator") : SNAME("font")); - int font_size = get_theme_font_size(items[p_item].separator ? SNAME("font_separator_size") : SNAME("font_size")); + Ref<Font> font = items[p_item].separator ? theme_cache.font_separator : theme_cache.font; + int font_size = items[p_item].separator ? theme_cache.font_separator_size : theme_cache.font_size; if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) { items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -821,6 +786,51 @@ void PopupMenu::remove_child_notify(Node *p_child) { _menu_changed(); } +void PopupMenu::_update_theme_item_cache() { + Popup::_update_theme_item_cache(); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.hover_style = get_theme_stylebox(SNAME("hover")); + + theme_cache.separator_style = get_theme_stylebox(SNAME("separator")); + theme_cache.labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left")); + theme_cache.labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right")); + + theme_cache.v_separation = get_theme_constant(SNAME("v_separation")); + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + theme_cache.indent = get_theme_constant(SNAME("indent")); + theme_cache.item_start_padding = get_theme_constant(SNAME("item_start_padding")); + theme_cache.item_end_padding = get_theme_constant(SNAME("item_end_padding")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.checked_disabled = get_theme_icon(SNAME("checked_disabled")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.unchecked_disabled = get_theme_icon(SNAME("unchecked_disabled")); + theme_cache.radio_checked = get_theme_icon(SNAME("radio_checked")); + theme_cache.radio_checked_disabled = get_theme_icon(SNAME("radio_checked_disabled")); + theme_cache.radio_unchecked = get_theme_icon(SNAME("radio_unchecked")); + theme_cache.radio_unchecked_disabled = get_theme_icon(SNAME("radio_unchecked_disabled")); + + theme_cache.submenu = get_theme_icon(SNAME("submenu")); + theme_cache.submenu_mirrored = get_theme_icon(SNAME("submenu_mirrored")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_separator = get_theme_font(SNAME("font_separator")); + theme_cache.font_separator_size = get_theme_font_size(SNAME("font_separator_size")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_hover_color = get_theme_color(SNAME("font_hover_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_accelerator_color = get_theme_color(SNAME("font_accelerator_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.font_separator_color = get_theme_color(SNAME("font_separator_color")); + theme_cache.font_separator_outline_size = get_theme_constant(SNAME("separator_outline_size")); + theme_cache.font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color")); +} + void PopupMenu::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -909,11 +919,10 @@ void PopupMenu::_notification(int p_what) { } // Set margin on the margin container - Ref<StyleBox> panel_style = get_theme_stylebox(SNAME("panel")); - margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT)); - margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP)); - margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT)); - margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM)); + margin_container->add_theme_constant_override("margin_left", theme_cache.panel_style->get_margin(Side::SIDE_LEFT)); + margin_container->add_theme_constant_override("margin_top", theme_cache.panel_style->get_margin(Side::SIDE_TOP)); + margin_container->add_theme_constant_override("margin_right", theme_cache.panel_style->get_margin(Side::SIDE_RIGHT)); + margin_container->add_theme_constant_override("margin_bottom", theme_cache.panel_style->get_margin(Side::SIDE_BOTTOM)); } } break; } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index d3ad0762e4..c8c598bd50 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -129,6 +129,49 @@ class PopupMenu : public Popup { ScrollContainer *scroll_container = nullptr; Control *control = nullptr; + struct ThemeCache { + Ref<StyleBox> panel_style; + Ref<StyleBox> hover_style; + + Ref<StyleBox> separator_style; + Ref<StyleBox> labeled_separator_left; + Ref<StyleBox> labeled_separator_right; + + int v_separation = 0; + int h_separation = 0; + int indent = 0; + int item_start_padding = 0; + int item_end_padding = 0; + + Ref<Texture2D> checked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked; + Ref<Texture2D> unchecked_disabled; + Ref<Texture2D> radio_checked; + Ref<Texture2D> radio_checked_disabled; + Ref<Texture2D> radio_unchecked; + Ref<Texture2D> radio_unchecked_disabled; + + Ref<Texture2D> submenu; + Ref<Texture2D> submenu_mirrored; + + Ref<Font> font; + int font_size = 0; + Ref<Font> font_separator; + int font_separator_size = 0; + + Color font_color; + Color font_hover_color; + Color font_disabled_color; + Color font_accelerator_color; + int font_outline_size = 0; + Color font_outline_color; + + Color font_separator_color; + int font_separator_outline_size = 0; + Color font_separator_outline_color; + } theme_cache; + void _draw_items(); void _draw_background(); @@ -137,6 +180,8 @@ class PopupMenu : public Popup { void _menu_changed(); protected: + virtual void _update_theme_item_cache() override; + virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; void _notification(int p_what); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 63a2db4569..fe609dd834 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -33,18 +33,13 @@ #include "scene/resources/text_line.h" Size2 ProgressBar::get_minimum_size() const { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - - Size2 minimum_size = bg->get_minimum_size(); - minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height); - minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width); + Size2 minimum_size = theme_cache.bg_style->get_minimum_size(); + minimum_size.height = MAX(minimum_size.height, theme_cache.fg_style->get_minimum_size().height); + minimum_size.width = MAX(minimum_size.width, theme_cache.fg_style->get_minimum_size().width); if (percent_visible) { String txt = "100%"; - TextLine tl = TextLine(txt, font, font_size); - minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + tl.get_size().y); + TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); + minimum_size.height = MAX(minimum_size.height, theme_cache.bg_style->get_minimum_size().height + tl.get_size().y); } else { // this is needed, else the progressbar will collapse minimum_size.width = MAX(minimum_size.width, 1); minimum_size.height = MAX(minimum_size.height, 1); @@ -52,23 +47,30 @@ Size2 ProgressBar::get_minimum_size() const { return minimum_size; } +void ProgressBar::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.bg_style = get_theme_stylebox(SNAME("bg")); + theme_cache.fg_style = get_theme_stylebox(SNAME("fg")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); +} + void ProgressBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - Color font_color = get_theme_color(SNAME("font_color")); - - draw_style_box(bg, Rect2(Point2(), get_size())); + draw_style_box(theme_cache.bg_style, Rect2(Point2(), get_size())); float r = get_as_ratio(); switch (mode) { case FILL_BEGIN_TO_END: case FILL_END_TO_BEGIN: { - int mp = fg->get_minimum_size().width; + int mp = theme_cache.fg_style->get_minimum_size().width; int p = round(r * (get_size().width - mp)); // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL, // and left to right otherwise. And likewise for FILL_END_TO_BEGIN. @@ -76,23 +78,23 @@ void ProgressBar::_notification(int p_what) { if (p > 0) { if (right_to_left) { int p_remaining = round((1.0 - r) * (get_size().width - mp)); - draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + draw_style_box(theme_cache.fg_style, Rect2(Point2(p_remaining, 0), Size2(p + theme_cache.fg_style->get_minimum_size().width, get_size().height))); } else { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + draw_style_box(theme_cache.fg_style, Rect2(Point2(0, 0), Size2(p + theme_cache.fg_style->get_minimum_size().width, get_size().height))); } } } break; case FILL_TOP_TO_BOTTOM: case FILL_BOTTOM_TO_TOP: { - int mp = fg->get_minimum_size().height; + int mp = theme_cache.fg_style->get_minimum_size().height; int p = round(r * (get_size().height - mp)); if (p > 0) { if (mode == FILL_TOP_TO_BOTTOM) { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height))); + draw_style_box(theme_cache.fg_style, Rect2(Point2(0, 0), Size2(get_size().width, p + theme_cache.fg_style->get_minimum_size().height))); } else { int p_remaining = round((1.0 - r) * (get_size().height - mp)); - draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height))); + draw_style_box(theme_cache.fg_style, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + theme_cache.fg_style->get_minimum_size().height))); } } } break; @@ -102,14 +104,14 @@ void ProgressBar::_notification(int p_what) { if (percent_visible) { String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); - TextLine tl = TextLine(txt, font, font_size); + TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - if (outline_size > 0 && font_outline_color.a > 0) { - tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color); + + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + tl.draw_outline(get_canvas_item(), text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - tl.draw(get_canvas_item(), text_pos, font_color); + + tl.draw(get_canvas_item(), text_pos, theme_cache.font_color); } } break; } diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index 5ba21ad7d5..c79b901928 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -38,7 +38,20 @@ class ProgressBar : public Range { bool percent_visible = true; + struct ThemeCache { + Ref<StyleBox> bg_style; + Ref<StyleBox> fg_style; + + Ref<Font> font; + int font_size = 0; + Color font_color; + int font_outline_size = 0; + Color font_outline_color; + } theme_cache; + protected: + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c9a903153d..9b2e40e050 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -453,6 +453,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> case TextServer::AUTOWRAP_OFF: break; } + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; // Clear cache. l.text_buf->clear(); @@ -4944,10 +4945,10 @@ void RichTextLabel::set_visible_ratio(float p_ratio) { if (visible_ratio != p_ratio) { _stop_thread(); - if (visible_ratio >= 1.0) { + if (p_ratio >= 1.0) { visible_characters = -1; visible_ratio = 1.0; - } else if (visible_ratio < 0.0) { + } else if (p_ratio < 0.0) { visible_characters = 0; visible_ratio = 0.0; } else { diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2555318f39..374ce6feea 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -70,8 +70,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (b->is_pressed()) { double ofs = orientation == VERTICAL ? b->get_position().y : b->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<Texture2D> incr = theme_cache.increment_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width(); @@ -146,7 +146,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { if (drag.active) { double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); + Ref<Texture2D> decr = theme_cache.decrement_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); ofs -= decr_size; @@ -156,8 +156,8 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { set_as_ratio(drag.value_at_click + diff); } else { double ofs = orientation == VERTICAL ? m->get_position().y : m->get_position().x; - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<Texture2D> incr = theme_cache.increment_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); double incr_size = orientation == VERTICAL ? incr->get_height() : incr->get_width(); @@ -217,6 +217,24 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { } } +void ScrollBar::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.scroll_style = get_theme_stylebox(SNAME("scroll")); + theme_cache.scroll_focus_style = get_theme_stylebox(SNAME("scroll_focus")); + theme_cache.scroll_offset_style = get_theme_stylebox(SNAME("hscroll")); + theme_cache.grabber_style = get_theme_stylebox(SNAME("grabber")); + theme_cache.grabber_hl_style = get_theme_stylebox(SNAME("grabber_highlight")); + theme_cache.grabber_pressed_style = get_theme_stylebox(SNAME("grabber_pressed")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.increment_pressed_icon = get_theme_icon(SNAME("increment_pressed")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.decrement_pressed_icon = get_theme_icon(SNAME("decrement_pressed")); +} + void ScrollBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { @@ -225,30 +243,30 @@ void ScrollBar::_notification(int p_what) { Ref<Texture2D> decr, incr; if (decr_active) { - decr = get_theme_icon(SNAME("decrement_pressed")); + decr = theme_cache.decrement_pressed_icon; } else if (highlight == HIGHLIGHT_DECR) { - decr = get_theme_icon(SNAME("decrement_highlight")); + decr = theme_cache.decrement_hl_icon; } else { - decr = get_theme_icon(SNAME("decrement")); + decr = theme_cache.decrement_icon; } if (incr_active) { - incr = get_theme_icon(SNAME("increment_pressed")); + incr = theme_cache.increment_pressed_icon; } else if (highlight == HIGHLIGHT_INCR) { - incr = get_theme_icon(SNAME("increment_highlight")); + incr = theme_cache.increment_hl_icon; } else { - incr = get_theme_icon(SNAME("increment")); + incr = theme_cache.increment_icon; } - Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll")); + Ref<StyleBox> bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style; Ref<StyleBox> grabber; if (drag.active) { - grabber = get_theme_stylebox(SNAME("grabber_pressed")); + grabber = theme_cache.grabber_pressed_style; } else if (highlight == HIGHLIGHT_RANGE) { - grabber = get_theme_stylebox(SNAME("grabber_highlight")); + grabber = theme_cache.grabber_hl_style; } else { - grabber = get_theme_stylebox(SNAME("grabber")); + grabber = theme_cache.grabber_style; } Point2 ofs; @@ -414,7 +432,7 @@ void ScrollBar::_notification(int p_what) { } double ScrollBar::get_grabber_min_size() const { - Ref<StyleBox> grabber = get_theme_stylebox(SNAME("grabber")); + Ref<StyleBox> grabber = theme_cache.grabber_style; Size2 gminsize = grabber->get_minimum_size() + grabber->get_center_size(); return (orientation == VERTICAL) ? gminsize.height : gminsize.width; } @@ -435,17 +453,17 @@ double ScrollBar::get_area_size() const { switch (orientation) { case VERTICAL: { double area = get_size().height; - area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().height; - area -= get_theme_icon(SNAME("increment"))->get_height(); - area -= get_theme_icon(SNAME("decrement"))->get_height(); + area -= theme_cache.scroll_style->get_minimum_size().height; + area -= theme_cache.increment_icon->get_height(); + area -= theme_cache.decrement_icon->get_height(); area -= get_grabber_min_size(); return area; } break; case HORIZONTAL: { double area = get_size().width; - area -= get_theme_stylebox(SNAME("scroll"))->get_minimum_size().width; - area -= get_theme_icon(SNAME("increment"))->get_width(); - area -= get_theme_icon(SNAME("decrement"))->get_width(); + area -= theme_cache.scroll_style->get_minimum_size().width; + area -= theme_cache.increment_icon->get_width(); + area -= theme_cache.decrement_icon->get_width(); area -= get_grabber_min_size(); return area; } break; @@ -459,13 +477,13 @@ double ScrollBar::get_area_offset() const { double ofs = 0.0; if (orientation == VERTICAL) { - ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_TOP); - ofs += get_theme_icon(SNAME("decrement"))->get_height(); + ofs += theme_cache.scroll_offset_style->get_margin(SIDE_TOP); + ofs += theme_cache.decrement_icon->get_height(); } if (orientation == HORIZONTAL) { - ofs += get_theme_stylebox(SNAME("hscroll"))->get_margin(SIDE_LEFT); - ofs += get_theme_icon(SNAME("decrement"))->get_width(); + ofs += theme_cache.scroll_offset_style->get_margin(SIDE_LEFT); + ofs += theme_cache.decrement_icon->get_width(); } return ofs; @@ -476,9 +494,9 @@ double ScrollBar::get_grabber_offset() const { } Size2 ScrollBar::get_minimum_size() const { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - Ref<StyleBox> bg = get_theme_stylebox(SNAME("scroll")); + Ref<Texture2D> incr = theme_cache.increment_icon; + Ref<Texture2D> decr = theme_cache.decrement_icon; + Ref<StyleBox> bg = theme_cache.scroll_style; Size2 minsize; if (orientation == VERTICAL) { diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 1823f86a67..13ca62d7ff 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -86,14 +86,31 @@ class ScrollBar : public Range { double target_scroll = 0.0; bool smooth_scroll_enabled = false; + struct ThemeCache { + Ref<StyleBox> scroll_style; + Ref<StyleBox> scroll_focus_style; + Ref<StyleBox> scroll_offset_style; + Ref<StyleBox> grabber_style; + Ref<StyleBox> grabber_hl_style; + Ref<StyleBox> grabber_pressed_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> increment_pressed_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> decrement_pressed_icon; + } theme_cache; + void _drag_node_exit(); void _drag_node_input(const Ref<InputEvent> &p_input); virtual void gui_input(const Ref<InputEvent> &p_event) override; protected: - void _notification(int p_what); + virtual void _update_theme_item_cache() override; + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index cd595446bb..f68cebd657 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -35,7 +35,6 @@ #include "scene/main/window.h" Size2 ScrollContainer::get_minimum_size() const { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); Size2 min_size; // Calculated in this function, as it needs to traverse all child controls once to calculate; @@ -77,10 +76,16 @@ Size2 ScrollContainer::get_minimum_size() const { min_size.x += v_scroll->get_minimum_size().x; } - min_size += sb->get_minimum_size(); + min_size += theme_cache.bg_style->get_minimum_size(); return min_size; } +void ScrollContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.bg_style = get_theme_stylebox(SNAME("bg")); +} + void ScrollContainer::_cancel_drag() { set_physics_process_internal(false); drag_touching_deaccel = false; @@ -271,9 +276,8 @@ void ScrollContainer::_reposition_children() { Size2 size = get_size(); Point2 ofs; - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - size -= sb->get_minimum_size(); - ofs += sb->get_offset(); + size -= theme_cache.bg_style->get_minimum_size(); + ofs += theme_cache.bg_style->get_offset(); bool rtl = is_layout_rtl(); if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons @@ -337,8 +341,7 @@ void ScrollContainer::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - draw_style_box(sb, Rect2(Vector2(), get_size())); + draw_style_box(theme_cache.bg_style, Rect2(Vector2(), get_size())); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -413,8 +416,7 @@ void ScrollContainer::_notification(int p_what) { void ScrollContainer::update_scrollbars() { Size2 size = get_size(); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - size -= sb->get_minimum_size(); + size -= theme_cache.bg_style->get_minimum_size(); Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index bfa74cfd0f..fa1f09ab3f 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -69,9 +69,14 @@ private: int deadzone = 0; bool follow_focus = false; + struct ThemeCache { + Ref<StyleBox> bg_style; + } theme_cache; + void _cancel_drag(); protected: + virtual void _update_theme_item_cache() override; Size2 get_minimum_size() const override; void _gui_focus_changed(Control *p_control); diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index e3400d9c8f..8177c1e469 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -33,24 +33,30 @@ Size2 Separator::get_minimum_size() const { Size2 ms(3, 3); if (orientation == VERTICAL) { - ms.x = get_theme_constant(SNAME("separation")); + ms.x = theme_cache.separation; } else { // HORIZONTAL - ms.y = get_theme_constant(SNAME("separation")); + ms.y = theme_cache.separation; } return ms; } +void Separator::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); + theme_cache.separator_style = get_theme_stylebox(SNAME("separator")); +} + void Separator::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { Size2i size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("separator")); - Size2i ssize = style->get_minimum_size() + style->get_center_size(); + Size2i ssize = theme_cache.separator_style->get_minimum_size() + theme_cache.separator_style->get_center_size(); if (orientation == VERTICAL) { - style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y)); + theme_cache.separator_style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y)); } else { - style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y)); + theme_cache.separator_style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y)); } } break; } diff --git a/scene/gui/separator.h b/scene/gui/separator.h index e6578a4d04..44e18a3f00 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -35,8 +35,16 @@ class Separator : public Control { GDCLASS(Separator, Control); + struct ThemeCache { + int separation = 0; + Ref<StyleBox> separator_style; + } theme_cache; + protected: Orientation orientation = Orientation::HORIZONTAL; + + virtual void _update_theme_item_cache() override; + void _notification(int p_what); public: diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 7bf61e3541..ff3adfb9ac 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -33,11 +33,8 @@ #include "core/os/keyboard.h" Size2 Slider::get_minimum_size() const { - Ref<StyleBox> style = get_theme_stylebox(SNAME("slider")); - Size2i ss = style->get_minimum_size() + style->get_center_size(); - - Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber")); - Size2i rs = grabber->get_size(); + Size2i ss = theme_cache.slider_style->get_minimum_size() + theme_cache.slider_style->get_center_size(); + Size2i rs = theme_cache.grabber_icon->get_size(); if (orientation == HORIZONTAL) { return Size2i(ss.width, MAX(ss.height, rs.height)); @@ -58,7 +55,13 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber"); + Ref<Texture2D> grabber; + if (mouse_inside || has_focus()) { + grabber = theme_cache.grabber_hl_icon; + } else { + grabber = theme_cache.grabber_icon; + } + grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x; double grab_width = (double)grabber->get_size().width; @@ -95,7 +98,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { if (grab.active) { Size2i size = get_size(); - Ref<Texture2D> grabber = get_theme_icon(SNAME("grabber")); + Ref<Texture2D> grabber = theme_cache.grabber_icon; double motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos; if (orientation == VERTICAL) { motion = -motion; @@ -145,6 +148,19 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { } } +void Slider::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.slider_style = get_theme_stylebox(SNAME("slider")); + theme_cache.grabber_area_style = get_theme_stylebox(SNAME("grabber_area")); + theme_cache.grabber_area_hl_style = get_theme_stylebox(SNAME("grabber_area_highlight")); + + theme_cache.grabber_icon = get_theme_icon(SNAME("grabber")); + theme_cache.grabber_hl_icon = get_theme_icon(SNAME("grabber_highlight")); + theme_cache.grabber_disabled_icon = get_theme_icon(SNAME("grabber_disabled")); + theme_cache.tick_icon = get_theme_icon(SNAME("tick")); +} + void Slider::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { @@ -171,13 +187,30 @@ void Slider::_notification(int p_what) { case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2i size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("slider")); - bool highlighted = mouse_inside || has_focus(); - Ref<StyleBox> grabber_area = get_theme_stylebox(highlighted ? "grabber_area_highlight" : "grabber_area"); - Ref<Texture2D> grabber = get_theme_icon(editable ? (highlighted ? "grabber_highlight" : "grabber") : "grabber_disabled"); - Ref<Texture2D> tick = get_theme_icon(SNAME("tick")); double ratio = Math::is_nan(get_as_ratio()) ? 0 : get_as_ratio(); + Ref<StyleBox> style = theme_cache.slider_style; + Ref<Texture2D> tick = theme_cache.tick_icon; + + bool highlighted = mouse_inside || has_focus(); + Ref<Texture2D> grabber; + if (editable) { + if (highlighted) { + grabber = theme_cache.grabber_hl_icon; + } else { + grabber = theme_cache.grabber_icon; + } + } else { + grabber = theme_cache.grabber_disabled_icon; + } + + Ref<StyleBox> grabber_area; + if (highlighted) { + grabber_area = theme_cache.grabber_area_hl_style; + } else { + grabber_area = theme_cache.grabber_area_style; + } + if (orientation == VERTICAL) { int widget_width = style->get_minimum_size().width + style->get_center_size().width; double areasize = size.height - grabber->get_size().height; diff --git a/scene/gui/slider.h b/scene/gui/slider.h index 5abaee27aa..51adb354fb 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -49,11 +49,24 @@ class Slider : public Range { bool editable = true; bool scrollable = true; + struct ThemeCache { + Ref<StyleBox> slider_style; + Ref<StyleBox> grabber_area_style; + Ref<StyleBox> grabber_area_hl_style; + + Ref<Texture2D> grabber_icon; + Ref<Texture2D> grabber_hl_icon; + Ref<Texture2D> grabber_disabled_icon; + Ref<Texture2D> tick_icon; + } theme_cache; + protected: + bool ticks_on_borders = false; + virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); - bool ticks_on_borders = false; public: virtual Size2 get_minimum_size() const override; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 900249ddd9..fe14049d93 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -210,25 +210,29 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { } } +void SpinBox::_update_theme_item_cache() { + Range::_update_theme_item_cache(); + + theme_cache.updown_icon = get_theme_icon(SNAME("updown")); +} + void SpinBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<Texture2D> updown = get_theme_icon(SNAME("updown")); - - _adjust_width_for_icon(updown); + _adjust_width_for_icon(theme_cache.updown_icon); RID ci = get_canvas_item(); Size2i size = get_size(); if (is_layout_rtl()) { - updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2)); + theme_cache.updown_icon->draw(ci, Point2i(0, (size.height - theme_cache.updown_icon->get_height()) / 2)); } else { - updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); + theme_cache.updown_icon->draw(ci, Point2i(size.width - theme_cache.updown_icon->get_width(), (size.height - theme_cache.updown_icon->get_height()) / 2)); } } break; case NOTIFICATION_ENTER_TREE: { - _adjust_width_for_icon(get_theme_icon(SNAME("updown"))); + _adjust_width_for_icon(theme_cache.updown_icon); _value_changed(0); } break; diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 3fcb85ac99..c2f2ac3f5a 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -69,9 +69,14 @@ class SpinBox : public Range { inline void _adjust_width_for_icon(const Ref<Texture2D> &icon); + struct ThemeCache { + Ref<Texture2D> updown_icon; + } theme_cache; + protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index b7e1f2a914..04bd5b3282 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -55,6 +55,18 @@ Control *SplitContainer::_getch(int p_idx) const { return nullptr; } +Ref<Texture2D> SplitContainer::_get_grabber_icon() const { + if (is_fixed) { + return theme_cache.grabber_icon; + } else { + if (vertical) { + return theme_cache.grabber_icon_v; + } else { + return theme_cache.grabber_icon_h; + } + } +} + void SplitContainer::_resort() { int axis = vertical ? 1 : 0; @@ -76,9 +88,8 @@ void SplitContainer::_resort() { bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND; // Determine the separation between items - Ref<Texture2D> g = get_theme_icon(SNAME("grabber")); - int sep = get_theme_constant(SNAME("separation")); - sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0; + Ref<Texture2D> g = _get_grabber_icon(); + int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; // Compute the minimum size Size2 ms_first = first->get_combined_minimum_size(); @@ -128,12 +139,9 @@ void SplitContainer::_resort() { } Size2 SplitContainer::get_minimum_size() const { - /* Calculate MINIMUM SIZE */ - Size2i minimum; - Ref<Texture2D> g = get_theme_icon(SNAME("grabber")); - int sep = get_theme_constant(SNAME("separation")); - sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(sep, vertical ? g->get_height() : g->get_width()) : 0; + Ref<Texture2D> g = _get_grabber_icon(); + int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0; for (int i = 0; i < 2; i++) { if (!_getch(i)) { @@ -162,6 +170,16 @@ Size2 SplitContainer::get_minimum_size() const { return minimum; } +void SplitContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.separation = get_theme_constant(SNAME("separation")); + theme_cache.autohide = get_theme_constant(SNAME("autohide")); + theme_cache.grabber_icon = get_theme_icon(SNAME("grabber")); + theme_cache.grabber_icon_h = get_theme_icon(SNAME("h_grabber")); + theme_cache.grabber_icon_v = get_theme_icon(SNAME("v_grabber")); +} + void SplitContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: @@ -175,7 +193,7 @@ void SplitContainer::_notification(int p_what) { case NOTIFICATION_MOUSE_EXIT: { mouse_inside = false; - if (get_theme_constant(SNAME("autohide"))) { + if (theme_cache.autohide) { queue_redraw(); } } break; @@ -185,7 +203,7 @@ void SplitContainer::_notification(int p_what) { return; } - if (collapsed || (!dragging && !mouse_inside && get_theme_constant(SNAME("autohide")))) { + if (collapsed || (!dragging && !mouse_inside && theme_cache.autohide)) { return; } @@ -193,8 +211,8 @@ void SplitContainer::_notification(int p_what) { return; } - int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? get_theme_constant(SNAME("separation")) : 0; - Ref<Texture2D> tex = get_theme_icon(SNAME("grabber")); + int sep = dragger_visibility != DRAGGER_HIDDEN_COLLAPSED ? theme_cache.separation : 0; + Ref<Texture2D> tex = theme_cache.grabber_icon; Size2 size = get_size(); if (vertical) { @@ -210,6 +228,12 @@ void SplitContainer::_notification(int p_what) { } } +void SplitContainer::_validate_property(PropertyInfo &p_property) const { + if (is_fixed && p_property.name == "vertical") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void SplitContainer::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -222,16 +246,14 @@ void SplitContainer::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - int sep = get_theme_constant(SNAME("separation")); - if (vertical) { - if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + sep) { + if (mb->get_position().y > middle_sep && mb->get_position().y < middle_sep + theme_cache.separation) { dragging = true; drag_from = mb->get_position().y; drag_ofs = split_offset; } } else { - if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + sep) { + if (mb->get_position().x > middle_sep && mb->get_position().x < middle_sep + theme_cache.separation) { dragging = true; drag_from = mb->get_position().x; drag_ofs = split_offset; @@ -248,14 +270,14 @@ void SplitContainer::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { bool mouse_inside_state = false; if (vertical) { - mouse_inside_state = mm->get_position().y > middle_sep && mm->get_position().y < middle_sep + get_theme_constant(SNAME("separation")); + mouse_inside_state = mm->get_position().y > middle_sep && mm->get_position().y < middle_sep + theme_cache.separation; } else { - mouse_inside_state = mm->get_position().x > middle_sep && mm->get_position().x < middle_sep + get_theme_constant(SNAME("separation")); + mouse_inside_state = mm->get_position().x > middle_sep && mm->get_position().x < middle_sep + theme_cache.separation; } if (mouse_inside != mouse_inside_state) { mouse_inside = mouse_inside_state; - if (get_theme_constant(SNAME("autohide"))) { + if (theme_cache.autohide) { queue_redraw(); } } @@ -281,14 +303,12 @@ Control::CursorShape SplitContainer::get_cursor_shape(const Point2 &p_pos) const } if (!collapsed && _getch(0) && _getch(1) && dragger_visibility == DRAGGER_VISIBLE) { - int sep = get_theme_constant(SNAME("separation")); - if (vertical) { - if (p_pos.y > middle_sep && p_pos.y < middle_sep + sep) { + if (p_pos.y > middle_sep && p_pos.y < middle_sep + theme_cache.separation) { return CURSOR_VSPLIT; } } else { - if (p_pos.x > middle_sep && p_pos.x < middle_sep + sep) { + if (p_pos.x > middle_sep && p_pos.x < middle_sep + theme_cache.separation) { return CURSOR_HSPLIT; } } @@ -344,6 +364,17 @@ bool SplitContainer::is_collapsed() const { return collapsed; } +void SplitContainer::set_vertical(bool p_vertical) { + ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); + vertical = p_vertical; + update_minimum_size(); + _resort(); +} + +bool SplitContainer::is_vertical() const { + return vertical; +} + Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const { Vector<int> flags; flags.append(SIZE_FILL); @@ -379,11 +410,15 @@ void SplitContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_dragger_visibility", "mode"), &SplitContainer::set_dragger_visibility); ClassDB::bind_method(D_METHOD("get_dragger_visibility"), &SplitContainer::get_dragger_visibility); + ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical); + ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical); + ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); BIND_ENUM_CONSTANT(DRAGGER_VISIBLE); BIND_ENUM_CONSTANT(DRAGGER_HIDDEN); diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index a69ffe4de9..8ab0779d4b 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -55,13 +55,27 @@ private: DraggerVisibility dragger_visibility = DRAGGER_VISIBLE; bool mouse_inside = false; + struct ThemeCache { + int separation = 0; + int autohide = 0; + Ref<Texture2D> grabber_icon; + Ref<Texture2D> grabber_icon_h; + Ref<Texture2D> grabber_icon_v; + } theme_cache; + Control *_getch(int p_idx) const; + Ref<Texture2D> _get_grabber_icon() const; void _resort(); protected: + bool is_fixed = false; + virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; + void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; static void _bind_methods(); public: @@ -75,6 +89,9 @@ public: void set_dragger_visibility(DraggerVisibility p_visibility); DraggerVisibility get_dragger_visibility() const; + void set_vertical(bool p_vertical); + bool is_vertical() const; + virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; virtual Size2 get_minimum_size() const override; @@ -92,7 +109,7 @@ class HSplitContainer : public SplitContainer { public: HSplitContainer() : - SplitContainer(false) {} + SplitContainer(false) { is_fixed = true; } }; class VSplitContainer : public SplitContainer { @@ -100,7 +117,7 @@ class VSplitContainer : public SplitContainer { public: VSplitContainer() : - SplitContainer(true) {} + SplitContainer(true) { is_fixed = true; } }; #endif // SPLIT_CONTAINER_H diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 61cf8e8a86..4d18af7743 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -44,14 +44,7 @@ Size2 TabBar::get_minimum_size() const { return ms; } - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> close = get_theme_icon(SNAME("close")); - int hseparation = get_theme_constant(SNAME("h_separation")); - - int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); + int y_margin = MAX(MAX(theme_cache.tab_unselected_style->get_minimum_size().height, theme_cache.tab_selected_style->get_minimum_size().height), theme_cache.tab_disabled_style->get_minimum_size().height); for (int i = 0; i < tabs.size(); i++) { if (tabs[i].hidden) { @@ -62,22 +55,22 @@ Size2 TabBar::get_minimum_size() const { Ref<StyleBox> style; if (tabs[i].disabled) { - style = tab_disabled; + style = theme_cache.tab_disabled_style; } else if (current == i) { - style = tab_selected; + style = theme_cache.tab_selected_style; } else { - style = tab_unselected; + style = theme_cache.tab_unselected_style; } ms.width += style->get_minimum_size().width; Ref<Texture2D> tex = tabs[i].icon; if (tex.is_valid()) { ms.height = MAX(ms.height, tex->get_size().height + y_margin); - ms.width += tex->get_size().width + hseparation; + ms.width += tex->get_size().width + theme_cache.h_separation; } if (!tabs[i].text.is_empty()) { - ms.width += tabs[i].size_text + hseparation; + ms.width += tabs[i].size_text + theme_cache.h_separation; } ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); @@ -87,22 +80,22 @@ Size2 TabBar::get_minimum_size() const { Ref<Texture2D> rb = tabs[i].right_button; if (close_visible) { - ms.width += button_highlight->get_minimum_size().width + rb->get_width(); + ms.width += theme_cache.button_hl_style->get_minimum_size().width + rb->get_width(); } else { - ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation; + ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; } ms.height = MAX(ms.height, rb->get_height() + y_margin); } if (close_visible) { - ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation; + ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + theme_cache.close_icon->get_width() + theme_cache.h_separation; - ms.height = MAX(ms.height, close->get_height() + y_margin); + ms.height = MAX(ms.height, theme_cache.close_icon->get_height() + y_margin); } if (ms.width - ofs > style->get_minimum_size().width) { - ms.width -= hseparation; + ms.width -= theme_cache.h_separation; } } @@ -122,16 +115,13 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { Point2 pos = mm->get_position(); if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { + if (pos.x < theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 1) { highlight_arrow = 1; queue_redraw(); } - } else if (pos.x < incr->get_width() + decr->get_width()) { + } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 0) { highlight_arrow = 0; queue_redraw(); @@ -141,8 +131,8 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { queue_redraw(); } } else { - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit_minus_buttons + decr->get_width()) { + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); + if (pos.x > limit_minus_buttons + theme_cache.decrement_icon->get_width()) { if (highlight_arrow != 1) { highlight_arrow = 1; queue_redraw(); @@ -214,18 +204,15 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { Point2 pos = mb->get_position(); if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - if (is_layout_rtl()) { - if (pos.x < decr->get_width()) { + if (pos.x < theme_cache.decrement_icon->get_width()) { if (missing_right) { offset++; _update_cache(); queue_redraw(); } return; - } else if (pos.x < incr->get_width() + decr->get_width()) { + } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { if (offset > 0) { offset--; _update_cache(); @@ -234,8 +221,8 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { return; } } else { - int limit = get_size().width - incr->get_width() - decr->get_width(); - if (pos.x > limit + decr->get_width()) { + int limit = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); + if (pos.x > limit + theme_cache.decrement_icon->get_width()) { if (missing_right) { offset++; _update_cache(); @@ -299,9 +286,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } void TabBar::_shape(int p_tab) { - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); tabs.write[p_tab].text_buf->set_width(-1); @@ -311,7 +295,37 @@ void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); } - tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].language); + tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, theme_cache.font, theme_cache.font_size, tabs[p_tab].language); +} + +void TabBar::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.h_separation = get_theme_constant(SNAME("h_separation")); + + theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); + theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected")); + theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark")); + theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color")); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.close_icon = get_theme_icon(SNAME("close")); + theme_cache.button_pressed_style = get_theme_stylebox(SNAME("button_pressed")); + theme_cache.button_hl_style = get_theme_stylebox(SNAME("button_highlight")); } void TabBar::_notification(int p_what) { @@ -352,18 +366,9 @@ void TabBar::_notification(int p_what) { return; } - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color font_unselected_color = get_theme_color(SNAME("font_unselected_color")); - Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - bool rtl = is_layout_rtl(); Vector2 size = get_size(); - int limit_minus_buttons = size.width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int ofs = tabs[offset].ofs_cache; @@ -378,14 +383,14 @@ void TabBar::_notification(int p_what) { Color col; if (tabs[i].disabled) { - sb = tab_disabled; - col = font_disabled_color; + sb = theme_cache.tab_disabled_style; + col = theme_cache.font_disabled_color; } else if (i == current) { - sb = tab_selected; - col = font_selected_color; + sb = theme_cache.tab_selected_style; + col = theme_cache.font_selected_color; } else { - sb = tab_unselected; - col = font_unselected_color; + sb = theme_cache.tab_unselected_style; + col = theme_cache.font_unselected_color; } _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs); @@ -396,41 +401,38 @@ void TabBar::_notification(int p_what) { // Draw selected tab in the front, but only if it's visible. if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) { - Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected; + Ref<StyleBox> sb = tabs[current].disabled ? theme_cache.tab_disabled_style : theme_cache.tab_selected_style; float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache; - _draw_tab(sb, font_selected_color, current, x); + _draw_tab(sb, theme_cache.font_selected_color, current, x); } if (buttons_visible) { - Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight")); - Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight")); - - int vofs = (size.height - incr->get_size().height) / 2; + int vofs = (size.height - theme_cache.increment_icon->get_size().height) / 2; if (rtl) { if (missing_right) { - draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); + draw_texture(highlight_arrow == 1 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(0, vofs)); } else { - draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.decrement_icon, Point2(0, vofs), Color(1, 1, 1, 0.5)); } if (offset > 0) { - draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); + draw_texture(highlight_arrow == 0 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs)); } else { - draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); } } else { if (offset > 0) { - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs)); + draw_texture(highlight_arrow == 0 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs)); } else { - draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); } if (missing_right) { - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs)); + draw_texture(highlight_arrow == 1 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs)); } else { - draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + draw_texture(theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); } } } @@ -462,10 +464,7 @@ void TabBar::_notification(int p_what) { } } - Ref<Texture2D> drop_mark = get_theme_icon(SNAME("drop_mark")); - Color drop_mark_color = get_theme_color(SNAME("drop_mark_color")); - - drop_mark->draw(get_canvas_item(), Point2(x - drop_mark->get_width() / 2, (size.height - drop_mark->get_height()) / 2), drop_mark_color); + theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - theme_cache.drop_mark_icon->get_width() / 2, (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); } } break; } @@ -475,10 +474,6 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in RID ci = get_canvas_item(); bool rtl = is_layout_rtl(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height); p_tab_style->draw(ci, sb_rect); @@ -491,7 +486,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (icon.is_valid()) { icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); - p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation; + p_x = rtl ? p_x - icon->get_width() - theme_cache.h_separation : p_x + icon->get_width() + theme_cache.h_separation; } // Draw the text. @@ -499,17 +494,17 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.outline_size > 0 && theme_cache.font_outline_color.a > 0) { + tabs[p_index].text_buf->draw_outline(ci, text_pos, theme_cache.outline_size, theme_cache.font_outline_color); } tabs[p_index].text_buf->draw(ci, text_pos, p_font_color); - p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation; + p_x = rtl ? p_x - tabs[p_index].size_text - theme_cache.h_separation : p_x + tabs[p_index].size_text + theme_cache.h_separation; } // Draw and calculate rect of the right button. if (tabs[p_index].right_button.is_valid()) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight")); + Ref<StyleBox> style = theme_cache.button_hl_style; Ref<Texture2D> rb = tabs[p_index].right_button; Rect2 rb_rect; @@ -521,7 +516,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (rb_hover == p_index) { if (rb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect); + theme_cache.button_pressed_style->draw(ci, rb_rect); } else { style->draw(ci, rb_rect); } @@ -534,8 +529,8 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in // Draw and calculate rect of the close button. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) { - Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> cb = get_theme_icon(SNAME("close")); + Ref<StyleBox> style = theme_cache.button_hl_style; + Ref<Texture2D> cb = theme_cache.close_icon; Rect2 cb_rect; cb_rect.size = style->get_minimum_size() + cb->get_size(); @@ -546,7 +541,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in if (!tabs[p_index].disabled && cb_hover == p_index) { if (cb_pressing) { - get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect); + theme_cache.button_pressed_style->draw(ci, cb_rect); } else { style->draw(ci, cb_rect); } @@ -849,14 +844,8 @@ void TabBar::_update_cache() { return; } - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit = get_size().width; - int limit_minus_buttons = limit - incr->get_width() - decr->get_width(); + int limit_minus_buttons = limit - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int w = 0; @@ -1258,52 +1247,47 @@ void TabBar::move_tab(int p_from, int p_to) { int TabBar::get_tab_width(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); - Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); - Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); - Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - int hseparation = get_theme_constant(SNAME("h_separation")); - Ref<StyleBox> style; if (tabs[p_idx].disabled) { - style = tab_disabled; + style = theme_cache.tab_disabled_style; } else if (current == p_idx) { - style = tab_selected; + style = theme_cache.tab_selected_style; } else { - style = tab_unselected; + style = theme_cache.tab_unselected_style; } int x = style->get_minimum_size().width; Ref<Texture2D> tex = tabs[p_idx].icon; if (tex.is_valid()) { - x += tex->get_width() + hseparation; + x += tex->get_width() + theme_cache.h_separation; } if (!tabs[p_idx].text.is_empty()) { - x += tabs[p_idx].size_text + hseparation; + x += tabs[p_idx].size_text + theme_cache.h_separation; } bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current); if (tabs[p_idx].right_button.is_valid()) { - Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight")); + Ref<StyleBox> btn_style = theme_cache.button_hl_style; Ref<Texture2D> rb = tabs[p_idx].right_button; if (close_visible) { x += btn_style->get_minimum_size().width + rb->get_width(); } else { - x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation; + x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; } } if (close_visible) { - Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight")); - Ref<Texture2D> cb = get_theme_icon(SNAME("close")); - x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation; + Ref<StyleBox> btn_style = theme_cache.button_hl_style; + Ref<Texture2D> cb = theme_cache.close_icon; + x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + theme_cache.h_separation; } if (x > style->get_minimum_size().width) { - x -= hseparation; + x -= theme_cache.h_separation; } return x; @@ -1314,9 +1298,7 @@ void TabBar::_ensure_no_over_offset() { return; } - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int prev_offset = offset; @@ -1359,9 +1341,7 @@ void TabBar::ensure_tab_visible(int p_idx) { return; } - Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); - Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); - int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); + int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache; for (int i = max_drawn_tab; i <= p_idx; i++) { diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index d123385e47..ac4a6a195e 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -104,6 +104,34 @@ private: bool scroll_to_selected = true; int tabs_rearrange_group = -1; + struct ThemeCache { + int h_separation = 0; + + Ref<StyleBox> tab_unselected_style; + Ref<StyleBox> tab_selected_style; + Ref<StyleBox> tab_disabled_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> drop_mark_icon; + Color drop_mark_color; + + Ref<Font> font; + int font_size; + int outline_size = 0; + + Color font_selected_color; + Color font_unselected_color; + Color font_disabled_color; + Color font_outline_color; + + Ref<Texture2D> close_icon; + Ref<StyleBox> button_pressed_style; + Ref<StyleBox> button_hl_style; + } theme_cache; + int get_tab_width(int p_idx) const; void _ensure_no_over_offset(); @@ -117,6 +145,7 @@ private: protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 3e04ebee6a..f45d132a66 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -60,26 +60,24 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { } // Handle menu button. - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - if (is_layout_rtl()) { - if (popup && pos.x < menu->get_width()) { + if (popup && pos.x < theme_cache.menu_icon->get_width()) { emit_signal(SNAME("pre_popup_pressed")); Vector2 popup_pos = get_screen_position(); - popup_pos.y += menu->get_height(); + popup_pos.y += theme_cache.menu_icon->get_height(); popup->set_position(popup_pos); popup->popup(); return; } } else { - if (popup && pos.x > size.width - menu->get_width()) { + if (popup && pos.x > size.width - theme_cache.menu_icon->get_width()) { emit_signal(SNAME("pre_popup_pressed")); Vector2 popup_pos = get_screen_position(); popup_pos.x += size.width - popup->get_size().width; - popup_pos.y += menu->get_height(); + popup_pos.y += theme_cache.menu_icon->get_height(); popup->set_position(popup_pos); popup->popup(); @@ -103,10 +101,9 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { return; } - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); if (popup) { if (is_layout_rtl()) { - if (pos.x <= menu->get_width()) { + if (pos.x <= theme_cache.menu_icon->get_width()) { if (!menu_hovered) { menu_hovered = true; queue_redraw(); @@ -117,7 +114,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { queue_redraw(); } } else { - if (pos.x >= size.width - menu->get_width()) { + if (pos.x >= size.width - theme_cache.menu_icon->get_width()) { if (!menu_hovered) { menu_hovered = true; queue_redraw(); @@ -136,6 +133,41 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { } } +void TabContainer::_update_theme_item_cache() { + Container::_update_theme_item_cache(); + + theme_cache.side_margin = get_theme_constant(SNAME("side_margin")); + + theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); + theme_cache.tabbar_style = get_theme_stylebox(SNAME("tabbar_background")); + + theme_cache.menu_icon = get_theme_icon(SNAME("menu")); + theme_cache.menu_hl_icon = get_theme_icon(SNAME("menu_highlight")); + + // TabBar overrides. + theme_cache.icon_separation = get_theme_constant(SNAME("icon_separation")); + theme_cache.outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.tab_unselected_style = get_theme_stylebox(SNAME("tab_unselected")); + theme_cache.tab_selected_style = get_theme_stylebox(SNAME("tab_selected")); + theme_cache.tab_disabled_style = get_theme_stylebox(SNAME("tab_disabled")); + + theme_cache.increment_icon = get_theme_icon(SNAME("increment")); + theme_cache.increment_hl_icon = get_theme_icon(SNAME("increment_highlight")); + theme_cache.decrement_icon = get_theme_icon(SNAME("decrement")); + theme_cache.decrement_hl_icon = get_theme_icon(SNAME("decrement_highlight")); + theme_cache.drop_mark_icon = get_theme_icon(SNAME("drop_mark")); + theme_cache.drop_mark_color = get_theme_color(SNAME("drop_mark_color")); + + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.font_unselected_color = get_theme_color(SNAME("font_unselected_color")); + theme_cache.font_disabled_color = get_theme_color(SNAME("font_disabled_color")); + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + + theme_cache.tab_font = get_theme_font(SNAME("font")); + theme_cache.tab_font_size = get_theme_font_size(SNAME("font_size")); +} + void TabContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -155,31 +187,26 @@ void TabContainer::_notification(int p_what) { Size2 size = get_size(); // Draw only the tab area if the header is hidden. - Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel")); if (!tabs_visible) { - panel->draw(canvas, Rect2(0, 0, size.width, size.height)); + theme_cache.panel_style->draw(canvas, Rect2(0, 0, size.width, size.height)); return; } int header_height = _get_top_margin(); // Draw background for the tabbar. - Ref<StyleBox> tabbar_background = get_theme_stylebox(SNAME("tabbar_background")); - tabbar_background->draw(canvas, Rect2(0, 0, size.width, header_height)); + theme_cache.tabbar_style->draw(canvas, Rect2(0, 0, size.width, header_height)); // Draw the background for the tab's content. - panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + theme_cache.panel_style->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); // Draw the popup menu. if (get_popup()) { - Ref<Texture2D> menu = get_theme_icon(SNAME("menu")); - Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight")); - - int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width(); + int x = is_layout_rtl() ? 0 : get_size().width - theme_cache.menu_icon->get_width(); if (menu_hovered) { - menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2)); + theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_hl_icon->get_height()) / 2)); } else { - menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2)); + theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_icon->get_height()) / 2)); } } } break; @@ -198,23 +225,27 @@ void TabContainer::_on_theme_changed() { return; } - tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected"))); - tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected"))); - tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled"))); - tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment"))); - tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight"))); - tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement"))); - tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight"))); - tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark"))); - tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color"))); - tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color"))); - tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color"))); - tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color"))); - tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color"))); - tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font"))); - tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size"))); - tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation"))); - tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size"))); + tab_bar->add_theme_style_override(SNAME("tab_unselected"), theme_cache.tab_unselected_style); + tab_bar->add_theme_style_override(SNAME("tab_selected"), theme_cache.tab_selected_style); + tab_bar->add_theme_style_override(SNAME("tab_disabled"), theme_cache.tab_disabled_style); + + tab_bar->add_theme_icon_override(SNAME("increment"), theme_cache.increment_icon); + tab_bar->add_theme_icon_override(SNAME("increment_highlight"), theme_cache.increment_hl_icon); + tab_bar->add_theme_icon_override(SNAME("decrement"), theme_cache.decrement_icon); + tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), theme_cache.decrement_hl_icon); + tab_bar->add_theme_icon_override(SNAME("drop_mark"), theme_cache.drop_mark_icon); + tab_bar->add_theme_color_override(SNAME("drop_mark_color"), theme_cache.drop_mark_color); + + tab_bar->add_theme_color_override(SNAME("font_selected_color"), theme_cache.font_selected_color); + tab_bar->add_theme_color_override(SNAME("font_unselected_color"), theme_cache.font_unselected_color); + tab_bar->add_theme_color_override(SNAME("font_disabled_color"), theme_cache.font_disabled_color); + tab_bar->add_theme_color_override(SNAME("font_outline_color"), theme_cache.font_outline_color); + + tab_bar->add_theme_font_override(SNAME("font"), theme_cache.tab_font); + tab_bar->add_theme_font_size_override(SNAME("font_size"), theme_cache.tab_font_size); + + tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation); + tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size); _update_margins(); if (get_tab_count() > 0) { @@ -228,7 +259,6 @@ void TabContainer::_on_theme_changed() { } void TabContainer::_repaint() { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel")); Vector<Control *> controls = _get_tab_controls(); int current = get_current_tab(); @@ -243,10 +273,10 @@ void TabContainer::_repaint() { c->set_offset(SIDE_TOP, _get_top_margin()); } - c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP)); - c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT)); - c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT)); - c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM)); + c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_TOP)); + c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_LEFT)); + c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - theme_cache.panel_style->get_margin(SIDE_RIGHT)); + c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - theme_cache.panel_style->get_margin(SIDE_BOTTOM)); } else { c->hide(); } @@ -256,8 +286,7 @@ void TabContainer::_repaint() { } void TabContainer::_update_margins() { - int menu_width = get_theme_icon(SNAME("menu"))->get_width(); - int side_margin = get_theme_constant(SNAME("side_margin")); + int menu_width = theme_cache.menu_icon->get_width(); // Directly check for validity, to avoid errors when quitting. bool has_popup = popup_obj_id.is_valid(); @@ -271,7 +300,7 @@ void TabContainer::_update_margins() { switch (get_tab_alignment()) { case TabBar::ALIGNMENT_LEFT: { - tab_bar->set_offset(SIDE_LEFT, side_margin); + tab_bar->set_offset(SIDE_LEFT, theme_cache.side_margin); tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); } break; @@ -293,10 +322,10 @@ void TabContainer::_update_margins() { int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width; // Calculate if all the tabs would still fit if the margin was present. - if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + side_margin) > get_size().width))) { + if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + theme_cache.side_margin) > get_size().width))) { tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0); } else { - tab_bar->set_offset(SIDE_RIGHT, -side_margin); + tab_bar->set_offset(SIDE_RIGHT, -theme_cache.side_margin); } } break; @@ -798,13 +827,12 @@ Size2 TabContainer::get_minimum_size() const { if (!get_clip_tabs()) { if (get_popup()) { - ms.x += get_theme_icon(SNAME("menu"))->get_width(); + ms.x += theme_cache.menu_icon->get_width(); } - int side_margin = get_theme_constant(SNAME("side_margin")); - if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER && + if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER && (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) { - ms.x += side_margin; + ms.x += theme_cache.side_margin; } } } @@ -824,7 +852,7 @@ Size2 TabContainer::get_minimum_size() const { } ms.y += max_control_height; - Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); + Size2 panel_ms = theme_cache.panel_style->get_minimum_size(); ms.x = MAX(ms.x, panel_ms.x); ms.y += panel_ms.y; diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 60c8130939..b552aa459b 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -48,6 +48,39 @@ class TabContainer : public Container { bool theme_changing = false; Node *child_removing = nullptr; + struct ThemeCache { + int side_margin = 0; + + Ref<StyleBox> panel_style; + Ref<StyleBox> tabbar_style; + + Ref<Texture2D> menu_icon; + Ref<Texture2D> menu_hl_icon; + + // TabBar overrides. + int icon_separation = 0; + int outline_size = 0; + + Ref<StyleBox> tab_unselected_style; + Ref<StyleBox> tab_selected_style; + Ref<StyleBox> tab_disabled_style; + + Ref<Texture2D> increment_icon; + Ref<Texture2D> increment_hl_icon; + Ref<Texture2D> decrement_icon; + Ref<Texture2D> decrement_hl_icon; + Ref<Texture2D> drop_mark_icon; + Color drop_mark_color; + + Color font_selected_color; + Color font_unselected_color; + Color font_disabled_color; + Color font_outline_color; + + Ref<Font> tab_font; + int tab_font_size; + } theme_cache; + int _get_top_margin() const; Vector<Control *> _get_tab_controls() const; void _on_theme_changed(); @@ -65,6 +98,8 @@ class TabContainer : public Container { protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual void _update_theme_item_cache() override; + void _notification(int p_what); virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index e2fd903e0e..2efb6593d3 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -112,7 +112,7 @@ bool TextureButton::has_point(const Point2 &p_point) const { } Point2i p = point; - return click_mask->get_bit(p); + return click_mask->get_bitv(p); } return Control::has_point(p_point); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 3c6be008f2..e8164b5728 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1286,14 +1286,14 @@ Size2 TreeItem::get_minimum_size(int p_column) { // Icon. if (cell.mode == CELL_MODE_CHECK) { - size.width += tree->cache.checked->get_width() + tree->cache.hseparation; + size.width += tree->theme_cache.checked->get_width() + tree->theme_cache.hseparation; } if (cell.icon.is_valid()) { Size2i icon_size = cell.get_icon_size(); if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { icon_size.width = cell.icon_max_w; } - size.width += icon_size.width + tree->cache.hseparation; + size.width += icon_size.width + tree->theme_cache.hseparation; size.height = MAX(size.height, icon_size.height); } @@ -1301,13 +1301,13 @@ Size2 TreeItem::get_minimum_size(int p_column) { for (int i = 0; i < cell.buttons.size(); i++) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { - Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); + Size2 button_size = texture->get_size() + tree->theme_cache.button_pressed->get_minimum_size(); size.width += button_size.width; size.height = MAX(size.height, button_size.height); } } if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + size.width += (cell.buttons.size() - 1) * tree->theme_cache.button_margin; } cells.write[p_column].cached_minimum_size = size; @@ -1535,68 +1535,66 @@ TreeItem::~TreeItem() { /**********************************************/ /**********************************************/ -void Tree::update_cache() { - cache.font = get_theme_font(SNAME("font")); - cache.font_size = get_theme_font_size(SNAME("font_size")); - cache.tb_font = get_theme_font(SNAME("title_button_font")); - cache.tb_font_size = get_theme_font_size(SNAME("title_button_font_size")); - cache.bg = get_theme_stylebox(SNAME("bg")); - cache.selected = get_theme_stylebox(SNAME("selected")); - cache.selected_focus = get_theme_stylebox(SNAME("selected_focus")); - cache.cursor = get_theme_stylebox(SNAME("cursor")); - cache.cursor_unfocus = get_theme_stylebox(SNAME("cursor_unfocused")); - cache.button_pressed = get_theme_stylebox(SNAME("button_pressed")); - - cache.checked = get_theme_icon(SNAME("checked")); - cache.unchecked = get_theme_icon(SNAME("unchecked")); - cache.indeterminate = get_theme_icon(SNAME("indeterminate")); - if (is_layout_rtl()) { - cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed_mirrored")); - } else { - cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed")); - } - cache.arrow = get_theme_icon(SNAME("arrow")); - cache.select_arrow = get_theme_icon(SNAME("select_arrow")); - cache.updown = get_theme_icon(SNAME("updown")); - - cache.custom_button = get_theme_stylebox(SNAME("custom_button")); - cache.custom_button_hover = get_theme_stylebox(SNAME("custom_button_hover")); - cache.custom_button_pressed = get_theme_stylebox(SNAME("custom_button_pressed")); - cache.custom_button_font_highlight = get_theme_color(SNAME("custom_button_font_highlight")); - - cache.font_color = get_theme_color(SNAME("font_color")); - cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); - cache.drop_position_color = get_theme_color(SNAME("drop_position_color")); - cache.hseparation = get_theme_constant(SNAME("h_separation")); - cache.vseparation = get_theme_constant(SNAME("v_separation")); - cache.item_margin = get_theme_constant(SNAME("item_margin")); - cache.button_margin = get_theme_constant(SNAME("button_margin")); - - cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); - cache.font_outline_size = get_theme_constant(SNAME("outline_size")); - - cache.draw_guides = get_theme_constant(SNAME("draw_guides")); - cache.guide_color = get_theme_color(SNAME("guide_color")); - cache.draw_relationship_lines = get_theme_constant(SNAME("draw_relationship_lines")); - cache.relationship_line_width = get_theme_constant(SNAME("relationship_line_width")); - cache.parent_hl_line_width = get_theme_constant(SNAME("parent_hl_line_width")); - cache.children_hl_line_width = get_theme_constant(SNAME("children_hl_line_width")); - cache.parent_hl_line_margin = get_theme_constant(SNAME("parent_hl_line_margin")); - cache.relationship_line_color = get_theme_color(SNAME("relationship_line_color")); - cache.parent_hl_line_color = get_theme_color(SNAME("parent_hl_line_color")); - cache.children_hl_line_color = get_theme_color(SNAME("children_hl_line_color")); - - cache.scroll_border = get_theme_constant(SNAME("scroll_border")); - cache.scroll_speed = get_theme_constant(SNAME("scroll_speed")); - - cache.title_button = get_theme_stylebox(SNAME("title_button_normal")); - cache.title_button_pressed = get_theme_stylebox(SNAME("title_button_pressed")); - cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover")); - cache.title_button_color = get_theme_color(SNAME("title_button_color")); - - cache.base_scale = get_theme_default_base_scale(); - - v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); +void Tree::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + theme_cache.font = get_theme_font(SNAME("font")); + theme_cache.font_size = get_theme_font_size(SNAME("font_size")); + theme_cache.tb_font = get_theme_font(SNAME("title_button_font")); + theme_cache.tb_font_size = get_theme_font_size(SNAME("title_button_font_size")); + theme_cache.bg = get_theme_stylebox(SNAME("bg")); + theme_cache.bg_focus = get_theme_stylebox(SNAME("bg_focus")); + theme_cache.selected = get_theme_stylebox(SNAME("selected")); + theme_cache.selected_focus = get_theme_stylebox(SNAME("selected_focus")); + theme_cache.cursor = get_theme_stylebox(SNAME("cursor")); + theme_cache.cursor_unfocus = get_theme_stylebox(SNAME("cursor_unfocused")); + theme_cache.button_pressed = get_theme_stylebox(SNAME("button_pressed")); + + theme_cache.checked = get_theme_icon(SNAME("checked")); + theme_cache.unchecked = get_theme_icon(SNAME("unchecked")); + theme_cache.indeterminate = get_theme_icon(SNAME("indeterminate")); + theme_cache.arrow = get_theme_icon(SNAME("arrow")); + theme_cache.arrow_collapsed = get_theme_icon(SNAME("arrow_collapsed")); + theme_cache.arrow_collapsed_mirrored = get_theme_icon(SNAME("arrow_collapsed_mirrored")); + theme_cache.select_arrow = get_theme_icon(SNAME("select_arrow")); + theme_cache.updown = get_theme_icon(SNAME("updown")); + + theme_cache.custom_button = get_theme_stylebox(SNAME("custom_button")); + theme_cache.custom_button_hover = get_theme_stylebox(SNAME("custom_button_hover")); + theme_cache.custom_button_pressed = get_theme_stylebox(SNAME("custom_button_pressed")); + theme_cache.custom_button_font_highlight = get_theme_color(SNAME("custom_button_font_highlight")); + + theme_cache.font_color = get_theme_color(SNAME("font_color")); + theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + theme_cache.drop_position_color = get_theme_color(SNAME("drop_position_color")); + theme_cache.hseparation = get_theme_constant(SNAME("h_separation")); + theme_cache.vseparation = get_theme_constant(SNAME("v_separation")); + theme_cache.item_margin = get_theme_constant(SNAME("item_margin")); + theme_cache.button_margin = get_theme_constant(SNAME("button_margin")); + + theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); + theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size")); + + theme_cache.draw_guides = get_theme_constant(SNAME("draw_guides")); + theme_cache.guide_color = get_theme_color(SNAME("guide_color")); + theme_cache.draw_relationship_lines = get_theme_constant(SNAME("draw_relationship_lines")); + theme_cache.relationship_line_width = get_theme_constant(SNAME("relationship_line_width")); + theme_cache.parent_hl_line_width = get_theme_constant(SNAME("parent_hl_line_width")); + theme_cache.children_hl_line_width = get_theme_constant(SNAME("children_hl_line_width")); + theme_cache.parent_hl_line_margin = get_theme_constant(SNAME("parent_hl_line_margin")); + theme_cache.relationship_line_color = get_theme_color(SNAME("relationship_line_color")); + theme_cache.parent_hl_line_color = get_theme_color(SNAME("parent_hl_line_color")); + theme_cache.children_hl_line_color = get_theme_color(SNAME("children_hl_line_color")); + + theme_cache.scroll_border = get_theme_constant(SNAME("scroll_border")); + theme_cache.scroll_speed = get_theme_constant(SNAME("scroll_speed")); + + theme_cache.title_button = get_theme_stylebox(SNAME("title_button_normal")); + theme_cache.title_button_pressed = get_theme_stylebox(SNAME("title_button_pressed")); + theme_cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover")); + theme_cache.title_button_color = get_theme_color(SNAME("title_button_color")); + + theme_cache.base_scale = get_theme_default_base_scale(); } int Tree::compute_item_height(TreeItem *p_item) const { @@ -1604,7 +1602,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { return 0; } - ERR_FAIL_COND_V(cache.font.is_null(), 0); + ERR_FAIL_COND_V(theme_cache.font.is_null(), 0); int height = 0; for (int i = 0; i < columns.size(); i++) { @@ -1622,7 +1620,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { switch (p_item->cells[i].mode) { case TreeItem::CELL_MODE_CHECK: { - int check_icon_h = cache.checked->get_height(); + int check_icon_h = theme_cache.checked->get_height(); if (height < check_icon_h) { height = check_icon_h; } @@ -1642,7 +1640,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { } } if (p_item->cells[i].mode == TreeItem::CELL_MODE_CUSTOM && p_item->cells[i].custom_button) { - height += cache.custom_button->get_minimum_size().height; + height += theme_cache.custom_button->get_minimum_size().height; } } break; @@ -1655,7 +1653,7 @@ int Tree::compute_item_height(TreeItem *p_item) const { height = item_min_height; } - height += cache.vseparation; + height += theme_cache.vseparation; return height; } @@ -1665,7 +1663,7 @@ int Tree::get_item_height(TreeItem *p_item) const { return 0; } int height = compute_item_height(p_item); - height += cache.vseparation; + height += theme_cache.vseparation; if (!p_item->collapsed) { /* if not collapsed, check the children */ @@ -1682,7 +1680,7 @@ int Tree::get_item_height(TreeItem *p_item) const { } void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) { - ERR_FAIL_COND(cache.font.is_null()); + ERR_FAIL_COND(theme_cache.font.is_null()); Rect2i rect = p_rect; Size2 ts = p_cell.text_buf->get_size(); @@ -1694,7 +1692,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co if (p_cell.icon_max_w > 0 && bmsize.width > p_cell.icon_max_w) { bmsize.width = p_cell.icon_max_w; } - w += bmsize.width + cache.hseparation; + w += bmsize.width + theme_cache.hseparation; if (rect.size.width > 0 && (w + ts.width) > rect.size.width) { ts.width = rect.size.width - w; } @@ -1728,8 +1726,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color); } p_cell.text_buf->draw(ci, draw_pos, p_color); - rect.position.x += ts.width + cache.hseparation; - rect.size.x -= ts.width + cache.hseparation; + rect.position.x += ts.width + theme_cache.hseparation; + rect.size.x -= ts.width + theme_cache.hseparation; } if (!p_cell.icon.is_null()) { @@ -1741,8 +1739,8 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co } p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color); - rect.position.x += bmsize.x + cache.hseparation; - rect.size.x -= bmsize.x + cache.hseparation; + rect.position.x += bmsize.x + theme_cache.hseparation; + rect.size.x -= bmsize.x + theme_cache.hseparation; } if (!rtl) { @@ -1764,7 +1762,7 @@ void Tree::update_column(int p_col) { columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction); } - columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].language); + columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.font, theme_cache.font_size, columns[p_col].language); } void Tree::update_item_cell(TreeItem *p_item, int p_col) { @@ -1813,14 +1811,14 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) { if (p_item->cells[p_col].custom_font.is_valid()) { font = p_item->cells[p_col].custom_font; } else { - font = cache.font; + font = theme_cache.font; } int font_size; if (p_item->cells[p_col].custom_font_size > 0) { font_size = p_item->cells[p_col].custom_font_size; } else { - font_size = cache.font_size; + font_size = theme_cache.font_size; } p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language); TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext)); @@ -1840,7 +1838,7 @@ void Tree::update_item_cache(TreeItem *p_item) { } int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item) { - if (p_pos.y - cache.offset.y > (p_draw_size.height)) { + if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) { return -1; //draw no more! } @@ -1856,18 +1854,18 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 bool rtl = cache.rtl; /* Calculate height of the label part */ - label_h += cache.vseparation; + label_h += theme_cache.vseparation; /* Draw label, if height fits */ bool skip = (p_item == root && hide_root); - if (!skip && (p_pos.y + label_h - cache.offset.y) > 0) { + if (!skip && (p_pos.y + label_h - theme_cache.offset.y) > 0) { // Draw separation. - ERR_FAIL_COND_V(cache.font.is_null(), -1); + ERR_FAIL_COND_V(theme_cache.font.is_null(), -1); - int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); + int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin); int skip2 = 0; for (int i = 0; i < columns.size(); i++) { if (skip2) { @@ -1885,8 +1883,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 continue; } } else { - ofs += cache.hseparation; - w -= cache.hseparation; + ofs += theme_cache.hseparation; + w -= theme_cache.hseparation; } if (p_item->cells[i].expand_right) { @@ -1902,10 +1900,10 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int button_w = 0; for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; - button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin; } - int total_ofs = ofs - cache.offset.x; + int total_ofs = ofs - theme_cache.offset.x; if (total_ofs + w > p_draw_size.width) { w = MAX(button_w, p_draw_size.width - total_ofs); @@ -1915,9 +1913,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int bw = 0; for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; - Size2 s = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 s = b->get_size() + theme_cache.button_pressed->get_minimum_size(); - Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs; + Point2i o = Point2i(ofs + w - s.width, p_pos.y) - theme_cache.offset + p_draw_ofs; if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) { // Being pressed. @@ -1925,48 +1923,48 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (rtl) { od.x = get_size().width - od.x - s.x; } - cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); + theme_cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); } o.y += (label_h - s.height) / 2; - o += cache.button_pressed->get_offset(); + o += theme_cache.button_pressed->get_offset(); if (rtl) { o.x = get_size().width - o.x - b->get_width(); } b->draw(ci, o, p_item->cells[i].buttons[j].disabled ? Color(1, 1, 1, 0.5) : p_item->cells[i].buttons[j].color); - w -= s.width + cache.button_margin; - bw += s.width + cache.button_margin; + w -= s.width + theme_cache.button_margin; + bw += s.width + theme_cache.button_margin; } - Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - cache.offset + p_draw_ofs, Size2i(w, label_h)); + Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(w, label_h)); Rect2i cell_rect = item_rect; if (i != 0) { - cell_rect.position.x -= cache.hseparation; - cell_rect.size.x += cache.hseparation; + cell_rect.position.x -= theme_cache.hseparation; + cell_rect.size.x += theme_cache.hseparation; } - if (cache.draw_guides) { + if (theme_cache.draw_guides) { Rect2 r = cell_rect; if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, cache.guide_color, 1); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, theme_cache.guide_color, 1); } if (i == 0) { if (p_item->cells[0].selected && select_mode == SELECT_ROW) { - Rect2i row_rect = Rect2i(Point2i(cache.bg->get_margin(SIDE_LEFT), item_rect.position.y), Size2i(get_size().width - cache.bg->get_minimum_size().width, item_rect.size.y)); + Rect2i row_rect = Rect2i(Point2i(theme_cache.bg->get_margin(SIDE_LEFT), item_rect.position.y), Size2i(get_size().width - theme_cache.bg->get_minimum_size().width, item_rect.size.y)); //Rect2 r = Rect2i(row_rect.pos,row_rect.size); //r.grow(cache.selected->get_margin(SIDE_LEFT)); if (rtl) { row_rect.position.x = get_size().width - row_rect.position.x - row_rect.size.x; } if (has_focus()) { - cache.selected_focus->draw(ci, row_rect); + theme_cache.selected_focus->draw(ci, row_rect); } else { - cache.selected->draw(ci, row_rect); + theme_cache.selected->draw(ci, row_rect); } } } @@ -1982,9 +1980,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (p_item->cells[i].selected) { if (has_focus()) { - cache.selected_focus->draw(ci, r); + theme_cache.selected_focus->draw(ci, r); } else { - cache.selected->draw(ci, r); + theme_cache.selected->draw(ci, r); } } } @@ -1996,8 +1994,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 r.position.x = p_draw_ofs.x; r.size.x = w + ofs; } else { - r.position.x -= cache.hseparation; - r.size.x += cache.hseparation; + r.position.x -= theme_cache.hseparation; + r.size.x += theme_cache.hseparation; } if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; @@ -2020,28 +2018,34 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_over == p_item) { if (drop_mode_section == 0 || drop_mode_section == -1) { // Line above. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color); } if (drop_mode_section == 0) { // Side lines. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), cache.drop_position_color); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), theme_cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), theme_cache.drop_position_color); } if (drop_mode_section == 0 || (drop_mode_section == 1 && (!p_item->get_first_child() || p_item->is_collapsed()))) { // Line below. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), theme_cache.drop_position_color); } } else if (drop_mode_over == p_item->get_parent()) { if (drop_mode_section == 1 && !p_item->get_prev() /* && !drop_mode_over->is_collapsed() */) { // The drop_mode_over shouldn't ever be collapsed in here, otherwise we would be drawing a child of a collapsed item. // Line above. - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color); } } } - Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_selected_color" : "font_color"); - Color font_outline_color = cache.font_outline_color; - int outline_size = cache.font_outline_size; + Color col; + if (p_item->cells[i].custom_color) { + col = p_item->cells[i].color; + } else { + col = p_item->cells[i].selected ? theme_cache.font_selected_color : theme_cache.font_color; + } + + Color font_outline_color = theme_cache.font_outline_color; + int outline_size = theme_cache.font_outline_size; Color icon_col = p_item->cells[i].icon_color; if (p_item->cells[i].dirty) { @@ -2061,9 +2065,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col, outline_size, font_outline_color); } break; case TreeItem::CELL_MODE_CHECK: { - Ref<Texture2D> checked = cache.checked; - Ref<Texture2D> unchecked = cache.unchecked; - Ref<Texture2D> indeterminate = cache.indeterminate; + Ref<Texture2D> checked = theme_cache.checked; + Ref<Texture2D> unchecked = theme_cache.unchecked; + Ref<Texture2D> indeterminate = theme_cache.indeterminate; Point2i check_ofs = item_rect.position; check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2); @@ -2075,7 +2079,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 unchecked->draw(ci, check_ofs); } - int check_w = checked->get_width() + cache.hseparation; + int check_w = checked->get_width() + theme_cache.hseparation; text_pos.x += check_w; @@ -2091,7 +2095,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; } - Ref<Texture2D> downarrow = cache.select_arrow; + Ref<Texture2D> downarrow = theme_cache.select_arrow; int cell_width = item_rect.size.x - downarrow->get_width(); p_item->cells.write[i].text_buf->set_width(cell_width); @@ -2113,7 +2117,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 downarrow->draw(ci, arrow_pos); } else { - Ref<Texture2D> updown = cache.updown; + Ref<Texture2D> updown = theme_cache.updown; int cell_width = item_rect.size.x - updown->get_width(); @@ -2170,7 +2174,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; } - Ref<Texture2D> downarrow = cache.select_arrow; + Ref<Texture2D> downarrow = theme_cache.select_arrow; Rect2i ir = item_rect; @@ -2182,16 +2186,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (p_item->cells[i].custom_button) { if (cache.hover_item == p_item && cache.hover_cell == i) { if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - draw_style_box(cache.custom_button_pressed, ir); + draw_style_box(theme_cache.custom_button_pressed, ir); } else { - draw_style_box(cache.custom_button_hover, ir); - col = cache.custom_button_font_highlight; + draw_style_box(theme_cache.custom_button_hover, ir); + col = theme_cache.custom_button_font_highlight; } } else { - draw_style_box(cache.custom_button, ir); + draw_style_box(theme_cache.custom_button, ir); } - ir.size -= cache.custom_button->get_minimum_size(); - ir.position += cache.custom_button->get_offset(); + ir.size -= theme_cache.custom_button->get_minimum_size(); + ir.position += theme_cache.custom_button->get_offset(); } draw_item_rect(p_item->cells.write[i], ir, col, icon_col, outline_size, font_outline_color); @@ -2212,9 +2216,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 cell_rect.position.x = get_size().width - cell_rect.position.x - cell_rect.size.x; } if (has_focus()) { - cache.cursor->draw(ci, cell_rect); + theme_cache.cursor->draw(ci, cell_rect); } else { - cache.cursor_unfocus->draw(ci, cell_rect); + theme_cache.cursor_unfocus->draw(ci, cell_rect); } } } @@ -2224,13 +2228,17 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Ref<Texture2D> arrow; if (p_item->collapsed) { - arrow = cache.arrow_collapsed; + if (is_layout_rtl()) { + arrow = theme_cache.arrow_collapsed_mirrored; + } else { + arrow = theme_cache.arrow_collapsed; + } } else { - arrow = cache.arrow; + arrow = theme_cache.arrow; } - Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset + p_draw_ofs; - apos.x += cache.item_margin - arrow->get_width(); + Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - theme_cache.offset + p_draw_ofs; + apos.x += theme_cache.item_margin - arrow->get_width(); if (rtl) { apos.x = get_size().width - apos.x - arrow->get_width(); @@ -2243,7 +2251,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 Point2 children_pos = p_pos; if (!skip) { - children_pos.x += cache.item_margin; + children_pos.x += theme_cache.item_margin; htotal += label_h; children_pos.y += htotal; } @@ -2251,7 +2259,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (!p_item->collapsed) { /* if not collapsed, check the children */ TreeItem *c = p_item->first_child; - int base_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; + int base_ofs = children_pos.y - theme_cache.offset.y + p_draw_ofs.y; int prev_ofs = base_ofs; int prev_hl_ofs = base_ofs; @@ -2262,20 +2270,20 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } // Draw relationship lines. - if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) { - int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); - int parent_ofs = p_pos.x + cache.item_margin; - Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; + if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) { + int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.hseparation : theme_cache.item_margin); + int parent_ofs = p_pos.x + theme_cache.item_margin; + Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - theme_cache.offset + p_draw_ofs; if (c->get_visible_child_count() > 0) { - root_pos -= Point2i(cache.arrow->get_width(), 0); + root_pos -= Point2i(theme_cache.arrow->get_width(), 0); } - float line_width = cache.relationship_line_width * Math::round(cache.base_scale); - float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale); - float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale); + float line_width = theme_cache.relationship_line_width * Math::round(theme_cache.base_scale); + float parent_line_width = theme_cache.parent_hl_line_width * Math::round(theme_cache.base_scale); + float children_line_width = theme_cache.children_hl_line_width * Math::round(theme_cache.base_scale); - Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; + Point2i parent_pos = Point2i(parent_ofs - theme_cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + theme_cache.arrow->get_height() / 2) - theme_cache.offset + p_draw_ofs; int more_prev_ofs = 0; @@ -2289,43 +2297,43 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (_is_branch_selected(c)) { // If this item or one of its children is selected, we draw the line using parent highlight style. if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.parent_hl_line_color, parent_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); - more_prev_ofs = cache.parent_hl_line_margin; + more_prev_ofs = theme_cache.parent_hl_line_margin; prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else if (p_item->is_selected(0)) { // If parent item is selected (but this item is not), we draw the line using children highlight style. // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. if (_is_sibling_branch_selected(c)) { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), theme_cache.children_hl_line_color, children_line_width); } } else { // If nothing of the above is true, we draw the line using normal style. // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. if (_is_sibling_branch_selected(c)) { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + theme_cache.parent_hl_line_margin, root_pos.y), theme_cache.relationship_line_color, line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width); prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); } else { if (htotal >= 0) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), theme_cache.relationship_line_color, line_width); } - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), theme_cache.relationship_line_color, line_width); } } } @@ -2338,12 +2346,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 break; // Last loop done, stop. } - if (cache.draw_relationship_lines == 0) { + if (theme_cache.draw_relationship_lines == 0) { return -1; // No need to draw anymore, full stop. } htotal = -1; - children_pos.y = cache.offset.y + p_draw_size.height; + children_pos.y = theme_cache.offset.y + p_draw_size.height; } else { htotal += child_h; children_pos.y += child_h; @@ -2494,7 +2502,7 @@ Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) { void Tree::_range_click_timeout() { if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - Point2 pos = get_local_mouse_position() - cache.bg->get_offset(); + Point2 pos = get_local_mouse_position() - theme_cache.bg->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); @@ -2512,7 +2520,7 @@ void Tree::_range_click_timeout() { Ref<InputEventMouseButton> mb; mb.instantiate(); - int x_limit = get_size().width - cache.bg->get_minimum_size().width; + int x_limit = get_size().width - theme_cache.bg->get_minimum_size().width; if (h_scroll->is_visible()) { x_limit -= h_scroll->get_minimum_size().width; } @@ -2521,7 +2529,7 @@ void Tree::_range_click_timeout() { propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, false, root, MouseButton::LEFT, mb); + propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, false, root, MouseButton::LEFT, mb); blocked--; if (range_click_timer->is_one_shot()) { @@ -2550,7 +2558,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return 0; } - int item_h = compute_item_height(p_item) + cache.vseparation; + int item_h = compute_item_height(p_item) + theme_cache.vseparation; bool skip = (p_item == root && hide_root); @@ -2561,7 +2569,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return -1; } - if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { + if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) { p_item->set_collapsed(!p_item->is_collapsed()); return -1; } @@ -2580,7 +2588,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int if (p_item->cells[i].expand_right) { int plus = 1; while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) { - col_width += cache.hseparation; + col_width += theme_cache.hseparation; col_width += get_column_width(i + plus); plus++; } @@ -2600,16 +2608,16 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int if (col == -1) { return -1; } else if (col == 0) { - int margin = x_ofs + cache.item_margin; //-cache.hseparation; - //int lm = cache.bg->get_margin(SIDE_LEFT); + int margin = x_ofs + theme_cache.item_margin; //-theme_cache.hseparation; + //int lm = theme_cache.bg->get_margin(SIDE_LEFT); col_width -= margin; limit_w -= margin; col_ofs += margin; x -= margin; } else { - col_width -= cache.hseparation; - limit_w -= cache.hseparation; - x -= cache.hseparation; + col_width -= theme_cache.hseparation; + limit_w -= theme_cache.hseparation; + x -= theme_cache.hseparation; } if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { @@ -2626,7 +2634,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int int button_w = 0; for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[col].buttons[j].texture; - button_w += b->get_size().width + cache.button_pressed->get_minimum_size().width + cache.button_margin; + button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin; } col_width = MAX(button_w, MIN(limit_w, col_width)); @@ -2634,7 +2642,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - int w = b->get_size().width + cache.button_pressed->get_minimum_size().width; + int w = b->get_size().width + theme_cache.button_pressed->get_minimum_size().width; if (x > col_width - w) { if (c.buttons[j].disabled) { @@ -2662,7 +2670,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return -1; } - col_width -= w + cache.button_margin; + col_width -= w + theme_cache.button_margin; } if (p_button == MouseButton::LEFT || (p_button == MouseButton::RIGHT && allow_rmb_select)) { @@ -2742,7 +2750,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int case TreeItem::CELL_MODE_CHECK: { bring_up_editor = false; //checkboxes are not edited with editor if (force_edit_checkbox_only_on_checkbox) { - if (x < cache.checked->get_width()) { + if (x < theme_cache.checked->get_width()) { p_item->set_checked(col, !c.checked); item_edited(col, p_item, p_button); } @@ -2764,7 +2772,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } popup_menu->set_size(Size2(col_width, 0)); - popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset); + popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - theme_cache.offset); popup_menu->popup(); popup_edited_item = p_item; popup_edited_item_col = col; @@ -2822,9 +2830,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int case TreeItem::CELL_MODE_CUSTOM: { edited_item = p_item; edited_col = col; - bool on_arrow = x > col_width - cache.select_arrow->get_width(); + bool on_arrow = x > col_width - theme_cache.select_arrow->get_width(); - custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - cache.offset.y), Size2(get_column_width(col), item_h)); + custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - theme_cache.offset.y), Size2(get_column_width(col), item_h)); if (on_arrow || !p_item->cells[col].custom_button) { emit_signal(SNAME("custom_popup_edited"), ((bool)(x >= (col_width - item_h / 2)))); @@ -2846,7 +2854,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int popup_pressing_edited_item = p_item; popup_pressing_edited_item_column = col; - pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - cache.offset, Size2(col_width, item_h)); + pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - theme_cache.offset, Size2(col_width, item_h)); pressing_for_editor_text = editor_text; pressing_for_editor = true; @@ -2855,8 +2863,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int Point2i new_pos = p_pos; if (!skip) { - x_ofs += cache.item_margin; - //new_pos.x-=cache.item_margin; + x_ofs += theme_cache.item_margin; + //new_pos.x-=theme_cache.item_margin; y_ofs += item_h; new_pos.y -= item_h; } @@ -3300,18 +3308,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); - } - - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.bg; bool rtl = is_layout_rtl(); Point2 pos = mm->get_position(); if (rtl) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); Cache::ClickType old_hover = cache.hover_type; int old_index = cache.hover_index; @@ -3321,7 +3325,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (show_column_titles) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - pos.x += cache.offset.x; + pos.x += theme_cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { len += get_column_width(i); @@ -3339,7 +3343,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (rtl) { mpos.x = get_size().width - mpos.x; } - mpos -= cache.bg->get_offset(); + mpos -= theme_cache.bg->get_offset(); mpos.y -= _get_title_button_height(); if (mpos.y >= 0) { if (h_scroll->is_visible_in_tree()) { @@ -3430,10 +3434,6 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); - } - bool rtl = is_layout_rtl(); if (!mb->is_pressed()) { @@ -3443,12 +3443,12 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { if (rtl) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); if (show_column_titles) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - pos.x += cache.offset.x; + pos.x += theme_cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { len += get_column_width(i); @@ -3537,7 +3537,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { switch (mb->get_button_index()) { case MouseButton::RIGHT: case MouseButton::LEFT: { - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.bg; Point2 pos = mb->get_position(); if (rtl) { @@ -3549,7 +3549,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { pos.y -= _get_title_button_height(); if (pos.y < 0) { - pos.x += cache.offset.x; + pos.x += theme_cache.offset.x; int len = 0; for (int i = 0; i < columns.size(); i++) { len += get_column_width(i); @@ -3572,14 +3572,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { pressing_for_editor = false; propagate_mouse_activated = false; - int x_limit = get_size().width - cache.bg->get_minimum_size().width; + int x_limit = get_size().width - theme_cache.bg->get_minimum_size().width; if (h_scroll->is_visible()) { x_limit -= h_scroll->get_minimum_size().width; } cache.rtl = is_layout_rtl(); blocked++; - propagate_mouse_event(pos + cache.offset, 0, 0, x_limit + cache.offset.width, mb->is_double_click(), root, mb->get_button_index(), mb); + propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, mb->is_double_click(), root, mb->get_button_index(), mb); blocked--; if (pressing_for_editor) { @@ -3766,7 +3766,7 @@ bool Tree::is_editing() { } Size2 Tree::get_internal_min_size() const { - Size2i size = cache.bg->get_offset(); + Size2i size = theme_cache.bg->get_offset(); if (root) { size.height += get_item_height(root); } @@ -3789,23 +3789,23 @@ void Tree::update_scrollbars() { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); - v_scroll->set_begin(Point2(size.width - vmin.width, cache.bg->get_margin(SIDE_TOP))); - v_scroll->set_end(Point2(size.width, size.height - cache.bg->get_margin(SIDE_TOP) - cache.bg->get_margin(SIDE_BOTTOM))); + v_scroll->set_begin(Point2(size.width - vmin.width, theme_cache.bg->get_margin(SIDE_TOP))); + v_scroll->set_end(Point2(size.width, size.height - theme_cache.bg->get_margin(SIDE_TOP) - theme_cache.bg->get_margin(SIDE_BOTTOM))); h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); Size2 internal_min_size = get_internal_min_size(); - bool display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) > size.height; - bool display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) > size.width; + bool display_vscroll = internal_min_size.height + theme_cache.bg->get_margin(SIDE_TOP) > size.height; + bool display_hscroll = internal_min_size.width + theme_cache.bg->get_margin(SIDE_LEFT) > size.width; for (int i = 0; i < 2; i++) { // Check twice, as both values are dependent on each other. if (display_hscroll) { - display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) + hmin.height > size.height; + display_vscroll = internal_min_size.height + theme_cache.bg->get_margin(SIDE_TOP) + hmin.height > size.height; } if (display_vscroll) { - display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) + vmin.width > size.width; + display_hscroll = internal_min_size.width + theme_cache.bg->get_margin(SIDE_LEFT) + vmin.width > size.width; } } @@ -3813,29 +3813,29 @@ void Tree::update_scrollbars() { v_scroll->show(); v_scroll->set_max(internal_min_size.height); v_scroll->set_page(size.height - hmin.height - tbh); - cache.offset.y = v_scroll->get_value(); + theme_cache.offset.y = v_scroll->get_value(); } else { v_scroll->hide(); - cache.offset.y = 0; + theme_cache.offset.y = 0; } if (display_hscroll) { h_scroll->show(); h_scroll->set_max(internal_min_size.width); h_scroll->set_page(size.width - vmin.width); - cache.offset.x = h_scroll->get_value(); + theme_cache.offset.x = h_scroll->get_value(); } else { h_scroll->hide(); - cache.offset.x = 0; + theme_cache.offset.x = 0; } } int Tree::_get_title_button_height() const { - ERR_FAIL_COND_V(cache.font.is_null() || cache.title_button.is_null(), 0); + ERR_FAIL_COND_V(theme_cache.font.is_null() || theme_cache.title_button.is_null(), 0); int h = 0; if (show_column_titles) { for (int i = 0; i < columns.size(); i++) { - h = MAX(h, columns[i].text_buf->get_size().y + cache.title_button->get_minimum_size().height); + h = MAX(h, columns[i].text_buf->get_size().y + theme_cache.title_button->get_minimum_size().height); } } return h; @@ -3860,10 +3860,6 @@ void Tree::_notification(int p_what) { drag_touching = false; } break; - case NOTIFICATION_ENTER_TREE: { - update_cache(); - } break; - case NOTIFICATION_DRAG_END: { drop_mode_flags = 0; scrolling = false; @@ -3873,7 +3869,7 @@ void Tree::_notification(int p_what) { case NOTIFICATION_DRAG_BEGIN: { single_select_defer = nullptr; - if (cache.scroll_speed > 0) { + if (theme_cache.scroll_speed > 0) { scrolling = true; set_physics_process_internal(true); } @@ -3917,22 +3913,22 @@ void Tree::_notification(int p_what) { } Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); - if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { + if (scrolling && get_rect().grow(theme_cache.scroll_border).has_point(mouse_position)) { Point2 point; - if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) { - point.x = mouse_position.x - cache.scroll_border; - } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) { - point.x = mouse_position.x - (get_size().width - cache.scroll_border); + if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < theme_cache.scroll_border)) { + point.x = mouse_position.x - theme_cache.scroll_border; + } else if (ABS(mouse_position.x - get_size().width) < theme_cache.scroll_border) { + point.x = mouse_position.x - (get_size().width - theme_cache.scroll_border); } - if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) { - point.y = mouse_position.y - cache.scroll_border; - } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) { - point.y = mouse_position.y - (get_size().height - cache.scroll_border); + if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < theme_cache.scroll_border)) { + point.y = mouse_position.y - theme_cache.scroll_border; + } else if (ABS(mouse_position.y - get_size().height) < theme_cache.scroll_border) { + point.y = mouse_position.y - (get_size().height - theme_cache.scroll_border); } - point *= cache.scroll_speed * get_physics_process_delta_time(); + point *= theme_cache.scroll_speed * get_physics_process_delta_time(); point += get_scroll(); h_scroll->set_value(point.x); v_scroll->set_value(point.y); @@ -3940,13 +3936,12 @@ void Tree::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - update_cache(); + v_scroll->set_custom_step(theme_cache.font->get_height(theme_cache.font_size)); + update_scrollbars(); RID ci = get_canvas_item(); - Ref<StyleBox> bg = cache.bg; - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Ref<StyleBox> bg = theme_cache.bg; Point2 draw_ofs; draw_ofs += bg->get_offset(); @@ -3970,11 +3965,11 @@ void Tree::_notification(int p_what) { if (show_column_titles) { //title buttons - int ofs2 = cache.bg->get_margin(SIDE_LEFT); + int ofs2 = theme_cache.bg->get_margin(SIDE_LEFT); for (int i = 0; i < columns.size(); i++) { - Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); - Ref<Font> f = cache.tb_font; - Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); + Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button); + Ref<Font> f = theme_cache.tb_font; + Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); if (cache.rtl) { tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; } @@ -3985,10 +3980,10 @@ void Tree::_notification(int p_what) { columns.write[i].text_buf->set_width(clip_w); Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) { + columns[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color); } - columns[i].text_buf->draw(ci, text_pos, cache.title_button_color); + columns[i].text_buf->draw(ci, text_pos, theme_cache.title_button_color); } } @@ -3996,8 +3991,7 @@ void Tree::_notification(int p_what) { // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling. if (has_focus()) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); - const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus")); - bg_focus->draw(ci, Rect2(Point2(), get_size())); + theme_cache.bg_focus->draw(ci, Rect2(Point2(), get_size())); RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); } } break; @@ -4005,7 +3999,6 @@ void Tree::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - update_cache(); _update_all(); } break; @@ -4040,7 +4033,7 @@ Size2 Tree::get_minimum_size() const { return Size2(); } else { Vector2 min_size = get_internal_min_size(); - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.bg; if (bg.is_valid()) { min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); @@ -4336,7 +4329,7 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); + min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.bg->get_margin(SIDE_LEFT) + theme_cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -4360,9 +4353,9 @@ int Tree::get_column_minimum_width(int p_column) const { // Get the item minimum size. Size2 item_size = item->get_minimum_size(p_column); if (p_column == 0) { - item_size.width += cache.item_margin * depth; + item_size.width += theme_cache.item_margin * depth; } else { - item_size.width += cache.hseparation; + item_size.width += theme_cache.hseparation; } // Check if the item is wider. @@ -4381,7 +4374,7 @@ int Tree::get_column_width(int p_column) const { if (columns[p_column].expand) { int expand_area = get_size().width; - Ref<StyleBox> bg = cache.bg; + Ref<StyleBox> bg = theme_cache.bg; if (bg.is_valid()) { expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); @@ -4458,7 +4451,7 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += compute_item_height(it); if (it != root || !hide_root) { - ofs += cache.vseparation; + ofs += theme_cache.vseparation; } if (it->first_child && !it->collapsed) { @@ -4489,14 +4482,14 @@ void Tree::ensure_cursor_is_visible() { return; // Nothing under cursor. } - const Size2 area_size = get_size() - cache.bg->get_minimum_size(); + const Size2 area_size = get_size() - theme_cache.bg->get_minimum_size(); int y_offset = get_item_offset(selected_item); if (y_offset != -1) { const int tbh = _get_title_button_height(); y_offset -= tbh; - const int cell_h = compute_item_height(selected_item) + cache.vseparation; + const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation; const int screen_h = area_size.height - h_scroll->get_combined_minimum_size().height - tbh; if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet. @@ -4563,7 +4556,7 @@ Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const { Vector2 ofst = Vector2(r.position.x + r.size.x, r.position.y); for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); ofst.x -= size.x; if (j == p_button) { @@ -4596,9 +4589,6 @@ void Tree::set_column_title(int p_column, const String &p_title) { return; } - if (cache.font.is_null()) { // avoid a strange case that may corrupt stuff - update_cache(); - } columns.write[p_column].title = p_title; update_column(p_column); queue_redraw(); @@ -4660,7 +4650,7 @@ void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { const real_t tree_height = get_size().y; const Rect2 item_rect = get_item_rect(p_item); const real_t item_y = item_rect.position.y; - const real_t item_height = item_rect.size.y + cache.vseparation; + const real_t item_height = item_rect.size.y + theme_cache.vseparation; if (p_center_on_item) { v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f); @@ -4784,7 +4774,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ Point2 pos = p_pos; if ((root != p_item || !hide_root) && p_item->is_visible()) { - h = compute_item_height(p_item) + cache.vseparation; + h = compute_item_height(p_item) + theme_cache.vseparation; if (pos.y < h) { if (drop_mode_flags == DROP_MODE_ON_ITEM) { section = 0; @@ -4841,7 +4831,7 @@ int Tree::get_column_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -1; @@ -4871,7 +4861,7 @@ int Tree::get_drop_section_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -100; @@ -4901,7 +4891,7 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { if (is_layout_rtl()) { pos.x = get_size().width - pos.x; } - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return nullptr; @@ -4928,7 +4918,7 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { int Tree::get_button_id_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return -1; @@ -4954,7 +4944,7 @@ int Tree::get_button_id_at_position(const Point2 &p_pos) const { for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); if (pos.x > col_width - size.width) { return c.buttons[j].id; } @@ -4969,7 +4959,7 @@ int Tree::get_button_id_at_position(const Point2 &p_pos) const { String Tree::get_tooltip(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; - pos -= cache.bg->get_offset(); + pos -= theme_cache.bg->get_offset(); pos.y -= _get_title_button_height(); if (pos.y < 0) { return Control::get_tooltip(p_pos); @@ -4995,7 +4985,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const { for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); if (pos.x > col_width - size.width) { String tooltip = c.buttons[j].tooltip; if (!tooltip.is_empty()) { @@ -5231,8 +5221,6 @@ Tree::Tree() { set_mouse_filter(MOUSE_FILTER_STOP); set_clip_contents(true); - - update_cache(); } Tree::~Tree() { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 8eabdd60a1..b4ee686bab 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -482,12 +482,13 @@ private: void propagate_set_columns(TreeItem *p_item); - struct Cache { + struct ThemeCache { Ref<Font> font; Ref<Font> tb_font; int font_size = 0; int tb_font_size = 0; Ref<StyleBox> bg; + Ref<StyleBox> bg_focus; Ref<StyleBox> selected; Ref<StyleBox> selected_focus; Ref<StyleBox> cursor; @@ -505,8 +506,9 @@ private: Ref<Texture2D> checked; Ref<Texture2D> unchecked; Ref<Texture2D> indeterminate; - Ref<Texture2D> arrow_collapsed; Ref<Texture2D> arrow; + Ref<Texture2D> arrow_collapsed; + Ref<Texture2D> arrow_collapsed_mirrored; Ref<Texture2D> select_arrow; Ref<Texture2D> updown; @@ -536,7 +538,9 @@ private: int scroll_border = 0; int scroll_speed = 0; int font_outline_size = 0; + } theme_cache; + struct Cache { enum ClickType { CLICK_NONE, CLICK_TITLE, @@ -559,7 +563,6 @@ private: Point2i text_editor_position; bool rtl = false; - } cache; int _get_title_button_height() const; @@ -572,7 +575,6 @@ private: bool v_scroll_enabled = true; Size2 get_internal_min_size() const; - void update_cache(); void update_scrollbars(); Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item); @@ -620,6 +622,8 @@ private: bool _scroll(bool p_horizontal, float p_pages); protected: + virtual void _update_theme_item_cache() override; + static void _bind_methods(); public: diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 61a7600664..65e7ba3e67 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -338,6 +338,7 @@ void CanvasItem::_notification(int p_what) { } if (window) { window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); + window = nullptr; } global_invalid = true; parent_visible_in_tree = false; diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp index aad5baccab..9b63118f7c 100644 --- a/scene/main/multiplayer_peer.cpp +++ b/scene/main/multiplayer_peer.cpp @@ -120,19 +120,10 @@ void MultiplayerPeer::_bind_methods() { /*************/ -int MultiplayerPeerExtension::get_available_packet_count() const { - int count; - if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { - return count; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!"); - return -1; -} - Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { - return (Error)err; + return err; } if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) { if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) { @@ -153,9 +144,9 @@ Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buff } Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { - return (Error)err; + return err; } if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) { PackedByteArray a; @@ -171,87 +162,6 @@ Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer return FAILED; } -int MultiplayerPeerExtension::get_max_packet_size() const { - int size; - if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { - return size; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!"); - return 0; -} - -void MultiplayerPeerExtension::set_transfer_channel(int p_channel) { - if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) { - return; - } - MultiplayerPeer::set_transfer_channel(p_channel); -} - -int MultiplayerPeerExtension::get_transfer_channel() const { - int channel; - if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) { - return channel; - } - return MultiplayerPeer::get_transfer_channel(); -} - -void MultiplayerPeerExtension::set_transfer_mode(TransferMode p_mode) { - if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) { - return; - } - MultiplayerPeer::set_transfer_mode(p_mode); -} - -MultiplayerPeer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const { - int mode; - if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) { - return (MultiplayerPeer::TransferMode)mode; - } - return MultiplayerPeer::get_transfer_mode(); -} - -void MultiplayerPeerExtension::set_target_peer(int p_peer_id) { - if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) { - return; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!"); -} - -int MultiplayerPeerExtension::get_packet_peer() const { - int peer; - if (GDVIRTUAL_CALL(_get_packet_peer, peer)) { - return peer; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!"); - return 0; -} - -bool MultiplayerPeerExtension::is_server() const { - bool server; - if (GDVIRTUAL_CALL(_is_server, server)) { - return server; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!"); - return false; -} - -void MultiplayerPeerExtension::poll() { - int err; - if (GDVIRTUAL_CALL(_poll, err)) { - return; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!"); -} - -int MultiplayerPeerExtension::get_unique_id() const { - int id; - if (GDVIRTUAL_CALL(_get_unique_id, id)) { - return id; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!"); - return 0; -} - void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { return; @@ -267,15 +177,6 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const { return MultiplayerPeer::is_refusing_new_connections(); } -MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const { - int status; - if (GDVIRTUAL_CALL(_get_connection_status, status)) { - return (ConnectionStatus)status; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!"); - return CONNECTION_DISCONNECTED; -} - void MultiplayerPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); @@ -300,4 +201,7 @@ void MultiplayerPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); GDVIRTUAL_BIND(_is_refusing_new_connections); GDVIRTUAL_BIND(_get_connection_status); + + ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_RELIABLE); + ADD_PROPERTY_DEFAULT("transfer_channel", 0); } diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h index 8a012d7520..ab7483ece5 100644 --- a/scene/main/multiplayer_peer.h +++ b/scene/main/multiplayer_peer.h @@ -33,6 +33,7 @@ #include "core/io/packet_peer.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -103,55 +104,35 @@ protected: PackedByteArray script_buffer; public: - /* PacketPeer */ - virtual int get_available_packet_count() const override; + /* PacketPeer extension */ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; - - /* MultiplayerPeer */ - virtual void set_transfer_channel(int p_channel) override; - virtual int get_transfer_channel() const override; - virtual void set_transfer_mode(TransferMode p_mode) override; - virtual TransferMode get_transfer_mode() const override; - virtual void set_target_peer(int p_peer_id) override; - - virtual int get_packet_peer() const override; - - virtual bool is_server() const override; + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript. - virtual void poll() override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL1R(Error, _put_packet_script, PackedByteArray); // For GDScript. - virtual int get_unique_id() const override; + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); + /* MultiplayerPeer extension */ virtual void set_refuse_new_connections(bool p_enable) override; - virtual bool is_refusing_new_connections() const override; + GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional. - virtual ConnectionStatus get_connection_status() const override; - - /* PacketPeer GDExtension */ - GDVIRTUAL0RC(int, _get_available_packet_count); - GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); - GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); - GDVIRTUAL0RC(int, _get_max_packet_size); - - /* PacketPeer GDScript */ - GDVIRTUAL0R(PackedByteArray, _get_packet_script); - GDVIRTUAL1R(int, _put_packet_script, PackedByteArray); - - /* MultiplayerPeer GDExtension */ - GDVIRTUAL1(_set_transfer_channel, int); - GDVIRTUAL0RC(int, _get_transfer_channel); - GDVIRTUAL1(_set_transfer_mode, int); - GDVIRTUAL0RC(int, _get_transfer_mode); - GDVIRTUAL1(_set_target_peer, int); - GDVIRTUAL0RC(int, _get_packet_peer); - GDVIRTUAL0RC(bool, _is_server); - GDVIRTUAL0R(int, _poll); - GDVIRTUAL0RC(int, _get_unique_id); - GDVIRTUAL1(_set_refuse_new_connections, bool); - GDVIRTUAL0RC(bool, _is_refusing_new_connections); - GDVIRTUAL0RC(int, _get_connection_status); + virtual bool is_refusing_new_connections() const override; + GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional. + + EXBIND1(set_transfer_channel, int); + EXBIND0RC(int, get_transfer_channel); + EXBIND1(set_transfer_mode, TransferMode); + EXBIND0RC(TransferMode, get_transfer_mode); + EXBIND1(set_target_peer, int); + EXBIND0RC(int, get_packet_peer); + EXBIND0RC(bool, is_server); + EXBIND0(poll); + EXBIND0RC(int, get_unique_id); + EXBIND0RC(ConnectionStatus, get_connection_status); }; #endif // MULTIPLAYER_PEER_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index ec98ff36a0..268b381029 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -46,6 +46,7 @@ #include "scene/debugger/scene_debugger.h" #include "scene/main/multiplayer_api.h" #include "scene/main/viewport.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -1472,18 +1473,18 @@ SceneTree::SceneTree() { } } - int shadowmap_size = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size", 4096); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size.mobile", 2048); - bool shadowmap_16_bits = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_16_bits", true); - int atlas_q0 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); - int atlas_q1 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); - int atlas_q2 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); - int atlas_q3 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + int shadowmap_size = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size.mobile", 2048); + bool shadowmap_16_bits = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_16_bits", true); + int atlas_q0 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); + int atlas_q1 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); + int atlas_q2 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); + int atlas_q3 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); root->set_positional_shadow_atlas_size(shadowmap_size); root->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index a738c6eabc..1bb1faacdd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -190,14 +190,7 @@ void Viewport::_sub_window_register(Window *p_window) { } void Viewport::_sub_window_update(Window *p_window) { - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); const SubWindow &sw = gui.sub_windows[index]; @@ -257,14 +250,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { return; } - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); if (p_window->get_flag(Window::FLAG_NO_FOCUS)) { @@ -312,13 +298,11 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { } void Viewport::_sub_window_remove(Window *p_window) { - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - RS::get_singleton()->free(gui.sub_windows[i].canvas_item); - gui.sub_windows.remove_at(i); - break; - } - } + int index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); + + RS::get_singleton()->free(gui.sub_windows[index].canvas_item); + gui.sub_windows.remove_at(index); if (gui.sub_windows.size() == 0) { RS::get_singleton()->free(subwindow_canvas); @@ -326,27 +310,46 @@ void Viewport::_sub_window_remove(Window *p_window) { } if (gui.subwindow_focused == p_window) { + Window *new_focused_window; Window *parent_visible = p_window->get_parent_visible_window(); gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); - if (parent_visible && parent_visible != this) { - gui.subwindow_focused = parent_visible; - gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + if (parent_visible) { + new_focused_window = parent_visible; } else { - gui.subwindow_focused = nullptr; - Window *this_window = Object::cast_to<Window>(this); - if (this_window) { - this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + new_focused_window = Object::cast_to<Window>(this); + } + + if (new_focused_window) { + int new_focused_index = _sub_window_find(new_focused_window); + if (new_focused_index != -1) { + gui.subwindow_focused = new_focused_window; + } else { + gui.subwindow_focused = nullptr; } + + new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } else { + gui.subwindow_focused = nullptr; } } RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID()); } +int Viewport::_sub_window_find(Window *p_window) { + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + return i; + } + } + + return -1; +} + void Viewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0b3853ba79..afea3ea56c 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -461,6 +461,7 @@ private: void _sub_window_update(Window *p_window); void _sub_window_grab_focus(Window *p_window); void _sub_window_remove(Window *p_window); + int _sub_window_find(Window *p_window); bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 1e52b644a3..79a1c71064 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -799,6 +799,11 @@ Viewport *Window::_get_embedder() const { void Window::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + } break; + case NOTIFICATION_ENTER_TREE: { bool embedded = false; { @@ -858,6 +863,8 @@ void Window::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { emit_signal(SceneStringNames::get_singleton()->theme_changed); + _invalidate_theme_cache(); + _update_theme_item_cache(); } break; case NOTIFICATION_READY: { @@ -867,6 +874,9 @@ void Window::_notification(int p_what) { } break; case NOTIFICATION_TRANSLATION_CHANGED: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + if (embedder) { embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { @@ -1342,6 +1352,18 @@ void Window::_theme_changed() { } } +void Window::_invalidate_theme_cache() { + theme_icon_cache.clear(); + theme_style_cache.clear(); + theme_font_cache.clear(); + theme_font_size_cache.clear(); + theme_color_cache.clear(); + theme_constant_cache.clear(); +} + +void Window::_update_theme_item_cache() { +} + void Window::set_theme_type_variation(const StringName &p_theme_type) { theme_type_variation = p_theme_type; if (is_inside_tree()) { @@ -1366,39 +1388,75 @@ void Window::_get_theme_type_dependencies(const StringName &p_theme_type, List<S } Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_icon_cache.has(p_theme_type) && theme_icon_cache[p_theme_type].has(p_name)) { + return theme_icon_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Texture2D>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + Ref<Texture2D> icon = Control::get_theme_item_in_types<Ref<Texture2D>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_icon_cache[p_theme_type][p_name] = icon; + return icon; } Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_style_cache.has(p_theme_type) && theme_style_cache[p_theme_type].has(p_name)) { + return theme_style_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<StyleBox>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + Ref<StyleBox> style = Control::get_theme_item_in_types<Ref<StyleBox>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_style_cache[p_theme_type][p_name] = style; + return style; } Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_cache.has(p_theme_type) && theme_font_cache[p_theme_type].has(p_name)) { + return theme_font_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Font>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + Ref<Font> font = Control::get_theme_item_in_types<Ref<Font>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_font_cache[p_theme_type][p_name] = font; + return font; } int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_size_cache.has(p_theme_type) && theme_font_size_cache[p_theme_type].has(p_name)) { + return theme_font_size_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + int font_size = Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_font_size_cache[p_theme_type][p_name] = font_size; + return font_size; } Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_color_cache.has(p_theme_type) && theme_color_cache[p_theme_type].has(p_name)) { + return theme_color_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Color>(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + Color color = Control::get_theme_item_in_types<Color>(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_color_cache[p_theme_type][p_name] = color; + return color; } int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_constant_cache.has(p_theme_type) && theme_constant_cache[p_theme_type].has(p_name)) { + return theme_constant_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + int constant = Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_constant_cache[p_theme_type][p_name] = constant; + return constant; } bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { diff --git a/scene/main/window.h b/scene/main/window.h index 238be484c0..5a42c5bb83 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -32,12 +32,12 @@ #define WINDOW_H #include "scene/main/viewport.h" +#include "scene/resources/theme.h" class Control; class Font; class Shortcut; class StyleBox; -class Theme; class Window : public Viewport { GDCLASS(Window, Viewport) @@ -141,6 +141,18 @@ private: Window *theme_owner_window = nullptr; StringName theme_type_variation; + mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache; + mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache; + mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache; + mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache; + mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache; + mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache; + + _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; + + void _theme_changed(); + void _invalidate_theme_cache(); + Viewport *embedder = nullptr; friend class Viewport; //friend back, can call the methods below @@ -158,6 +170,8 @@ protected: Viewport *_get_embedder() const; virtual Rect2i _popup_adjust_rect() const { return Rect2i(); } + virtual void _update_theme_item_cache(); + virtual void _post_popup() {} virtual Size2 _get_contents_minimum_size() const; static void _bind_methods(); @@ -259,11 +273,9 @@ public: void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; - void _theme_changed(); void set_theme_type_variation(const StringName &p_theme_type); StringName get_theme_type_variation() const; - _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; Size2 get_contents_minimum_size() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 370334f02e..a9470596b2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -54,6 +54,7 @@ #include "scene/2d/mesh_instance_2d.h" #include "scene/2d/multimesh_instance_2d.h" #include "scene/2d/navigation_agent_2d.h" +#include "scene/2d/navigation_link_2d.h" #include "scene/2d/navigation_obstacle_2d.h" #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" @@ -142,7 +143,7 @@ #include "scene/resources/bit_map.h" #include "scene/resources/bone_map.h" #include "scene/resources/box_shape_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/circle_shape_2d.h" @@ -152,6 +153,7 @@ #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" #include "scene/resources/default_theme/default_theme.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/height_map_shape_3d.h" @@ -239,6 +241,7 @@ #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" #include "scene/3d/navigation_agent_3d.h" +#include "scene/3d/navigation_link_3d.h" #include "scene/3d/navigation_obstacle_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/node_3d.h" @@ -377,14 +380,14 @@ void register_scene_types() { GDREGISTER_CLASS(VSeparator); GDREGISTER_CLASS(TextureButton); GDREGISTER_CLASS(Container); - GDREGISTER_ABSTRACT_CLASS(BoxContainer); + GDREGISTER_CLASS(BoxContainer); GDREGISTER_CLASS(HBoxContainer); GDREGISTER_CLASS(VBoxContainer); GDREGISTER_CLASS(GridContainer); GDREGISTER_CLASS(CenterContainer); GDREGISTER_CLASS(ScrollContainer); GDREGISTER_CLASS(PanelContainer); - GDREGISTER_ABSTRACT_CLASS(FlowContainer); + GDREGISTER_CLASS(FlowContainer); GDREGISTER_CLASS(HFlowContainer); GDREGISTER_CLASS(VFlowContainer); @@ -421,7 +424,7 @@ void register_scene_types() { GDREGISTER_CLASS(MarginContainer); GDREGISTER_CLASS(SubViewportContainer); - GDREGISTER_ABSTRACT_CLASS(SplitContainer); + GDREGISTER_CLASS(SplitContainer); GDREGISTER_CLASS(HSplitContainer); GDREGISTER_CLASS(VSplitContainer); @@ -576,6 +579,7 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationRegion3D); GDREGISTER_CLASS(NavigationAgent3D); GDREGISTER_CLASS(NavigationObstacle3D); + GDREGISTER_CLASS(NavigationLink3D); OS::get_singleton()->yield(); // may take time to init #endif // _3D_DISABLED @@ -837,7 +841,9 @@ void register_scene_types() { GDREGISTER_CLASS(PhysicsMaterial); GDREGISTER_CLASS(World3D); GDREGISTER_CLASS(Environment); - GDREGISTER_CLASS(CameraEffects); + GDREGISTER_VIRTUAL_CLASS(CameraAttributes); + GDREGISTER_CLASS(CameraAttributesPhysical); + GDREGISTER_CLASS(CameraAttributesPractical); GDREGISTER_CLASS(World2D); GDREGISTER_VIRTUAL_CLASS(Texture); GDREGISTER_VIRTUAL_CLASS(Texture2D); @@ -932,6 +938,7 @@ void register_scene_types() { GDREGISTER_CLASS(NavigationRegion2D); GDREGISTER_CLASS(NavigationAgent2D); GDREGISTER_CLASS(NavigationObstacle2D); + GDREGISTER_CLASS(NavigationLink2D); OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 9b1adde00a..0505f6b559 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -33,13 +33,18 @@ #include "core/io/image_loader.h" #include "core/variant/typed_array.h" -void BitMap::create(const Size2 &p_size) { +void BitMap::create(const Size2i &p_size) { ERR_FAIL_COND(p_size.width < 1); ERR_FAIL_COND(p_size.height < 1); + ERR_FAIL_COND(static_cast<int64_t>(p_size.width) * static_cast<int64_t>(p_size.height) > INT32_MAX); + + Error err = bitmask.resize((((p_size.width * p_size.height) - 1) / 8) + 1); + ERR_FAIL_COND(err != OK); + width = p_size.width; height = p_size.height; - bitmask.resize((((width * height) - 1) / 8) + 1); + memset(bitmask.ptrw(), 0, bitmask.size()); } @@ -49,7 +54,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - create(img->get_size()); + create(Size2i(img->get_width(), img->get_height())); const uint8_t *r = img->get_data().ptr(); uint8_t *w = bitmask.ptrw(); @@ -63,7 +68,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol } } -void BitMap::set_bit_rect(const Rect2 &p_rect, bool p_value) { +void BitMap::set_bit_rect(const Rect2i &p_rect, bool p_value) { Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect); uint8_t *data = bitmask.ptrw(); @@ -91,7 +96,7 @@ int BitMap::get_true_bit_count() const { const uint8_t *d = bitmask.ptr(); int c = 0; - //fast, almost branchless version + // Fast, almost branchless version. for (int i = 0; i < ds; i++) { c += (d[i] & (1 << 7)) >> 7; @@ -107,14 +112,15 @@ int BitMap::get_true_bit_count() const { return c; } -void BitMap::set_bit(const Point2 &p_pos, bool p_value) { - int x = p_pos.x; - int y = p_pos.y; +void BitMap::set_bitv(const Point2i &p_pos, bool p_value) { + set_bit(p_pos.x, p_pos.y, p_value); +} - ERR_FAIL_INDEX(x, width); - ERR_FAIL_INDEX(y, height); +void BitMap::set_bit(int p_x, int p_y, bool p_value) { + ERR_FAIL_INDEX(p_x, width); + ERR_FAIL_INDEX(p_y, height); - int ofs = width * y + x; + int ofs = width * p_y + p_x; int bbyte = ofs / 8; int bbit = ofs % 8; @@ -129,21 +135,23 @@ void BitMap::set_bit(const Point2 &p_pos, bool p_value) { bitmask.write[bbyte] = b; } -bool BitMap::get_bit(const Point2 &p_pos) const { - int x = Math::fast_ftoi(p_pos.x); - int y = Math::fast_ftoi(p_pos.y); - ERR_FAIL_INDEX_V(x, width, false); - ERR_FAIL_INDEX_V(y, height, false); +bool BitMap::get_bitv(const Point2i &p_pos) const { + return get_bit(p_pos.x, p_pos.y); +} + +bool BitMap::get_bit(int p_x, int p_y) const { + ERR_FAIL_INDEX_V(p_x, width, false); + ERR_FAIL_INDEX_V(p_y, height, false); - int ofs = width * y + x; + int ofs = width * p_y + p_x; int bbyte = ofs / 8; int bbit = ofs % 8; return (bitmask[bbyte] & (1 << bbit)) != 0; } -Size2 BitMap::get_size() const { - return Size2(width, height); +Size2i BitMap::get_size() const { + return Size2i(width, height); } void BitMap::_set_data(const Dictionary &p_d) { @@ -161,13 +169,13 @@ Dictionary BitMap::_get_data() const { return d; } -Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) const { +Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { int stepx = 0; int stepy = 0; int prevx = 0; int prevy = 0; - int startx = start.x; - int starty = start.y; + int startx = p_start.x; + int starty = p_start.y; int curx = startx; int cury = starty; unsigned int count = 0; @@ -176,7 +184,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) Vector<Vector2> _points; do { int sv = 0; - { //square value + { // Square value /* checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent @@ -187,13 +195,13 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) +---+---+ */ Point2i tl = Point2i(curx - 1, cury - 1); - sv += (rect.has_point(tl) && get_bit(tl)) ? 1 : 0; + sv += (p_rect.has_point(tl) && get_bitv(tl)) ? 1 : 0; Point2i tr = Point2i(curx, cury - 1); - sv += (rect.has_point(tr) && get_bit(tr)) ? 2 : 0; + sv += (p_rect.has_point(tr) && get_bitv(tr)) ? 2 : 0; Point2i bl = Point2i(curx - 1, cury); - sv += (rect.has_point(bl) && get_bit(bl)) ? 4 : 0; + sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); - sv += (rect.has_point(br) && get_bit(br)) ? 8 : 0; + sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); } @@ -303,16 +311,16 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) default: ERR_PRINT("this shouldn't happen."); } - //little optimization - // if previous direction is same as current direction, - // then we should modify the last vec to current + // Small optimization: + // If the previous direction is same as the current direction, + // then we should modify the last vector to current. curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); + _points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); + _points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); } else { - _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); + _points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); } count++; @@ -348,7 +356,7 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { int index = -1; float dist = 0.0; - //not looping first and last point + // Not looping first and last point. for (size_t i = 1, size = v.size(); i < size - 1; ++i) { float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]); if (cdist > dist) { @@ -385,9 +393,9 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, float epsilon) { int size = points.size(); - // if there are less than 3 points, then we have nothing + // If there are less than 3 points, then we have nothing. ERR_FAIL_COND_V(size < 3, Vector<Vector2>()); - // if there are less than 9 points (but more than 3), then we don't need to reduce it + // If there are less than 9 points (but more than 3), then we don't need to reduce it. if (size < 9) { return points; } @@ -412,9 +420,9 @@ struct FillBitsStackEntry { }; static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) { - // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps + // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps. Vector<FillBitsStackEntry> stack; - // Tracking size since we won't be shrinking the stack vector + // Tracking size since we won't be shrinking the stack vector. int stack_size = 0; Point2i pos = p_pos; @@ -433,10 +441,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ for (int i = next_i; i <= pos.x + 1; i++) { for (int j = next_j; j <= pos.y + 1; j++) { if (popped) { - // The next loop over j must start normally + // The next loop over j must start normally. next_j = pos.y; popped = false; - // Skip because an iteration was already executed with current counter values + // Skip because an iteration was already executed with current counter values. continue; } @@ -447,11 +455,11 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ continue; } - if (p_map->get_bit(Vector2(i, j))) { + if (p_map->get_bit(i, j)) { continue; - } else if (p_src->get_bit(Vector2(i, j))) { - p_map->set_bit(Vector2(i, j), true); + } else if (p_src->get_bit(i, j)) { + p_map->set_bit(i, j, true); FillBitsStackEntry se = { pos, i, j }; stack.resize(MAX(stack_size + 1, stack.size())); @@ -482,7 +490,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ print_verbose("BitMap: Max stack size: " + itos(stack.size())); } -Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { +Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); print_verbose("BitMap: Rect: " + r); @@ -494,7 +502,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo Vector<Vector<Vector2>> polygons; for (int i = r.position.y; i < r.position.y + r.size.height; i++) { for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { + if (!fill->get_bit(j, i) && get_bit(j, i)) { fill_bits(this, fill, Point2i(j, i), r); Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); @@ -515,7 +523,7 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, flo return polygons; } -void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { +void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) { if (p_pixels == 0) { return; } @@ -532,7 +540,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { for (int i = r.position.y; i < r.position.y + r.size.height; i++) { for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (bit_value == get_bit(Point2(j, i))) { + if (bit_value == get_bit(j, i)) { continue; } @@ -543,7 +551,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { bool outside = false; if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) { - // outside of rectangle counts as bit not set + // Outside of rectangle counts as bit not set. if (!bit_value) { outside = true; } else { @@ -556,7 +564,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { continue; } - if (outside || (bit_value == copy->get_bit(Point2(x, y)))) { + if (outside || (bit_value == copy->get_bit(x, y))) { found = true; break; } @@ -567,20 +575,20 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { } if (found) { - set_bit(Point2(j, i), bit_value); + set_bit(j, i, bit_value); } } } } -void BitMap::shrink_mask(int p_pixels, const Rect2 &p_rect) { +void BitMap::shrink_mask(int p_pixels, const Rect2i &p_rect) { grow_mask(-p_pixels, p_rect); } -TypedArray<PackedVector2Array> BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { +TypedArray<PackedVector2Array> BitMap::_opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const { Vector<Vector<Vector2>> result = clip_opaque_to_polygons(p_rect, p_epsilon); - // Convert result to bindable types + // Convert result to bindable types. TypedArray<PackedVector2Array> result_array; result_array.resize(result.size()); @@ -603,15 +611,25 @@ TypedArray<PackedVector2Array> BitMap::_opaque_to_polygons_bind(const Rect2 &p_r return result_array; } -void BitMap::resize(const Size2 &p_new_size) { +void BitMap::resize(const Size2i &p_new_size) { + ERR_FAIL_COND(p_new_size.width < 0 || p_new_size.height < 0); + if (p_new_size == get_size()) { + return; + } + Ref<BitMap> new_bitmap; new_bitmap.instantiate(); new_bitmap->create(p_new_size); - int lw = MIN(width, p_new_size.width); - int lh = MIN(height, p_new_size.height); + // also allow for upscaling + int lw = (width == 0) ? 0 : p_new_size.width; + int lh = (height == 0) ? 0 : p_new_size.height; + + float scale_x = ((float)width / p_new_size.width); + float scale_y = ((float)height / p_new_size.height); for (int x = 0; x < lw; x++) { for (int y = 0; y < lh; y++) { - new_bitmap->set_bit(Vector2(x, y), get_bit(Vector2(x, y))); + bool new_bit = get_bit(x * scale_x, y * scale_y); + new_bitmap->set_bit(x, y, new_bit); } } @@ -627,14 +645,16 @@ Ref<Image> BitMap::convert_to_image() const { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { - image->set_pixel(i, j, get_bit(Point2(i, j)) ? Color(1, 1, 1) : Color(0, 0, 0)); + image->set_pixel(i, j, get_bit(i, j) ? Color(1, 1, 1) : Color(0, 0, 0)); } } return image; } -void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { +void BitMap::blit(const Vector2i &p_pos, const Ref<BitMap> &p_bitmap) { + ERR_FAIL_COND_MSG(p_bitmap.is_null(), "It's not a reference to a valid BitMap object."); + int x = p_pos.x; int y = p_pos.y; int w = p_bitmap->get_size().width; @@ -650,8 +670,8 @@ void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { if (py < 0 || py >= height) { continue; } - if (p_bitmap->get_bit(Vector2(i, j))) { - set_bit(Vector2(x, y), true); + if (p_bitmap->get_bit(i, j)) { + set_bit(px, py, true); } } } @@ -661,8 +681,10 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "threshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1)); - ClassDB::bind_method(D_METHOD("set_bit", "position", "bit"), &BitMap::set_bit); - ClassDB::bind_method(D_METHOD("get_bit", "position"), &BitMap::get_bit); + ClassDB::bind_method(D_METHOD("set_bitv", "position", "bit"), &BitMap::set_bitv); + ClassDB::bind_method(D_METHOD("set_bit", "x", "y", "bit"), &BitMap::set_bit); + ClassDB::bind_method(D_METHOD("get_bitv", "position"), &BitMap::get_bitv); + ClassDB::bind_method(D_METHOD("get_bit", "x", "y"), &BitMap::get_bit); ClassDB::bind_method(D_METHOD("set_bit_rect", "rect", "bit"), &BitMap::set_bit_rect); ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count); @@ -681,5 +703,3 @@ void BitMap::_bind_methods() { } BitMap::BitMap() {} - -////////////////////////////////////// diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index d8507dfa8b..291ed8c4d0 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -46,9 +46,9 @@ class BitMap : public Resource { int width = 0; int height = 0; - Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const; + Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; - TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; + TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; protected: void _set_data(const Dictionary &p_d); @@ -57,24 +57,27 @@ protected: static void _bind_methods(); public: - void create(const Size2 &p_size); + void create(const Size2i &p_size); void create_from_image_alpha(const Ref<Image> &p_image, float p_threshold = 0.1); - void set_bit(const Point2 &p_pos, bool p_value); - bool get_bit(const Point2 &p_pos) const; - void set_bit_rect(const Rect2 &p_rect, bool p_value); + void set_bitv(const Point2i &p_pos, bool p_value); + void set_bit(int p_x, int p_y, bool p_value); + void set_bit_rect(const Rect2i &p_rect, bool p_value); + bool get_bitv(const Point2i &p_pos) const; + bool get_bit(int p_x, int p_y) const; + int get_true_bit_count() const; - Size2 get_size() const; - void resize(const Size2 &p_new_size); + Size2i get_size() const; + void resize(const Size2i &p_new_size); - void grow_mask(int p_pixels, const Rect2 &p_rect); - void shrink_mask(int p_pixels, const Rect2 &p_rect); + void grow_mask(int p_pixels, const Rect2i &p_rect); + void shrink_mask(int p_pixels, const Rect2i &p_rect); - void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap); + void blit(const Vector2i &p_pos, const Ref<BitMap> &p_bitmap); Ref<Image> convert_to_image() const; - Vector<Vector<Vector2>> clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; + Vector<Vector<Vector2>> clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon = 2.0) const; BitMap(); }; diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp new file mode 100644 index 0000000000..3c322f32b6 --- /dev/null +++ b/scene/resources/camera_attributes.cpp @@ -0,0 +1,493 @@ +/*************************************************************************/ +/* camera_attributes.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_attributes.h" + +#include "core/config/project_settings.h" +#include "servers/rendering_server.h" + +void CameraAttributes::set_exposure_multiplier(float p_multiplier) { + exposure_multiplier = p_multiplier; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_multiplier() const { + return exposure_multiplier; +} + +void CameraAttributes::set_exposure_sensitivity(float p_sensitivity) { + exposure_sensitivity = p_sensitivity; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_sensitivity() const { + return exposure_sensitivity; +} + +void CameraAttributes::_update_exposure() { + float exposure_normalization = 1.0; + // Ignore physical properties if not using physical light units. + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + exposure_normalization = calculate_exposure_normalization(); + } + + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, exposure_multiplier, exposure_normalization); +} + +void CameraAttributes::set_auto_exposure_enabled(bool p_enabled) { + auto_exposure_enabled = p_enabled; + _update_auto_exposure(); + notify_property_list_changed(); +} + +bool CameraAttributes::is_auto_exposure_enabled() const { + return auto_exposure_enabled; +} + +void CameraAttributes::set_auto_exposure_speed(float p_auto_exposure_speed) { + auto_exposure_speed = p_auto_exposure_speed; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_speed() const { + return auto_exposure_speed; +} + +void CameraAttributes::set_auto_exposure_scale(float p_auto_exposure_scale) { + auto_exposure_scale = p_auto_exposure_scale; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_scale() const { + return auto_exposure_scale; +} + +RID CameraAttributes::get_rid() const { + return camera_attributes; +} + +void CameraAttributes::_validate_property(PropertyInfo &p_property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && p_property.name == "exposure_sensitivity") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } + + if (p_property.name.begins_with("auto_exposure_") && p_property.name != "auto_exposure_enabled" && !auto_exposure_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributes::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_exposure_multiplier", "multiplier"), &CameraAttributes::set_exposure_multiplier); + ClassDB::bind_method(D_METHOD("get_exposure_multiplier"), &CameraAttributes::get_exposure_multiplier); + ClassDB::bind_method(D_METHOD("set_exposure_sensitivity", "sensitivity"), &CameraAttributes::set_exposure_sensitivity); + ClassDB::bind_method(D_METHOD("get_exposure_sensitivity"), &CameraAttributes::get_exposure_sensitivity); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_enabled", "enabled"), &CameraAttributes::set_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("is_auto_exposure_enabled"), &CameraAttributes::is_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("set_auto_exposure_speed", "exposure_speed"), &CameraAttributes::set_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("get_auto_exposure_speed"), &CameraAttributes::get_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("set_auto_exposure_scale", "exposure_grey"), &CameraAttributes::set_auto_exposure_scale); + ClassDB::bind_method(D_METHOD("get_auto_exposure_scale"), &CameraAttributes::get_auto_exposure_scale); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_auto_exposure_enabled", "is_auto_exposure_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_scale", "get_auto_exposure_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_speed", "get_auto_exposure_speed"); +} + +CameraAttributes::CameraAttributes() { + camera_attributes = RS::get_singleton()->camera_attributes_create(); +} + +CameraAttributes::~CameraAttributes() { + RS::get_singleton()->free(camera_attributes); +} + +////////////////////////////////////////////////////// +/* CameraAttributesPractical */ + +void CameraAttributesPractical::set_dof_blur_far_enabled(bool p_enabled) { + dof_blur_far_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_far_enabled() const { + return dof_blur_far_enabled; +} + +void CameraAttributesPractical::set_dof_blur_far_distance(float p_distance) { + dof_blur_far_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_distance() const { + return dof_blur_far_distance; +} + +void CameraAttributesPractical::set_dof_blur_far_transition(float p_distance) { + dof_blur_far_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_transition() const { + return dof_blur_far_transition; +} + +void CameraAttributesPractical::set_dof_blur_near_enabled(bool p_enabled) { + dof_blur_near_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_near_enabled() const { + return dof_blur_near_enabled; +} + +void CameraAttributesPractical::set_dof_blur_near_distance(float p_distance) { + dof_blur_near_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_distance() const { + return dof_blur_near_distance; +} + +void CameraAttributesPractical::set_dof_blur_near_transition(float p_distance) { + dof_blur_near_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_transition() const { + return dof_blur_near_transition; +} + +void CameraAttributesPractical::set_dof_blur_amount(float p_amount) { + dof_blur_amount = p_amount; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_amount() const { + return dof_blur_amount; +} + +void CameraAttributesPractical::_update_dof_blur() { + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + dof_blur_far_enabled, + dof_blur_far_distance, + dof_blur_far_transition, + dof_blur_near_enabled, + dof_blur_near_distance, + dof_blur_near_transition, + dof_blur_amount); +} + +float CameraAttributesPractical::calculate_exposure_normalization() const { + return exposure_sensitivity / 3072007.0; // Matches exposure normalization for default CameraAttributesPhysical at ISO 100. +} + +void CameraAttributesPractical::set_auto_exposure_min_sensitivity(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_min_sensitivity() const { + return auto_exposure_min; +} + +void CameraAttributesPractical::set_auto_exposure_max_sensitivity(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_max_sensitivity() const { + return auto_exposure_max; +} + +void CameraAttributesPractical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + auto_exposure_min * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_max * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPractical::_validate_property(PropertyInfo &p_property) const { + if ((!dof_blur_far_enabled && (p_property.name == "dof_blur_far_distance" || p_property.name == "dof_blur_far_transition")) || + (!dof_blur_near_enabled && (p_property.name == "dof_blur_near_distance" || p_property.name == "dof_blur_near_transition"))) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } +} + +void CameraAttributesPractical::_bind_methods() { + // DOF blur + + ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraAttributesPractical::is_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraAttributesPractical::set_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraAttributesPractical::get_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraAttributesPractical::set_dof_blur_far_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraAttributesPractical::get_dof_blur_far_transition); + + ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraAttributesPractical::is_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraAttributesPractical::set_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraAttributesPractical::get_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraAttributesPractical::set_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraAttributesPractical::get_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraAttributesPractical::set_dof_blur_amount); + ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraAttributesPractical::get_dof_blur_amount); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_sensitivity", "max_sensitivity"), &CameraAttributesPractical::set_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_sensitivity"), &CameraAttributesPractical::get_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_sensitivity", "min_sensitivity"), &CameraAttributesPractical::set_auto_exposure_min_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_sensitivity"), &CameraAttributesPractical::get_auto_exposure_min_sensitivity); + + ADD_GROUP("DOF Blur", "dof_blur_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_sensitivity", PROPERTY_HINT_RANGE, "0,1600,0.01,or_greater,suffic:ISO"), "set_auto_exposure_min_sensitivity", "get_auto_exposure_min_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_sensitivity", PROPERTY_HINT_RANGE, "0,64000,0.1,or_greater,suffic:ISO"), "set_auto_exposure_max_sensitivity", "get_auto_exposure_max_sensitivity"); +} + +CameraAttributesPractical::CameraAttributesPractical() { + _update_dof_blur(); + _update_exposure(); + set_auto_exposure_min_sensitivity(0.0); + set_auto_exposure_max_sensitivity(800.0); + notify_property_list_changed(); +} + +CameraAttributesPractical::~CameraAttributesPractical() { +} + +////////////////////////////////////////////////////// +/* CameraAttributesPhysical */ + +void CameraAttributesPhysical::set_aperture(float p_aperture) { + exposure_aperture = p_aperture; + _update_exposure(); + _update_frustum(); +} + +float CameraAttributesPhysical::get_aperture() const { + return exposure_aperture; +} + +void CameraAttributesPhysical::set_shutter_speed(float p_shutter_speed) { + exposure_shutter_speed = p_shutter_speed; + _update_exposure(); +} + +float CameraAttributesPhysical::get_shutter_speed() const { + return exposure_shutter_speed; +} + +void CameraAttributesPhysical::set_focal_length(float p_focal_length) { + frustum_focal_length = p_focal_length; + _update_frustum(); + emit_changed(); +} + +float CameraAttributesPhysical::get_focal_length() const { + return frustum_focal_length; +} + +void CameraAttributesPhysical::set_focus_distance(float p_focus_distance) { + frustum_focus_distance = p_focus_distance; + _update_frustum(); +} + +float CameraAttributesPhysical::get_focus_distance() const { + return frustum_focus_distance; +} + +void CameraAttributesPhysical::set_near(real_t p_near) { + frustum_near = p_near; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_near() const { + return frustum_near; +} + +void CameraAttributesPhysical::set_far(real_t p_far) { + frustum_far = p_far; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_far() const { + return frustum_far; +} + +real_t CameraAttributesPhysical::get_fov() const { + return frustum_fov; +} + +void CameraAttributesPhysical::_update_frustum() { + //https://en.wikipedia.org/wiki/Circle_of_confusion#Circle_of_confusion_diameter_limit_based_on_d/1500 + Vector2i sensor_size = Vector2i(36, 24); // Matches high-end DSLR, could be made variable if there is demand. + float CoC = sensor_size.length() / 1500.0; + + frustum_fov = Math::rad_to_deg(2 * atan(sensor_size.height / (2 * frustum_focal_length))); + + // Based on https://en.wikipedia.org/wiki/Depth_of_field. + float u = MAX(frustum_focus_distance * 1000.0, frustum_focal_length + 1.0); // Focus distance expressed in mm and clamped to at least 1 mm away from lens. + float hyperfocal_length = frustum_focal_length + ((frustum_focal_length * frustum_focal_length) / (exposure_aperture * CoC)); + + // This computes the start and end of the depth of field. Anything between these two points has a Circle of Confusino so small + // that it is not picked up by the camera sensors. + // To be properly physically-based, we would run the DoF shader at all depths. To be efficient, we are only running it where the CoC + // will be visible, this introduces some value shifts in the near field that we have to compensate for below. + float near = ((hyperfocal_length * u) / (hyperfocal_length + (u - frustum_focal_length))) / 1000.0; // In meters. + float far = ((hyperfocal_length * u) / (hyperfocal_length - (u - frustum_focal_length))) / 1000.0; // In meters. + float scale = (frustum_focal_length / (u - frustum_focal_length)) * (frustum_focal_length / exposure_aperture); + + bool use_far = (far < frustum_far) && (far > 0.0); + bool use_near = near > frustum_near; + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + use_far, + u / 1000.0, // Focus distance clampd to focal length expressed in meters. + -1.0, // Negative to tell Bokeh effect to use physically-based scaling. + use_near, + u / 1000.0, + -1.0, + scale / 5.0); // Arbitrary scaling to get close to how much blur there should be. +} + +float CameraAttributesPhysical::calculate_exposure_normalization() const { + const float e = (exposure_aperture * exposure_aperture) * exposure_shutter_speed * (100.0 / exposure_sensitivity); + return 1.0 / (e * 1.2); +} + +void CameraAttributesPhysical::set_auto_exposure_min_exposure_value(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_min_exposure_value() const { + return auto_exposure_min; +} + +void CameraAttributesPhysical::set_auto_exposure_max_exposure_value(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_max_exposure_value() const { + return auto_exposure_max; +} + +void CameraAttributesPhysical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + pow(2.0, auto_exposure_min) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + pow(2.0, auto_exposure_max) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPhysical::_validate_property(PropertyInfo &property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (property.name == "exposure_aperture" || property.name == "exposure_shutter_speed")) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributesPhysical::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aperture", "aperture"), &CameraAttributesPhysical::set_aperture); + ClassDB::bind_method(D_METHOD("get_aperture"), &CameraAttributesPhysical::get_aperture); + ClassDB::bind_method(D_METHOD("set_shutter_speed", "shutter_speed"), &CameraAttributesPhysical::set_shutter_speed); + ClassDB::bind_method(D_METHOD("get_shutter_speed"), &CameraAttributesPhysical::get_shutter_speed); + + ClassDB::bind_method(D_METHOD("set_focal_length", "focal_length"), &CameraAttributesPhysical::set_focal_length); + ClassDB::bind_method(D_METHOD("get_focal_length"), &CameraAttributesPhysical::get_focal_length); + ClassDB::bind_method(D_METHOD("set_focus_distance", "focus_distance"), &CameraAttributesPhysical::set_focus_distance); + ClassDB::bind_method(D_METHOD("get_focus_distance"), &CameraAttributesPhysical::get_focus_distance); + ClassDB::bind_method(D_METHOD("set_near", "near"), &CameraAttributesPhysical::set_near); + ClassDB::bind_method(D_METHOD("get_near"), &CameraAttributesPhysical::get_near); + ClassDB::bind_method(D_METHOD("set_far", "far"), &CameraAttributesPhysical::set_far); + ClassDB::bind_method(D_METHOD("get_far"), &CameraAttributesPhysical::get_far); + ClassDB::bind_method(D_METHOD("get_fov"), &CameraAttributesPhysical::get_fov); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_exposure_value", "exposure_value_max"), &CameraAttributesPhysical::set_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_exposure_value", "exposure_value_min"), &CameraAttributesPhysical::set_auto_exposure_min_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_min_exposure_value); + + ADD_GROUP("Frustum", "frustum_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focus_distance", PROPERTY_HINT_RANGE, "0.01,4000.0,0.01,suffix:m"), "set_focus_distance", "get_focus_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focal_length", PROPERTY_HINT_RANGE, "1.0,800.0,0.01,exp,suffix:mm"), "set_focal_length", "get_focal_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_aperture", PROPERTY_HINT_RANGE, "0.5,64.0,0.01,exp,suffix:f-stop"), "set_aperture", "get_aperture"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_shutter_speed", PROPERTY_HINT_RANGE, "0.1,8000.0,0.001,suffix:1/s"), "set_shutter_speed", "get_shutter_speed"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_min_exposure_value", "get_auto_exposure_min_exposure_value"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_max_exposure_value", "get_auto_exposure_max_exposure_value"); +}; + +CameraAttributesPhysical::CameraAttributesPhysical() { + _update_exposure(); + _update_frustum(); + set_auto_exposure_min_exposure_value(-8); + set_auto_exposure_max_exposure_value(10); // Use a wide range by default to feel more like a real camera. + notify_property_list_changed(); +} + +CameraAttributesPhysical::~CameraAttributesPhysical() { +} diff --git a/scene/resources/camera_attributes.h b/scene/resources/camera_attributes.h new file mode 100644 index 0000000000..c4c783af29 --- /dev/null +++ b/scene/resources/camera_attributes.h @@ -0,0 +1,183 @@ +/*************************************************************************/ +/* camera_attributes.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CAMERA_ATTRIBUTES_H +#define CAMERA_ATTRIBUTES_H + +#include "core/io/resource.h" +#include "core/templates/rid.h" + +class CameraAttributes : public Resource { + GDCLASS(CameraAttributes, Resource); + +private: + RID camera_attributes; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + + float exposure_multiplier = 1.0; + float exposure_sensitivity = 100.0; // In ISO. + void _update_exposure(); + + bool auto_exposure_enabled = false; + float auto_exposure_min = 0.01; + float auto_exposure_max = 64.0; + float auto_exposure_speed = 0.5; + float auto_exposure_scale = 0.4; + virtual void _update_auto_exposure(){}; + +public: + virtual RID get_rid() const override; + virtual float calculate_exposure_normalization() const { return 1.0; } + + void set_exposure_multiplier(float p_multiplier); + float get_exposure_multiplier() const; + void set_exposure_sensitivity(float p_sensitivity); + float get_exposure_sensitivity() const; + + void set_auto_exposure_enabled(bool p_enabled); + bool is_auto_exposure_enabled() const; + void set_auto_exposure_speed(float p_auto_exposure_speed); + float get_auto_exposure_speed() const; + void set_auto_exposure_scale(float p_auto_exposure_scale); + float get_auto_exposure_scale() const; + + CameraAttributes(); + ~CameraAttributes(); +}; + +class CameraAttributesPractical : public CameraAttributes { + GDCLASS(CameraAttributesPractical, CameraAttributes); + +private: + // DOF blur + bool dof_blur_far_enabled = false; + float dof_blur_far_distance = 10.0; + float dof_blur_far_transition = 5.0; + + bool dof_blur_near_enabled = false; + float dof_blur_near_distance = 2.0; + float dof_blur_near_transition = 1.0; + + float dof_blur_amount = 0.1; + void _update_dof_blur(); + + virtual void _update_auto_exposure() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + +public: + // DOF blur + void set_dof_blur_far_enabled(bool p_enabled); + bool is_dof_blur_far_enabled() const; + void set_dof_blur_far_distance(float p_distance); + float get_dof_blur_far_distance() const; + void set_dof_blur_far_transition(float p_distance); + float get_dof_blur_far_transition() const; + + void set_dof_blur_near_enabled(bool p_enabled); + bool is_dof_blur_near_enabled() const; + void set_dof_blur_near_distance(float p_distance); + float get_dof_blur_near_distance() const; + void set_dof_blur_near_transition(float p_distance); + float get_dof_blur_near_transition() const; + void set_dof_blur_amount(float p_amount); + float get_dof_blur_amount() const; + + void set_auto_exposure_min_sensitivity(float p_min); + float get_auto_exposure_min_sensitivity() const; + void set_auto_exposure_max_sensitivity(float p_max); + float get_auto_exposure_max_sensitivity() const; + + virtual float calculate_exposure_normalization() const override; + + CameraAttributesPractical(); + ~CameraAttributesPractical(); +}; + +class CameraAttributesPhysical : public CameraAttributes { + GDCLASS(CameraAttributesPhysical, CameraAttributes); + +private: + // Exposure + float exposure_aperture = 16.0; // In f-stops; + float exposure_shutter_speed = 100.0; // In 1 / seconds; + + // Camera properties. + float frustum_focal_length = 35.0; // In millimeters. + float frustum_focus_distance = 10.0; // In Meters. + real_t frustum_near = 0.05; + real_t frustum_far = 4000.0; + real_t frustum_fov = 75.0; + void _update_frustum(); + + virtual void _update_auto_exposure() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_aperture(float p_aperture); + float get_aperture() const; + + void set_shutter_speed(float p_shutter_speed); + float get_shutter_speed() const; + + void set_focal_length(float p_focal_length); + float get_focal_length() const; + + void set_focus_distance(float p_focus_distance); + float get_focus_distance() const; + + void set_near(real_t p_near); + real_t get_near() const; + + void set_far(real_t p_far); + real_t get_far() const; + + real_t get_fov() const; + + void set_auto_exposure_min_exposure_value(float p_min); + float get_auto_exposure_min_exposure_value() const; + void set_auto_exposure_max_exposure_value(float p_max); + float get_auto_exposure_max_exposure_value() const; + + virtual float calculate_exposure_normalization() const override; + + CameraAttributesPhysical(); + ~CameraAttributesPhysical(); +}; + +#endif // CAMERA_ATTRIBUTES_H diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp deleted file mode 100644 index 0b11366591..0000000000 --- a/scene/resources/camera_effects.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*************************************************************************/ -/* camera_effects.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "camera_effects.h" - -#include "servers/rendering_server.h" - -RID CameraEffects::get_rid() const { - return camera_effects; -} - -// DOF blur - -void CameraEffects::set_dof_blur_far_enabled(bool p_enabled) { - dof_blur_far_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_far_enabled() const { - return dof_blur_far_enabled; -} - -void CameraEffects::set_dof_blur_far_distance(float p_distance) { - dof_blur_far_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_distance() const { - return dof_blur_far_distance; -} - -void CameraEffects::set_dof_blur_far_transition(float p_distance) { - dof_blur_far_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_transition() const { - return dof_blur_far_transition; -} - -void CameraEffects::set_dof_blur_near_enabled(bool p_enabled) { - dof_blur_near_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_near_enabled() const { - return dof_blur_near_enabled; -} - -void CameraEffects::set_dof_blur_near_distance(float p_distance) { - dof_blur_near_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_distance() const { - return dof_blur_near_distance; -} - -void CameraEffects::set_dof_blur_near_transition(float p_distance) { - dof_blur_near_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_transition() const { - return dof_blur_near_transition; -} - -void CameraEffects::set_dof_blur_amount(float p_amount) { - dof_blur_amount = p_amount; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_amount() const { - return dof_blur_amount; -} - -void CameraEffects::_update_dof_blur() { - RS::get_singleton()->camera_effects_set_dof_blur( - camera_effects, - dof_blur_far_enabled, - dof_blur_far_distance, - dof_blur_far_transition, - dof_blur_near_enabled, - dof_blur_near_distance, - dof_blur_near_transition, - dof_blur_amount); -} - -// Custom exposure - -void CameraEffects::set_override_exposure_enabled(bool p_enabled) { - override_exposure_enabled = p_enabled; - _update_override_exposure(); - notify_property_list_changed(); -} - -bool CameraEffects::is_override_exposure_enabled() const { - return override_exposure_enabled; -} - -void CameraEffects::set_override_exposure(float p_exposure) { - override_exposure = p_exposure; - _update_override_exposure(); -} - -float CameraEffects::get_override_exposure() const { - return override_exposure; -} - -void CameraEffects::_update_override_exposure() { - RS::get_singleton()->camera_effects_set_custom_exposure( - camera_effects, - override_exposure_enabled, - override_exposure); -} - -// Private methods, constructor and destructor - -void CameraEffects::_validate_property(PropertyInfo &p_property) const { - if ((!dof_blur_far_enabled && (p_property.name == "dof_blur_far_distance" || p_property.name == "dof_blur_far_transition")) || - (!dof_blur_near_enabled && (p_property.name == "dof_blur_near_distance" || p_property.name == "dof_blur_near_transition")) || - (!override_exposure_enabled && p_property.name == "override_exposure")) { - p_property.usage = PROPERTY_USAGE_NO_EDITOR; - } -} - -void CameraEffects::_bind_methods() { - // DOF blur - - ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraEffects::set_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraEffects::is_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraEffects::set_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraEffects::get_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraEffects::set_dof_blur_far_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraEffects::get_dof_blur_far_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraEffects::set_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraEffects::is_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraEffects::set_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraEffects::get_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraEffects::set_dof_blur_near_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraEffects::get_dof_blur_near_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraEffects::set_dof_blur_amount); - ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraEffects::get_dof_blur_amount); - - ADD_GROUP("DOF Blur", "dof_blur_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); - - // Override exposure - - ClassDB::bind_method(D_METHOD("set_override_exposure_enabled", "enabled"), &CameraEffects::set_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_override_exposure_enabled"), &CameraEffects::is_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_override_exposure", "exposure"), &CameraEffects::set_override_exposure); - ClassDB::bind_method(D_METHOD("get_override_exposure"), &CameraEffects::get_override_exposure); - - ADD_GROUP("Override Exposure", "override_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_exposure_enabled"), "set_override_exposure_enabled", "is_override_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "override_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_override_exposure", "get_override_exposure"); -} - -CameraEffects::CameraEffects() { - camera_effects = RS::get_singleton()->camera_effects_create(); - - _update_dof_blur(); - _update_override_exposure(); -} - -CameraEffects::~CameraEffects() { - RS::get_singleton()->free(camera_effects); -} diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 73ad1ceff7..208e28f17a 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -609,11 +609,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Dialogs - theme->set_constant("margin", "Dialogs", 8 * scale); - theme->set_constant("button_margin", "Dialogs", 32 * scale); - - // AcceptDialog - + // AcceptDialog is currently the base dialog, so this defines styles for all extending nodes. + theme->set_constant("margin", "AcceptDialog", 8 * scale); + theme->set_constant("button_margin", "AcceptDialog", 32 * scale); theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 0, 0, 0, 0)); // File Dialog @@ -998,9 +996,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Containers + theme->set_icon("h_grabber", "SplitContainer", icons["hsplitter"]); + theme->set_icon("v_grabber", "SplitContainer", icons["vsplitter"]); theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]); theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]); + theme->set_constant("separation", "BoxContainer", 4 * scale); theme->set_constant("separation", "HBoxContainer", 4 * scale); theme->set_constant("separation", "VBoxContainer", 4 * scale); theme->set_constant("margin_left", "MarginContainer", 0 * scale); @@ -1009,10 +1010,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("margin_bottom", "MarginContainer", 0 * scale); theme->set_constant("h_separation", "GridContainer", 4 * scale); theme->set_constant("v_separation", "GridContainer", 4 * scale); + theme->set_constant("separation", "SplitContainer", 12 * scale); theme->set_constant("separation", "HSplitContainer", 12 * scale); theme->set_constant("separation", "VSplitContainer", 12 * scale); + theme->set_constant("autohide", "SplitContainer", 1 * scale); theme->set_constant("autohide", "HSplitContainer", 1 * scale); theme->set_constant("autohide", "VSplitContainer", 1 * scale); + theme->set_constant("h_separation", "FlowContainer", 4 * scale); + theme->set_constant("v_separation", "FlowContainer", 4 * scale); theme->set_constant("h_separation", "HFlowContainer", 4 * scale); theme->set_constant("v_separation", "HFlowContainer", 4 * scale); theme->set_constant("h_separation", "VFlowContainer", 4 * scale); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index f7a7818b3b..18df59a7aa 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -94,13 +94,30 @@ Color Environment::get_bg_color() const { return bg_color; } -void Environment::set_bg_energy(float p_energy) { - bg_energy = p_energy; - RS::get_singleton()->environment_set_bg_energy(environment, p_energy); +void Environment::set_bg_energy_multiplier(float p_multiplier) { + bg_energy_multiplier = p_multiplier; + _update_bg_energy(); } -float Environment::get_bg_energy() const { - return bg_energy; +float Environment::get_bg_energy_multiplier() const { + return bg_energy_multiplier; +} + +void Environment::set_bg_intensity(float p_exposure_value) { + bg_intensity = p_exposure_value; + _update_bg_energy(); +} + +float Environment::get_bg_intensity() const { + return bg_intensity; +} + +void Environment::_update_bg_energy() { + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, bg_intensity); + } else { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, 1.0); + } } void Environment::set_canvas_max_layer(int p_max_layer) { @@ -214,63 +231,12 @@ float Environment::get_tonemap_white() const { return tonemap_white; } -void Environment::set_tonemap_auto_exposure_enabled(bool p_enabled) { - tonemap_auto_exposure_enabled = p_enabled; - _update_tonemap(); - notify_property_list_changed(); -} - -bool Environment::is_tonemap_auto_exposure_enabled() const { - return tonemap_auto_exposure_enabled; -} - -void Environment::set_tonemap_auto_exposure_min(float p_auto_exposure_min) { - tonemap_auto_exposure_min = p_auto_exposure_min; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_min() const { - return tonemap_auto_exposure_min; -} - -void Environment::set_tonemap_auto_exposure_max(float p_auto_exposure_max) { - tonemap_auto_exposure_max = p_auto_exposure_max; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_max() const { - return tonemap_auto_exposure_max; -} - -void Environment::set_tonemap_auto_exposure_speed(float p_auto_exposure_speed) { - tonemap_auto_exposure_speed = p_auto_exposure_speed; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_speed() const { - return tonemap_auto_exposure_speed; -} - -void Environment::set_tonemap_auto_exposure_grey(float p_auto_exposure_grey) { - tonemap_auto_exposure_grey = p_auto_exposure_grey; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_grey() const { - return tonemap_auto_exposure_grey; -} - void Environment::_update_tonemap() { RS::get_singleton()->environment_set_tonemap( environment, RS::EnvironmentToneMapper(tone_mapper), tonemap_exposure, - tonemap_white, - tonemap_auto_exposure_enabled, - tonemap_auto_exposure_min, - tonemap_auto_exposure_max, - tonemap_auto_exposure_speed, - tonemap_auto_exposure_grey); + tonemap_white); } // SSR @@ -852,6 +818,15 @@ float Environment::get_fog_aerial_perspective() const { return fog_aerial_perspective; } +void Environment::set_fog_sky_affect(float p_sky_affect) { + fog_sky_affect = p_sky_affect; + _update_fog(); +} + +float Environment::get_fog_sky_affect() const { + return fog_sky_affect; +} + void Environment::_update_fog() { RS::get_singleton()->environment_set_fog( environment, @@ -862,13 +837,28 @@ void Environment::_update_fog() { fog_density, fog_height, fog_height_density, - fog_aerial_perspective); + fog_aerial_perspective, + fog_sky_affect); } // Volumetric Fog void Environment::_update_volumetric_fog() { - RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_albedo, volumetric_fog_emission, volumetric_fog_emission_energy, volumetric_fog_anisotropy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount, volumetric_fog_ambient_inject); + RS::get_singleton()->environment_set_volumetric_fog( + environment, + volumetric_fog_enabled, + volumetric_fog_density, + volumetric_fog_albedo, + volumetric_fog_emission, + volumetric_fog_emission_energy, + volumetric_fog_anisotropy, + volumetric_fog_length, + volumetric_fog_detail_spread, + volumetric_fog_gi_inject, + volumetric_fog_temporal_reproject, + volumetric_fog_temporal_reproject_amount, + volumetric_fog_ambient_inject, + volumetric_fog_sky_affect); } void Environment::set_volumetric_fog_enabled(bool p_enable) { @@ -946,6 +936,15 @@ float Environment::get_volumetric_fog_ambient_inject() const { return volumetric_fog_ambient_inject; } +void Environment::set_volumetric_fog_sky_affect(float p_sky_affect) { + volumetric_fog_sky_affect = p_sky_affect; + _update_volumetric_fog(); +} + +float Environment::get_volumetric_fog_sky_affect() const { + return volumetric_fog_sky_affect; +} + void Environment::set_volumetric_fog_temporal_reprojection_enabled(bool p_enable) { volumetric_fog_temporal_reproject = p_enable; _update_volumetric_fog(); @@ -1080,10 +1079,13 @@ void Environment::_validate_property(PropertyInfo &p_property) const { } } + if (p_property.name == "background_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + static const char *hide_prefixes[] = { "fog_", "volumetric_fog_", - "auto_exposure_", "ssr_", "ssao_", "ssil_", @@ -1095,7 +1097,6 @@ void Environment::_validate_property(PropertyInfo &p_property) const { }; static const char *high_end_prefixes[] = { - "auto_exposure_", "ssr_", "ssao_", nullptr @@ -1162,8 +1163,10 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sky_rotation"), &Environment::get_sky_rotation); ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &Environment::set_bg_color); ClassDB::bind_method(D_METHOD("get_bg_color"), &Environment::get_bg_color); - ClassDB::bind_method(D_METHOD("set_bg_energy", "energy"), &Environment::set_bg_energy); - ClassDB::bind_method(D_METHOD("get_bg_energy"), &Environment::get_bg_energy); + ClassDB::bind_method(D_METHOD("set_bg_energy_multiplier", "energy"), &Environment::set_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_bg_energy_multiplier"), &Environment::get_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("set_bg_intensity", "energy"), &Environment::set_bg_intensity); + ClassDB::bind_method(D_METHOD("get_bg_intensity"), &Environment::get_bg_intensity); ClassDB::bind_method(D_METHOD("set_canvas_max_layer", "layer"), &Environment::set_canvas_max_layer); ClassDB::bind_method(D_METHOD("get_canvas_max_layer"), &Environment::get_canvas_max_layer); ClassDB::bind_method(D_METHOD("set_camera_feed_id", "id"), &Environment::set_camera_feed_id); @@ -1172,7 +1175,9 @@ void Environment::_bind_methods() { ADD_GROUP("Background", "background_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy", "get_bg_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy_multiplier", "get_bg_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_intensity", PROPERTY_HINT_RANGE, "0,100000,0.01,suffix:nt"), "set_bg_intensity", "get_bg_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "background_canvas_max_layer", PROPERTY_HINT_RANGE, "-1000,1000,1"), "set_canvas_max_layer", "get_canvas_max_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_camera_feed_id", PROPERTY_HINT_RANGE, "1,10,1"), "set_camera_feed_id", "get_camera_feed_id"); @@ -1211,27 +1216,11 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tonemap_exposure"), &Environment::get_tonemap_exposure); ClassDB::bind_method(D_METHOD("set_tonemap_white", "white"), &Environment::set_tonemap_white); ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_enabled", "enabled"), &Environment::set_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_tonemap_auto_exposure_enabled"), &Environment::is_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_max", "exposure_max"), &Environment::set_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_max"), &Environment::get_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_min", "exposure_min"), &Environment::set_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_min"), &Environment::get_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_speed", "exposure_speed"), &Environment::set_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_speed"), &Environment::get_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_grey", "exposure_grey"), &Environment::set_tonemap_auto_exposure_grey); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_grey"), &Environment::get_tonemap_auto_exposure_grey); ADD_GROUP("Tonemap", "tonemap_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); - ADD_GROUP("Auto Exposure", "auto_exposure_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_tonemap_auto_exposure_enabled", "is_tonemap_auto_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_grey", "get_tonemap_auto_exposure_grey"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_min", "get_tonemap_auto_exposure_min"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_max", "get_tonemap_auto_exposure_max"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_speed", "get_tonemap_auto_exposure_speed"); // SSR @@ -1419,6 +1408,9 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fog_aerial_perspective", "aerial_perspective"), &Environment::set_fog_aerial_perspective); ClassDB::bind_method(D_METHOD("get_fog_aerial_perspective"), &Environment::get_fog_aerial_perspective); + ClassDB::bind_method(D_METHOD("set_fog_sky_affect", "sky_affect"), &Environment::set_fog_sky_affect); + ClassDB::bind_method(D_METHOD("get_fog_sky_affect"), &Environment::get_fog_sky_affect); + ADD_GROUP("Fog", "fog_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color"); @@ -1427,6 +1419,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_fog_density", "get_fog_density"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_sky_affect", "get_fog_sky_affect"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater,suffix:m"), "set_fog_height", "get_fog_height"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density"); @@ -1450,6 +1443,8 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject); ClassDB::bind_method(D_METHOD("set_volumetric_fog_ambient_inject", "enabled"), &Environment::set_volumetric_fog_ambient_inject); ClassDB::bind_method(D_METHOD("get_volumetric_fog_ambient_inject"), &Environment::get_volumetric_fog_ambient_inject); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_sky_affect", "sky_affect"), &Environment::set_volumetric_fog_sky_affect); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_sky_affect"), &Environment::get_volumetric_fog_sky_affect); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_enabled", "enabled"), &Environment::set_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("is_volumetric_fog_temporal_reprojection_enabled"), &Environment::is_volumetric_fog_temporal_reprojection_enabled); ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_amount", "temporal_reprojection_amount"), &Environment::set_volumetric_fog_temporal_reprojection_amount); @@ -1466,6 +1461,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_volumetric_fog_sky_affect", "get_volumetric_fog_sky_affect"); ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.5,0.99,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount"); @@ -1549,6 +1545,7 @@ Environment::Environment() { _update_fog(); _update_adjustment(); _update_volumetric_fog(); + _update_bg_energy(); notify_property_list_changed(); } diff --git a/scene/resources/environment.h b/scene/resources/environment.h index d39cb1acd8..507a0cee39 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -92,9 +92,11 @@ private: float bg_sky_custom_fov = 0.0; Vector3 bg_sky_rotation; Color bg_color; - float bg_energy = 1.0; int bg_canvas_max_layer = 0; int bg_camera_feed_id = 1; + float bg_energy_multiplier = 1.0; + float bg_intensity = 30000.0; // Measured in nits or candela/m^2 + void _update_bg_energy(); // Ambient light Color ambient_color; @@ -108,11 +110,6 @@ private: ToneMapper tone_mapper = TONE_MAPPER_LINEAR; float tonemap_exposure = 1.0; float tonemap_white = 1.0; - bool tonemap_auto_exposure_enabled = false; - float tonemap_auto_exposure_min = 0.05; - float tonemap_auto_exposure_max = 8.0; - float tonemap_auto_exposure_speed = 0.5; - float tonemap_auto_exposure_grey = 0.4; void _update_tonemap(); // SSR @@ -182,6 +179,7 @@ private: float fog_height = 0.0; float fog_height_density = 0.0; //can be negative to invert effect float fog_aerial_perspective = 0.0; + float fog_sky_affect = 1.0; void _update_fog(); @@ -196,6 +194,7 @@ private: float volumetric_fog_detail_spread = 2.0; float volumetric_fog_gi_inject = 1.0; float volumetric_fog_ambient_inject = 0.0; + float volumetric_fog_sky_affect = 1.0; bool volumetric_fog_temporal_reproject = true; float volumetric_fog_temporal_reproject_amount = 0.9; void _update_volumetric_fog(); @@ -231,8 +230,10 @@ public: Vector3 get_sky_rotation() const; void set_bg_color(const Color &p_color); Color get_bg_color() const; - void set_bg_energy(float p_energy); - float get_bg_energy() const; + void set_bg_energy_multiplier(float p_energy); + float get_bg_energy_multiplier() const; + void set_bg_intensity(float p_energy); + float get_bg_intensity() const; void set_canvas_max_layer(int p_max_layer); int get_canvas_max_layer() const; void set_camera_feed_id(int p_id); @@ -257,16 +258,6 @@ public: float get_tonemap_exposure() const; void set_tonemap_white(float p_white); float get_tonemap_white() const; - void set_tonemap_auto_exposure_enabled(bool p_enabled); - bool is_tonemap_auto_exposure_enabled() const; - void set_tonemap_auto_exposure_min(float p_auto_exposure_min); - float get_tonemap_auto_exposure_min() const; - void set_tonemap_auto_exposure_max(float p_auto_exposure_max); - float get_tonemap_auto_exposure_max() const; - void set_tonemap_auto_exposure_speed(float p_auto_exposure_speed); - float get_tonemap_auto_exposure_speed() const; - void set_tonemap_auto_exposure_grey(float p_auto_exposure_grey); - float get_tonemap_auto_exposure_grey() const; // SSR void set_ssr_enabled(bool p_enabled); @@ -385,6 +376,8 @@ public: float get_fog_height_density() const; void set_fog_aerial_perspective(float p_aerial_perspective); float get_fog_aerial_perspective() const; + void set_fog_sky_affect(float p_sky_affect); + float get_fog_sky_affect() const; // Volumetric Fog void set_volumetric_fog_enabled(bool p_enable); @@ -407,6 +400,8 @@ public: float get_volumetric_fog_gi_inject() const; void set_volumetric_fog_ambient_inject(float p_ambient_inject); float get_volumetric_fog_ambient_inject() const; + void set_volumetric_fog_sky_affect(float p_sky_affect); + float get_volumetric_fog_sky_affect() const; void set_volumetric_fog_temporal_reprojection_enabled(bool p_enable); bool is_volumetric_fog_temporal_reprojection_enabled() const; void set_volumetric_fog_temporal_reprojection_amount(float p_amount); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 80a9b3ca21..448ff74a53 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -31,6 +31,8 @@ #include "material.h" #include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/error/error_macros.h" #include "core/version.h" #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -1506,13 +1508,27 @@ Color BaseMaterial3D::get_emission() const { return emission; } -void BaseMaterial3D::set_emission_energy(float p_emission_energy) { - emission_energy = p_emission_energy; - RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy); +void BaseMaterial3D::set_emission_energy_multiplier(float p_emission_energy_multiplier) { + emission_energy_multiplier = p_emission_energy_multiplier; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity); + } else { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier); + } +} + +float BaseMaterial3D::get_emission_energy_multiplier() const { + return emission_energy_multiplier; +} + +void BaseMaterial3D::set_emission_intensity(float p_emission_intensity) { + ERR_FAIL_COND_EDMSG(!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"), "Cannot set material emission intensity when Physical Light Units disabled."); + emission_intensity = p_emission_intensity; + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, emission_energy_multiplier * emission_intensity); } -float BaseMaterial3D::get_emission_energy() const { - return emission_energy; +float BaseMaterial3D::get_emission_intensity() const { + return emission_intensity; } void BaseMaterial3D::set_normal_scale(float p_normal_scale) { @@ -1886,6 +1902,10 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { _validate_high_end("subsurf_scatter", p_property); _validate_high_end("heightmap", p_property); + if (p_property.name == "emission_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NONE; + } + if (p_property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -2465,8 +2485,11 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission", "emission"), &BaseMaterial3D::set_emission); ClassDB::bind_method(D_METHOD("get_emission"), &BaseMaterial3D::get_emission); - ClassDB::bind_method(D_METHOD("set_emission_energy", "emission_energy"), &BaseMaterial3D::set_emission_energy); - ClassDB::bind_method(D_METHOD("get_emission_energy"), &BaseMaterial3D::get_emission_energy); + ClassDB::bind_method(D_METHOD("set_emission_energy_multiplier", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_emission_energy_multiplier"), &BaseMaterial3D::get_emission_energy_multiplier); + + ClassDB::bind_method(D_METHOD("set_emission_intensity", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_intensity); + ClassDB::bind_method(D_METHOD("get_emission_intensity"), &BaseMaterial3D::get_emission_intensity); ClassDB::bind_method(D_METHOD("set_normal_scale", "normal_scale"), &BaseMaterial3D::set_normal_scale); ClassDB::bind_method(D_METHOD("get_normal_scale"), &BaseMaterial3D::get_normal_scale); @@ -2683,7 +2706,9 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Emission", "emission_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_enabled"), "set_feature", "get_feature", FEATURE_EMISSION); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy_multiplier", "get_emission_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_intensity", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:nt"), "set_emission_intensity", "get_emission_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION); @@ -2945,7 +2970,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_roughness(1.0); set_metallic(0.0); set_emission(Color(0, 0, 0)); - set_emission_energy(1.0); + set_emission_energy_multiplier(1.0); set_normal_scale(1); set_rim(1.0); set_rim_tint(0.5); @@ -3098,6 +3123,8 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) { "depth_flip_binormal", "heightmap_flip_binormal" }, { "depth_texture", "heightmap_texture" }, + { "emission_energy", "emission_energy_multiplier" }, + { nullptr, nullptr }, }; diff --git a/scene/resources/material.h b/scene/resources/material.h index 63c116fa66..6c81293ee3 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -467,7 +467,8 @@ private: float metallic = 0.0f; float roughness = 0.0f; Color emission; - float emission_energy = 0.0f; + float emission_energy_multiplier = 1.0f; + float emission_intensity = 1000.0f; // In nits, equivalent to indoor lighting. float normal_scale = 0.0f; float rim = 0.0f; float rim_tint = 0.0f; @@ -573,8 +574,11 @@ public: void set_emission(const Color &p_emission); Color get_emission() const; - void set_emission_energy(float p_emission_energy); - float get_emission_energy() const; + void set_emission_energy_multiplier(float p_emission_energy_multiplier); + float get_emission_energy_multiplier() const; + + void set_emission_intensity(float p_emission_intensity); + float get_emission_intensity() const; void set_normal_scale(float p_normal_scale); float get_normal_scale() const; diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index a64b262cb4..6ebf96db8e 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -41,7 +41,7 @@ void RectangleShape2D::_update_shape() { bool RectangleShape2D::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "extents") { // Compatibility with Godot 3.x. // Convert to `size`, twice as big. - set_size((Vector2)p_value * 2); + set_size((Size2)p_value * 2); return true; } return false; @@ -57,13 +57,13 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const } #endif // DISABLE_DEPRECATED -void RectangleShape2D::set_size(const Vector2 &p_size) { +void RectangleShape2D::set_size(const Size2 &p_size) { ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative."); size = p_size; _update_shape(); } -Vector2 RectangleShape2D::get_size() const { +Size2 RectangleShape2D::get_size() const { return size; } @@ -106,6 +106,6 @@ void RectangleShape2D::_bind_methods() { RectangleShape2D::RectangleShape2D() : Shape2D(PhysicsServer2D::get_singleton()->rectangle_shape_create()) { - size = Vector2(20, 20); + size = Size2(20, 20); _update_shape(); } diff --git a/scene/resources/rectangle_shape_2d.h b/scene/resources/rectangle_shape_2d.h index fa85aef428..9cc7b999be 100644 --- a/scene/resources/rectangle_shape_2d.h +++ b/scene/resources/rectangle_shape_2d.h @@ -36,7 +36,7 @@ class RectangleShape2D : public Shape2D { GDCLASS(RectangleShape2D, Shape2D); - Vector2 size; + Size2 size; void _update_shape(); protected: @@ -47,8 +47,8 @@ protected: #endif // DISABLE_DEPRECATED public: - void set_size(const Vector2 &p_size); - Vector2 get_size() const; + void set_size(const Size2 &p_size); + Size2 get_size() const; virtual void draw(const RID &p_to_rid, const Color &p_color) override; virtual Rect2 get_rect() const override; diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 737c50e570..fc999d5fcb 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -30,6 +30,7 @@ #include "sky_material.h" +#include "core/config/project_settings.h" #include "core/version.h" Mutex ProceduralSkyMaterial::shader_mutex; @@ -62,13 +63,13 @@ float ProceduralSkyMaterial::get_sky_curve() const { return sky_curve; } -void ProceduralSkyMaterial::set_sky_energy(float p_energy) { - sky_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy); +void ProceduralSkyMaterial::set_sky_energy_multiplier(float p_multiplier) { + sky_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy_multiplier); } -float ProceduralSkyMaterial::get_sky_energy() const { - return sky_energy; +float ProceduralSkyMaterial::get_sky_energy_multiplier() const { + return sky_energy_multiplier; } void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) { @@ -117,13 +118,13 @@ float ProceduralSkyMaterial::get_ground_curve() const { return ground_curve; } -void ProceduralSkyMaterial::set_ground_energy(float p_energy) { - ground_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy); +void ProceduralSkyMaterial::set_ground_energy_multiplier(float p_multiplier) { + ground_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy_multiplier); } -float ProceduralSkyMaterial::get_ground_energy() const { - return ground_energy; +float ProceduralSkyMaterial::get_ground_energy_multiplier() const { + return ground_energy_multiplier; } void ProceduralSkyMaterial::set_sun_angle_max(float p_angle) { @@ -171,6 +172,12 @@ RID ProceduralSkyMaterial::get_shader_rid() const { return shader; } +void ProceduralSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if ((p_property.name == "sky_luminance" || p_property.name == "ground_luminance") && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_top_color", "color"), &ProceduralSkyMaterial::set_sky_top_color); ClassDB::bind_method(D_METHOD("get_sky_top_color"), &ProceduralSkyMaterial::get_sky_top_color); @@ -181,8 +188,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_curve", "curve"), &ProceduralSkyMaterial::set_sky_curve); ClassDB::bind_method(D_METHOD("get_sky_curve"), &ProceduralSkyMaterial::get_sky_curve); - ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSkyMaterial::set_sky_energy); - ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSkyMaterial::get_sky_energy); + ClassDB::bind_method(D_METHOD("set_sky_energy_multiplier", "multiplier"), &ProceduralSkyMaterial::set_sky_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_sky_energy_multiplier"), &ProceduralSkyMaterial::get_sky_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sky_cover", "sky_cover"), &ProceduralSkyMaterial::set_sky_cover); ClassDB::bind_method(D_METHOD("get_sky_cover"), &ProceduralSkyMaterial::get_sky_cover); @@ -199,8 +206,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_curve", "curve"), &ProceduralSkyMaterial::set_ground_curve); ClassDB::bind_method(D_METHOD("get_ground_curve"), &ProceduralSkyMaterial::get_ground_curve); - ClassDB::bind_method(D_METHOD("set_ground_energy", "energy"), &ProceduralSkyMaterial::set_ground_energy); - ClassDB::bind_method(D_METHOD("get_ground_energy"), &ProceduralSkyMaterial::get_ground_energy); + ClassDB::bind_method(D_METHOD("set_ground_energy_multiplier", "energy"), &ProceduralSkyMaterial::set_ground_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_ground_energy_multiplier"), &ProceduralSkyMaterial::get_ground_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sun_angle_max", "degrees"), &ProceduralSkyMaterial::set_sun_angle_max); ClassDB::bind_method(D_METHOD("get_sun_angle_max"), &ProceduralSkyMaterial::get_sun_angle_max); @@ -215,7 +222,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy_multiplier", "get_sky_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_cover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_sky_cover", "get_sky_cover"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_cover_modulate"), "set_sky_cover_modulate", "get_sky_cover_modulate"); @@ -223,7 +230,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy_multiplier", "get_ground_energy_multiplier"); ADD_GROUP("Sun", "sun_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01,degrees"), "set_sun_angle_max", "get_sun_angle_max"); @@ -253,7 +260,7 @@ shader_type sky; uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); uniform float sky_curve : hint_range(0, 1) = 0.15; -uniform float sky_energy = 1.0; +uniform float sky_energy = 1.0; // In Lux. uniform sampler2D sky_cover : source_color, hint_default_black; uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); @@ -338,13 +345,13 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sky_top_color(Color(0.385, 0.454, 0.55)); set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_sky_curve(0.15); - set_sky_energy(1.0); + set_sky_energy_multiplier(1.0); set_sky_cover_modulate(Color(1, 1, 1)); set_ground_bottom_color(Color(0.2, 0.169, 0.133)); set_ground_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_ground_curve(0.02); - set_ground_energy(1.0); + set_ground_energy_multiplier(1.0); set_sun_angle_max(30.0); set_sun_curve(0.15); @@ -528,13 +535,13 @@ Color PhysicalSkyMaterial::get_ground_color() const { return ground_color; } -void PhysicalSkyMaterial::set_exposure(float p_exposure) { - exposure = p_exposure; - RS::get_singleton()->material_set_param(_get_material(), "exposure", exposure); +void PhysicalSkyMaterial::set_energy_multiplier(float p_multiplier) { + energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "exposure", energy_multiplier); } -float PhysicalSkyMaterial::get_exposure() const { - return exposure; +float PhysicalSkyMaterial::get_energy_multiplier() const { + return energy_multiplier; } void PhysicalSkyMaterial::set_use_debanding(bool p_use_debanding) { @@ -574,6 +581,12 @@ RID PhysicalSkyMaterial::get_shader_rid() const { return shader; } +void PhysicalSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "exposure_value" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + Mutex PhysicalSkyMaterial::shader_mutex; RID PhysicalSkyMaterial::shader; @@ -602,8 +615,8 @@ void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_color", "color"), &PhysicalSkyMaterial::set_ground_color); ClassDB::bind_method(D_METHOD("get_ground_color"), &PhysicalSkyMaterial::get_ground_color); - ClassDB::bind_method(D_METHOD("set_exposure", "exposure"), &PhysicalSkyMaterial::set_exposure); - ClassDB::bind_method(D_METHOD("get_exposure"), &PhysicalSkyMaterial::get_exposure); + ClassDB::bind_method(D_METHOD("set_energy_multiplier", "multiplier"), &PhysicalSkyMaterial::set_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_energy_multiplier"), &PhysicalSkyMaterial::get_energy_multiplier); ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &PhysicalSkyMaterial::set_use_debanding); ClassDB::bind_method(D_METHOD("get_use_debanding"), &PhysicalSkyMaterial::get_use_debanding); @@ -623,7 +636,7 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy_multiplier", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_energy_multiplier", "get_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); } @@ -654,16 +667,13 @@ uniform vec4 mie_color : source_color = vec4(0.69, 0.729, 0.812, 1.0); uniform float turbidity : hint_range(0, 1000) = 10.0; uniform float sun_disk_scale : hint_range(0, 360) = 1.0; uniform vec4 ground_color : source_color = vec4(0.1, 0.07, 0.034, 1.0); -uniform float exposure : hint_range(0, 128) = 0.1; +uniform float exposure : hint_range(0, 128) = 1.0; uniform bool use_debanding = true; uniform sampler2D night_sky : source_color, hint_default_black; const vec3 UP = vec3( 0.0, 1.0, 0.0 ); -// Sun constants -const float SUN_ENERGY = 1000.0; - // Optical length at zenith for molecules. const float rayleigh_zenith_size = 8.4e3; const float mie_zenith_size = 1.25e3; @@ -683,7 +693,7 @@ vec3 interleaved_gradient_noise(vec2 pos) { void sky() { if (LIGHT0_ENABLED) { float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 ); - float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY; + float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * LIGHT0_ENERGY; float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0); // Rayleigh coefficients. @@ -721,10 +731,10 @@ void sky() { float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale); float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5); float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta); - vec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR; + vec3 L0 = (sun_energy * extinction) * sundisk * LIGHT0_COLOR; L0 += texture(night_sky, SKY_COORDS).xyz * extinction; - vec3 color = (Lin + L0) * 0.04; + vec3 color = Lin + L0; COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade)))); COLOR *= exposure; if (use_debanding) { @@ -732,7 +742,7 @@ void sky() { } } else { // There is no sun, so display night_sky and nothing else. - COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04; + COLOR = texture(night_sky, SKY_COORDS).xyz; COLOR *= exposure; } } @@ -751,7 +761,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { set_turbidity(10.0); set_sun_disk_scale(1.0); set_ground_color(Color(0.1, 0.07, 0.034)); - set_exposure(0.1); + set_energy_multiplier(1.0); set_use_debanding(true); } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 61999af3c4..b517fd806b 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -41,14 +41,14 @@ private: Color sky_top_color; Color sky_horizon_color; float sky_curve = 0.0f; - float sky_energy = 0.0f; + float sky_energy_multiplier = 0.0f; Ref<Texture2D> sky_cover; Color sky_cover_modulate; Color ground_bottom_color; Color ground_horizon_color; float ground_curve = 0.0f; - float ground_energy = 0.0f; + float ground_energy_multiplier = 0.0f; float sun_angle_max = 0.0f; float sun_curve = 0.0f; @@ -61,6 +61,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_sky_top_color(const Color &p_sky_top); @@ -72,8 +73,8 @@ public: void set_sky_curve(float p_curve); float get_sky_curve() const; - void set_sky_energy(float p_energy); - float get_sky_energy() const; + void set_sky_energy_multiplier(float p_multiplier); + float get_sky_energy_multiplier() const; void set_sky_cover(const Ref<Texture2D> &p_sky_cover); Ref<Texture2D> get_sky_cover() const; @@ -90,8 +91,8 @@ public: void set_ground_curve(float p_curve); float get_ground_curve() const; - void set_ground_energy(float p_energy); - float get_ground_energy() const; + void set_ground_energy_multiplier(float p_energy); + float get_ground_energy_multiplier() const; void set_sun_angle_max(float p_angle); float get_sun_angle_max() const; @@ -138,6 +139,9 @@ public: void set_filtering_enabled(bool p_enabled); bool is_filtering_enabled() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; virtual RID get_rid() const override; @@ -166,7 +170,7 @@ private: float turbidity = 0.0f; float sun_disk_scale = 0.0f; Color ground_color; - float exposure = 0.0f; + float energy_multiplier = 1.0f; bool use_debanding = true; Ref<Texture2D> night_sky; static void _update_shader(); @@ -174,6 +178,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_rayleigh_coefficient(float p_rayleigh); @@ -200,8 +205,11 @@ public: void set_ground_color(Color p_ground_color); Color get_ground_color() const; - void set_exposure(float p_exposure); - float get_exposure() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + + void set_exposure_value(float p_exposure); + float get_exposure_value() const; void set_use_debanding(bool p_use_debanding); bool get_use_debanding() const; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index ff5210f1b3..4b151eed12 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -687,7 +687,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); // Only enable antialiasing if it is actually needed. This improve performances // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. - const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased; + const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; const bool blend_on = blend_border && draw_border; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 43d3f329fa..7e9a2591e4 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -77,7 +77,7 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags); ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive"), "set_break_flags", "get_break_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive,Trim Spaces"), "set_break_flags", "get_break_flags"); ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags); ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index f8f6900976..028c131d0c 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -294,7 +294,7 @@ bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; @@ -561,7 +561,7 @@ bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; @@ -1017,7 +1017,7 @@ bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const { x = CLAMP(x, 0, aw); y = CLAMP(y, 0, ah); - return alpha_cache->get_bit(Point2(x, y)); + return alpha_cache->get_bit(x, y); } return true; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 376b53b75c..9138a82ba8 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -118,7 +118,7 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { pattern.erase(p_coords); if (p_update_size) { - size = Vector2i(); + size = Size2i(); for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { size = size.max(E.key + Vector2i(1, 1)); } @@ -157,11 +157,11 @@ TypedArray<Vector2i> TileMapPattern::get_used_cells() const { return a; } -Vector2i TileMapPattern::get_size() const { +Size2i TileMapPattern::get_size() const { return size; } -void TileMapPattern::set_size(const Vector2i &p_size) { +void TileMapPattern::set_size(const Size2i &p_size) { for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { Vector2i coords = E.key; if (p_size.x <= coords.x || p_size.y <= coords.y) { @@ -178,7 +178,7 @@ bool TileMapPattern::is_empty() const { }; void TileMapPattern::clear() { - size = Vector2i(); + size = Size2i(); pattern.clear(); emit_changed(); }; diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 4c0823cdf2..e156679711 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -117,7 +117,7 @@ union TileMapCell { class TileMapPattern : public Resource { GDCLASS(TileMapPattern, Resource); - Vector2i size; + Size2i size; HashMap<Vector2i, TileMapCell> pattern; void _set_tile_data(const Vector<int> &p_data); @@ -140,8 +140,8 @@ public: TypedArray<Vector2i> get_used_cells() const; - Vector2i get_size() const; - void set_size(const Vector2i &p_size); + Size2i get_size() const; + void set_size(const Size2i &p_size); bool is_empty() const; void clear(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 1a797547c8..70a73186bc 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -3855,11 +3855,11 @@ VisualShaderNodeParameter::VisualShaderNodeParameter() { ////////////// ResizeableBase -void VisualShaderNodeResizableBase::set_size(const Vector2 &p_size) { +void VisualShaderNodeResizableBase::set_size(const Size2 &p_size) { size = p_size; } -Vector2 VisualShaderNodeResizableBase::get_size() const { +Size2 VisualShaderNodeResizableBase::get_size() const { return size; } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 05f738dd5a..88e92f15cf 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -612,15 +612,15 @@ class VisualShaderNodeResizableBase : public VisualShaderNode { GDCLASS(VisualShaderNodeResizableBase, VisualShaderNode); protected: - Vector2 size = Size2(0, 0); + Size2 size = Size2(0, 0); bool allow_v_resize = true; protected: static void _bind_methods(); public: - void set_size(const Vector2 &p_size); - Vector2 get_size() const; + void set_size(const Size2 &p_size); + Size2 get_size() const; bool is_allow_v_resize() const; void set_allow_v_resize(bool p_enabled); diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 4dfbe5f079..75deb1e60b 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -85,6 +85,7 @@ World2D::World2D() { NavigationServer2D::get_singleton()->map_set_active(navigation_map, true); NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1)); NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1)); + NavigationServer2D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/2d/default_link_connection_radius", 4)); } World2D::~World2D() { diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index fb6dcd3d57..ae8c9a182f 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -33,6 +33,8 @@ #include "core/config/project_settings.h" #include "scene/3d/camera_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" @@ -98,17 +100,17 @@ Ref<Environment> World3D::get_fallback_environment() const { return fallback_environment; } -void World3D::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - camera_effects = p_camera_effects; - if (camera_effects.is_valid()) { - RS::get_singleton()->scenario_set_camera_effects(scenario, camera_effects->get_rid()); +void World3D::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; + if (camera_attributes.is_valid()) { + RS::get_singleton()->scenario_set_camera_attributes(scenario, camera_attributes->get_rid()); } else { - RS::get_singleton()->scenario_set_camera_effects(scenario, RID()); + RS::get_singleton()->scenario_set_camera_attributes(scenario, RID()); } } -Ref<CameraEffects> World3D::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> World3D::get_camera_attributes() const { + return camera_attributes; } PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { @@ -123,12 +125,12 @@ void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment); ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment); ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment); - ClassDB::bind_method(D_METHOD("set_camera_effects", "effects"), &World3D::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "attributes"), &World3D::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &World3D::get_camera_attributes); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space"); ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map"); ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_scenario"); @@ -151,6 +153,7 @@ World3D::World3D() { NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.25)); NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.25)); + NavigationServer3D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_DEF("navigation/3d/default_link_connection_radius", 1.0)); } World3D::~World3D() { diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 08bc050349..411b9aab37 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -32,11 +32,11 @@ #define WORLD_3D_H #include "core/io/resource.h" -#include "scene/resources/camera_effects.h" #include "scene/resources/environment.h" #include "servers/physics_server_3d.h" #include "servers/rendering_server.h" +class CameraAttributes; class Camera3D; class VisibleOnScreenNotifier3D; struct SpatialIndexer; @@ -51,7 +51,7 @@ private: Ref<Environment> environment; Ref<Environment> fallback_environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; HashSet<Camera3D *> cameras; @@ -74,8 +74,8 @@ public: void set_fallback_environment(const Ref<Environment> &p_environment); Ref<Environment> get_fallback_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; _FORCE_INLINE_ const HashSet<Camera3D *> &get_cameras() const { return cameras; } diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 22f42ca343..6f14e73f34 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -44,37 +44,37 @@ DisplayServer::DisplayServerCreate DisplayServer::server_create_functions[Displa int DisplayServer::server_create_count = 1; -int DisplayServer::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } -int DisplayServer::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { +int DisplayServer::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { WARN_PRINT("Global menus not supported by this display server."); return -1; } @@ -103,6 +103,10 @@ void DisplayServer::global_menu_set_item_callback(const String &p_menu_root, int WARN_PRINT("Global menus not supported by this display server."); } +void DisplayServer::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { + WARN_PRINT("Global menus not supported by this display server."); +} + bool DisplayServer::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const { WARN_PRINT("Global menus not supported by this display server."); return false; @@ -123,6 +127,11 @@ Callable DisplayServer::global_menu_get_item_callback(const String &p_menu_root, return Callable(); } +Callable DisplayServer::global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const { + WARN_PRINT("Global menus not supported by this display server."); + return Callable(); +} + Variant DisplayServer::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { WARN_PRINT("Global menus not supported by this display server."); return Variant(); @@ -527,14 +536,14 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("has_feature", "feature"), &DisplayServer::has_feature); ClassDB::bind_method(D_METHOD("get_name"), &DisplayServer::get_name); - ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_check_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_icon_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_icon_check_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_radio_check_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_radio_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_icon_radio_check_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("global_menu_add_multistate_item", "menu_root", "labe", "max_states", "default_state", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_multistate_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_submenu_item", "menu_root", "label", "submenu", "index"), &DisplayServer::global_menu_add_submenu_item, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu_root", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_check_item", "menu_root", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_icon_item", "menu_root", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_icon_check_item", "menu_root", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_radio_check_item", "menu_root", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_icon_radio_check_item", "menu_root", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("global_menu_add_multistate_item", "menu_root", "labe", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu_root", "index"), &DisplayServer::global_menu_add_separator, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_get_item_index_from_text", "menu_root", "text"), &DisplayServer::global_menu_get_item_index_from_text); @@ -544,6 +553,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_is_item_checkable", "menu_root", "idx"), &DisplayServer::global_menu_is_item_checkable); ClassDB::bind_method(D_METHOD("global_menu_is_item_radio_checkable", "menu_root", "idx"), &DisplayServer::global_menu_is_item_radio_checkable); ClassDB::bind_method(D_METHOD("global_menu_get_item_callback", "menu_root", "idx"), &DisplayServer::global_menu_get_item_callback); + ClassDB::bind_method(D_METHOD("global_menu_get_item_key_callback", "menu_root", "idx"), &DisplayServer::global_menu_get_item_key_callback); ClassDB::bind_method(D_METHOD("global_menu_get_item_tag", "menu_root", "idx"), &DisplayServer::global_menu_get_item_tag); ClassDB::bind_method(D_METHOD("global_menu_get_item_text", "menu_root", "idx"), &DisplayServer::global_menu_get_item_text); ClassDB::bind_method(D_METHOD("global_menu_get_item_submenu", "menu_root", "idx"), &DisplayServer::global_menu_get_item_submenu); @@ -559,6 +569,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_set_item_checkable", "menu_root", "idx", "checkable"), &DisplayServer::global_menu_set_item_checkable); ClassDB::bind_method(D_METHOD("global_menu_set_item_radio_checkable", "menu_root", "idx", "checkable"), &DisplayServer::global_menu_set_item_radio_checkable); ClassDB::bind_method(D_METHOD("global_menu_set_item_callback", "menu_root", "idx", "callback"), &DisplayServer::global_menu_set_item_callback); + ClassDB::bind_method(D_METHOD("global_menu_set_item_key_callback", "menu_root", "idx", "key_callback"), &DisplayServer::global_menu_set_item_key_callback); ClassDB::bind_method(D_METHOD("global_menu_set_item_tag", "menu_root", "idx", "tag"), &DisplayServer::global_menu_set_item_tag); ClassDB::bind_method(D_METHOD("global_menu_set_item_text", "menu_root", "idx", "text"), &DisplayServer::global_menu_set_item_text); ClassDB::bind_method(D_METHOD("global_menu_set_item_submenu", "menu_root", "idx", "submenu"), &DisplayServer::global_menu_set_item_submenu); diff --git a/servers/display_server.h b/servers/display_server.h index 0b162fe491..7cb880cbcb 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -128,14 +128,14 @@ public: virtual bool has_feature(Feature p_feature) const = 0; virtual String get_name() const = 0; - virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); - virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1); + virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); virtual int global_menu_add_separator(const String &p_menu_root, int p_index = -1); virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const; @@ -145,6 +145,7 @@ public: virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const; virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const; virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const; + virtual Callable global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const; virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const; virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const; virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const; @@ -160,6 +161,7 @@ public: virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable); virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable); virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback); + virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback); virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag); virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text); virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu); diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index 0f73df8894..cec8b95225 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -53,6 +53,12 @@ NavigationServer2D *NavigationServer2D::singleton = nullptr; return NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0)); \ } +#define FORWARD_1_R_C(CONV_R, FUNC_NAME, T_0, D_0, CONV_0) \ + NavigationServer2D::FUNC_NAME(T_0 D_0) \ + const { \ + return CONV_R(NavigationServer3D::get_singleton()->FUNC_NAME(CONV_0(D_0))); \ + } + #define FORWARD_2_C(FUNC_NAME, T_0, D_0, T_1, D_1, CONV_0, CONV_1) \ NavigationServer2D::FUNC_NAME(T_0 D_0, T_1 D_1) \ const { \ @@ -190,6 +196,22 @@ Color NavigationServer2D::get_debug_navigation_geometry_face_disabled_color() co return NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_color(); } +void NavigationServer2D::set_debug_navigation_link_connection_color(const Color &p_color) { + NavigationServer3D::get_singleton_mut()->set_debug_navigation_link_connection_color(p_color); +} + +Color NavigationServer2D::get_debug_navigation_link_connection_color() const { + return NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_color(); +} + +void NavigationServer2D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) { + NavigationServer3D::get_singleton_mut()->set_debug_navigation_link_connection_disabled_color(p_color); +} + +Color NavigationServer2D::get_debug_navigation_link_connection_disabled_color() const { + return NavigationServer3D::get_singleton()->get_debug_navigation_link_connection_disabled_color(); +} + void NavigationServer2D::set_debug_navigation_enable_edge_connections(const bool p_value) { NavigationServer3D::get_singleton_mut()->set_debug_navigation_enable_edge_connections(p_value); } @@ -209,10 +231,13 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer2D::map_get_cell_size); ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer2D::map_set_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer2D::map_get_edge_connection_margin); + ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer2D::map_set_link_connection_radius); + ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer2D::map_get_link_connection_radius); ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer2D::map_get_path, DEFVAL(1)); ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer2D::map_get_closest_point); ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer2D::map_get_closest_point_owner); + ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer2D::map_get_links); ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer2D::map_get_regions); ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer2D::map_get_agents); @@ -234,6 +259,22 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); + ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map); + ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer2D::link_get_map); + ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer2D::link_set_bidirectional); + ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional); + ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers); + ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer2D::link_get_navigation_layers); + ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer2D::link_set_start_location); + ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer2D::link_get_start_location); + ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer2D::link_set_end_location); + ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer2D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer2D::link_set_enter_cost); + ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer2D::link_get_enter_cost); + ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer2D::link_set_travel_cost); + ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer2D::link_get_travel_cost); + ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer2D::agent_create); ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer2D::agent_set_map); ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer2D::agent_get_map); @@ -265,6 +306,8 @@ NavigationServer2D::~NavigationServer2D() { TypedArray<RID> FORWARD_0_C(get_maps); +TypedArray<RID> FORWARD_1_C(map_get_links, RID, p_map, rid_to_rid); + TypedArray<RID> FORWARD_1_C(map_get_regions, RID, p_map, rid_to_rid); TypedArray<RID> FORWARD_1_C(map_get_agents, RID, p_map, rid_to_rid); @@ -289,6 +332,9 @@ real_t FORWARD_1_C(map_get_cell_size, RID, p_map, rid_to_rid); void FORWARD_2_C(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin, rid_to_rid, real_to_real); real_t FORWARD_1_C(map_get_edge_connection_margin, RID, p_map, rid_to_rid); +void FORWARD_2_C(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius, rid_to_rid, real_to_real); +real_t FORWARD_1_C(map_get_link_connection_radius, RID, p_map, rid_to_rid); + Vector<Vector2> FORWARD_5_R_C(vector_v3_to_v2, map_get_path, RID, p_map, Vector2, p_origin, Vector2, p_destination, bool, p_optimize, uint32_t, p_layers, rid_to_rid, v2_to_v3, v2_to_v3, bool_to_bool, uint32_to_uint32); Vector2 FORWARD_2_R_C(v3_to_v2, map_get_closest_point, RID, p_map, const Vector2 &, p_point, rid_to_rid, v2_to_v3); @@ -315,6 +361,23 @@ int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); +RID FORWARD_0_C(link_create); + +void FORWARD_2_C(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid); +RID FORWARD_1_C(link_get_map, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool); +bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32); +uint32_t FORWARD_1_C(link_get_navigation_layers, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_start_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_start_location, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_end_location, RID, p_link, Vector2, p_location, rid_to_rid, v2_to_v3); +Vector2 FORWARD_1_R_C(v3_to_v2, link_get_end_location, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_enter_cost, RID, p_link, real_t, p_enter_cost, rid_to_rid, real_to_real); +real_t FORWARD_1_C(link_get_enter_cost, RID, p_link, rid_to_rid); +void FORWARD_2_C(link_set_travel_cost, RID, p_link, real_t, p_travel_cost, rid_to_rid, real_to_real); +real_t FORWARD_1_C(link_get_travel_cost, RID, p_link, rid_to_rid); + RID NavigationServer2D::agent_create() const { RID agent = NavigationServer3D::get_singleton()->agent_create(); NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, true); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 5e96466d66..b2ea4c28a0 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -76,12 +76,19 @@ public: /// Returns the edge connection margin of this map. virtual real_t map_get_edge_connection_margin(RID p_map) const; + /// Set the map link connection radius used to attach links to the nav mesh. + virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) const; + + /// Returns the link connection radius of this map. + virtual real_t map_get_link_connection_radius(RID p_map) const; + /// Returns the navigation path to reach the destination from the origin. virtual Vector<Vector2> map_get_path(RID p_map, Vector2 p_origin, Vector2 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const; virtual Vector2 map_get_closest_point(RID p_map, const Vector2 &p_point) const; virtual RID map_get_closest_point_owner(RID p_map, const Vector2 &p_point) const; + virtual TypedArray<RID> map_get_links(RID p_map) const; virtual TypedArray<RID> map_get_regions(RID p_map) const; virtual TypedArray<RID> map_get_agents(RID p_map) const; @@ -119,6 +126,37 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const; + /// Creates a new link between locations in the nav map. + virtual RID link_create() const; + + /// Set the map of this link. + virtual void link_set_map(RID p_link, RID p_map) const; + virtual RID link_get_map(RID p_link) const; + + /// Set whether this link travels in both directions. + virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) const; + virtual bool link_is_bidirectional(RID p_link) const; + + /// Set the link's layers. + virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) const; + virtual uint32_t link_get_navigation_layers(RID p_link) const; + + /// Set the start location of the link. + virtual void link_set_start_location(RID p_link, Vector2 p_location) const; + virtual Vector2 link_get_start_location(RID p_link) const; + + /// Set the end location of the link. + virtual void link_set_end_location(RID p_link, Vector2 p_location) const; + virtual Vector2 link_get_end_location(RID p_link) const; + + /// Set the enter cost of the link. + virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) const; + virtual real_t link_get_enter_cost(RID p_link) const; + + /// Set the travel cost of the link. + virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) const; + virtual real_t link_get_travel_cost(RID p_link) const; + /// Creates the agent. virtual RID agent_create() const; @@ -198,6 +236,12 @@ public: void set_debug_navigation_geometry_face_disabled_color(const Color &p_color); Color get_debug_navigation_geometry_face_disabled_color() const; + void set_debug_navigation_link_connection_color(const Color &p_color); + Color get_debug_navigation_link_connection_color() const; + + void set_debug_navigation_link_connection_disabled_color(const Color &p_color); + Color get_debug_navigation_link_connection_disabled_color() const; + void set_debug_navigation_enable_edge_connections(const bool p_value); bool get_debug_navigation_enable_edge_connections() const; #endif // DEBUG_ENABLED diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 466b74bc64..bc0602e1df 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -48,12 +48,15 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &NavigationServer3D::map_get_cell_size); ClassDB::bind_method(D_METHOD("map_set_edge_connection_margin", "map", "margin"), &NavigationServer3D::map_set_edge_connection_margin); ClassDB::bind_method(D_METHOD("map_get_edge_connection_margin", "map"), &NavigationServer3D::map_get_edge_connection_margin); + ClassDB::bind_method(D_METHOD("map_set_link_connection_radius", "map", "radius"), &NavigationServer3D::map_set_link_connection_radius); + ClassDB::bind_method(D_METHOD("map_get_link_connection_radius", "map"), &NavigationServer3D::map_get_link_connection_radius); ClassDB::bind_method(D_METHOD("map_get_path", "map", "origin", "destination", "optimize", "navigation_layers"), &NavigationServer3D::map_get_path, DEFVAL(1)); ClassDB::bind_method(D_METHOD("map_get_closest_point_to_segment", "map", "start", "end", "use_collision"), &NavigationServer3D::map_get_closest_point_to_segment, DEFVAL(false)); ClassDB::bind_method(D_METHOD("map_get_closest_point", "map", "to_point"), &NavigationServer3D::map_get_closest_point); ClassDB::bind_method(D_METHOD("map_get_closest_point_normal", "map", "to_point"), &NavigationServer3D::map_get_closest_point_normal); ClassDB::bind_method(D_METHOD("map_get_closest_point_owner", "map", "to_point"), &NavigationServer3D::map_get_closest_point_owner); + ClassDB::bind_method(D_METHOD("map_get_links", "map"), &NavigationServer3D::map_get_links); ClassDB::bind_method(D_METHOD("map_get_regions", "map"), &NavigationServer3D::map_get_regions); ClassDB::bind_method(D_METHOD("map_get_agents", "map"), &NavigationServer3D::map_get_agents); @@ -76,6 +79,22 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); + ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map); + ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer3D::link_get_map); + ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer3D::link_set_bidirectional); + ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer3D::link_is_bidirectional); + ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer3D::link_set_navigation_layers); + ClassDB::bind_method(D_METHOD("link_get_navigation_layers", "link"), &NavigationServer3D::link_get_navigation_layers); + ClassDB::bind_method(D_METHOD("link_set_start_location", "link", "location"), &NavigationServer3D::link_set_start_location); + ClassDB::bind_method(D_METHOD("link_get_start_location", "link"), &NavigationServer3D::link_get_start_location); + ClassDB::bind_method(D_METHOD("link_set_end_location", "link", "location"), &NavigationServer3D::link_set_end_location); + ClassDB::bind_method(D_METHOD("link_get_end_location", "link"), &NavigationServer3D::link_get_end_location); + ClassDB::bind_method(D_METHOD("link_set_enter_cost", "link", "enter_cost"), &NavigationServer3D::link_set_enter_cost); + ClassDB::bind_method(D_METHOD("link_get_enter_cost", "link"), &NavigationServer3D::link_get_enter_cost); + ClassDB::bind_method(D_METHOD("link_set_travel_cost", "link", "travel_cost"), &NavigationServer3D::link_set_travel_cost); + ClassDB::bind_method(D_METHOD("link_get_travel_cost", "link"), &NavigationServer3D::link_get_travel_cost); + ClassDB::bind_method(D_METHOD("agent_create"), &NavigationServer3D::agent_create); ClassDB::bind_method(D_METHOD("agent_set_map", "agent", "map"), &NavigationServer3D::agent_set_map); ClassDB::bind_method(D_METHOD("agent_get_map", "agent"), &NavigationServer3D::agent_get_map); @@ -118,11 +137,16 @@ NavigationServer3D::NavigationServer3D() { debug_navigation_geometry_face_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_color", Color(0.5, 1.0, 1.0, 0.4)); debug_navigation_geometry_edge_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_disabled_color", Color(0.5, 0.5, 0.5, 1.0)); debug_navigation_geometry_face_disabled_color = GLOBAL_DEF("debug/shapes/navigation/geometry_face_disabled_color", Color(0.5, 0.5, 0.5, 0.4)); + debug_navigation_link_connection_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_color", Color(1.0, 0.5, 1.0, 1.0)); + debug_navigation_link_connection_disabled_color = GLOBAL_DEF("debug/shapes/navigation/link_connection_disabled_color", Color(0.5, 0.5, 0.5, 1.0)); + debug_navigation_enable_edge_connections = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections", true); debug_navigation_enable_edge_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_connections_xray", true); debug_navigation_enable_edge_lines = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines", true); debug_navigation_enable_edge_lines_xray = GLOBAL_DEF("debug/shapes/navigation/enable_edge_lines_xray", true); debug_navigation_enable_geometry_face_random_color = GLOBAL_DEF("debug/shapes/navigation/enable_geometry_face_random_color", true); + debug_navigation_enable_link_connections = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections", true); + debug_navigation_enable_link_connections_xray = GLOBAL_DEF("debug/shapes/navigation/enable_link_connections_xray", true); if (Engine::get_singleton()->is_editor_hint()) { // enable NavigationServer3D when in Editor or else navmesh edge connections are invisible @@ -261,6 +285,40 @@ Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_edge_connection return debug_navigation_edge_connections_material; } +Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connections_material() { + if (debug_navigation_link_connections_material.is_valid()) { + return debug_navigation_link_connections_material; + } + + Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_albedo(debug_navigation_link_connection_color); + if (debug_navigation_enable_link_connections_xray) { + material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + } + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2); + + debug_navigation_link_connections_material = material; + return debug_navigation_link_connections_material; +} + +Ref<StandardMaterial3D> NavigationServer3D::get_debug_navigation_link_connections_disabled_material() { + if (debug_navigation_link_connections_disabled_material.is_valid()) { + return debug_navigation_link_connections_disabled_material; + } + + Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_albedo(debug_navigation_link_connection_disabled_color); + if (debug_navigation_enable_link_connections_xray) { + material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); + } + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MAX - 2); + + debug_navigation_link_connections_disabled_material = material; + return debug_navigation_link_connections_disabled_material; +} + void NavigationServer3D::set_debug_navigation_edge_connection_color(const Color &p_color) { debug_navigation_edge_connection_color = p_color; if (debug_navigation_edge_connections_material.is_valid()) { @@ -316,6 +374,28 @@ Color NavigationServer3D::get_debug_navigation_geometry_face_disabled_color() co return debug_navigation_geometry_face_disabled_color; } +void NavigationServer3D::set_debug_navigation_link_connection_color(const Color &p_color) { + debug_navigation_link_connection_color = p_color; + if (debug_navigation_link_connections_material.is_valid()) { + debug_navigation_link_connections_material->set_albedo(debug_navigation_link_connection_color); + } +} + +Color NavigationServer3D::get_debug_navigation_link_connection_color() const { + return debug_navigation_link_connection_color; +} + +void NavigationServer3D::set_debug_navigation_link_connection_disabled_color(const Color &p_color) { + debug_navigation_link_connection_disabled_color = p_color; + if (debug_navigation_link_connections_disabled_material.is_valid()) { + debug_navigation_link_connections_disabled_material->set_albedo(debug_navigation_link_connection_disabled_color); + } +} + +Color NavigationServer3D::get_debug_navigation_link_connection_disabled_color() const { + return debug_navigation_link_connection_disabled_color; +} + void NavigationServer3D::set_debug_navigation_enable_edge_connections(const bool p_value) { debug_navigation_enable_edge_connections = p_value; debug_dirty = true; @@ -368,6 +448,27 @@ bool NavigationServer3D::get_debug_navigation_enable_geometry_face_random_color( return debug_navigation_enable_geometry_face_random_color; } +void NavigationServer3D::set_debug_navigation_enable_link_connections(const bool p_value) { + debug_navigation_enable_link_connections = p_value; + debug_dirty = true; + call_deferred("_emit_navigation_debug_changed_signal"); +} + +bool NavigationServer3D::get_debug_navigation_enable_link_connections() const { + return debug_navigation_enable_link_connections; +} + +void NavigationServer3D::set_debug_navigation_enable_link_connections_xray(const bool p_value) { + debug_navigation_enable_link_connections_xray = p_value; + if (debug_navigation_link_connections_material.is_valid()) { + debug_navigation_link_connections_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, debug_navigation_enable_link_connections_xray); + } +} + +bool NavigationServer3D::get_debug_navigation_enable_link_connections_xray() const { + return debug_navigation_enable_link_connections_xray; +} + void NavigationServer3D::set_debug_enabled(bool p_enabled) { if (debug_enabled != p_enabled) { debug_dirty = true; diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 3213da3d84..02770794c6 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -85,6 +85,12 @@ public: /// Returns the edge connection margin of this map. virtual real_t map_get_edge_connection_margin(RID p_map) const = 0; + /// Set the map link connection radius used to attach links to the nav mesh. + virtual void map_set_link_connection_radius(RID p_map, real_t p_connection_radius) const = 0; + + /// Returns the link connection radius of this map. + virtual real_t map_get_link_connection_radius(RID p_map) const = 0; + /// Returns the navigation path to reach the destination from the origin. virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const = 0; @@ -93,6 +99,7 @@ public: virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const = 0; virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const = 0; + virtual TypedArray<RID> map_get_links(RID p_map) const = 0; virtual TypedArray<RID> map_get_regions(RID p_map) const = 0; virtual TypedArray<RID> map_get_agents(RID p_map) const = 0; @@ -133,6 +140,37 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + /// Creates a new link between locations in the nav map. + virtual RID link_create() const = 0; + + /// Set the map of this link. + virtual void link_set_map(RID p_link, RID p_map) const = 0; + virtual RID link_get_map(RID p_link) const = 0; + + /// Set whether this link travels in both directions. + virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) const = 0; + virtual bool link_is_bidirectional(RID p_link) const = 0; + + /// Set the link's layers. + virtual void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) const = 0; + virtual uint32_t link_get_navigation_layers(RID p_link) const = 0; + + /// Set the start location of the link. + virtual void link_set_start_location(RID p_link, Vector3 p_location) const = 0; + virtual Vector3 link_get_start_location(RID p_link) const = 0; + + /// Set the end location of the link. + virtual void link_set_end_location(RID p_link, Vector3 p_location) const = 0; + virtual Vector3 link_get_end_location(RID p_link) const = 0; + + /// Set the enter cost of the link. + virtual void link_set_enter_cost(RID p_link, real_t p_enter_cost) const = 0; + virtual real_t link_get_enter_cost(RID p_link) const = 0; + + /// Set the travel cost of the link. + virtual void link_set_travel_cost(RID p_link, real_t p_travel_cost) const = 0; + virtual real_t link_get_travel_cost(RID p_link) const = 0; + /// Creates the agent. virtual RID agent_create() const = 0; @@ -209,29 +247,38 @@ public: virtual ~NavigationServer3D(); #ifdef DEBUG_ENABLED +private: bool debug_enabled = false; bool debug_dirty = true; void _emit_navigation_debug_changed_signal(); - void set_debug_enabled(bool p_enabled); - bool get_debug_enabled() const; - Color debug_navigation_edge_connection_color = Color(1.0, 0.0, 1.0, 1.0); Color debug_navigation_geometry_edge_color = Color(0.5, 1.0, 1.0, 1.0); Color debug_navigation_geometry_face_color = Color(0.5, 1.0, 1.0, 0.4); Color debug_navigation_geometry_edge_disabled_color = Color(0.5, 0.5, 0.5, 1.0); Color debug_navigation_geometry_face_disabled_color = Color(0.5, 0.5, 0.5, 0.4); + Color debug_navigation_link_connection_color = Color(1.0, 0.5, 1.0, 1.0); + Color debug_navigation_link_connection_disabled_color = Color(0.5, 0.5, 0.5, 1.0); + bool debug_navigation_enable_edge_connections = true; bool debug_navigation_enable_edge_connections_xray = true; bool debug_navigation_enable_edge_lines = true; bool debug_navigation_enable_edge_lines_xray = true; bool debug_navigation_enable_geometry_face_random_color = true; + bool debug_navigation_enable_link_connections = true; + bool debug_navigation_enable_link_connections_xray = true; Ref<StandardMaterial3D> debug_navigation_geometry_edge_material; Ref<StandardMaterial3D> debug_navigation_geometry_face_material; Ref<StandardMaterial3D> debug_navigation_geometry_edge_disabled_material; Ref<StandardMaterial3D> debug_navigation_geometry_face_disabled_material; Ref<StandardMaterial3D> debug_navigation_edge_connections_material; + Ref<StandardMaterial3D> debug_navigation_link_connections_material; + Ref<StandardMaterial3D> debug_navigation_link_connections_disabled_material; + +public: + void set_debug_enabled(bool p_enabled); + bool get_debug_enabled() const; void set_debug_navigation_edge_connection_color(const Color &p_color); Color get_debug_navigation_edge_connection_color() const; @@ -248,6 +295,12 @@ public: void set_debug_navigation_geometry_face_disabled_color(const Color &p_color); Color get_debug_navigation_geometry_face_disabled_color() const; + void set_debug_navigation_link_connection_color(const Color &p_color); + Color get_debug_navigation_link_connection_color() const; + + void set_debug_navigation_link_connection_disabled_color(const Color &p_color); + Color get_debug_navigation_link_connection_disabled_color() const; + void set_debug_navigation_enable_edge_connections(const bool p_value); bool get_debug_navigation_enable_edge_connections() const; @@ -263,11 +316,19 @@ public: void set_debug_navigation_enable_geometry_face_random_color(const bool p_value); bool get_debug_navigation_enable_geometry_face_random_color() const; + void set_debug_navigation_enable_link_connections(const bool p_value); + bool get_debug_navigation_enable_link_connections() const; + + void set_debug_navigation_enable_link_connections_xray(const bool p_value); + bool get_debug_navigation_enable_link_connections_xray() const; + Ref<StandardMaterial3D> get_debug_navigation_geometry_face_material(); Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_material(); Ref<StandardMaterial3D> get_debug_navigation_geometry_face_disabled_material(); Ref<StandardMaterial3D> get_debug_navigation_geometry_edge_disabled_material(); Ref<StandardMaterial3D> get_debug_navigation_edge_connections_material(); + Ref<StandardMaterial3D> get_debug_navigation_link_connections_material(); + Ref<StandardMaterial3D> get_debug_navigation_link_connections_disabled_material(); #endif // DEBUG_ENABLED }; diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/servers/physics_2d/godot_body_direct_state_2d.cpp index cde6e8c991..d413e03be6 100644 --- a/servers/physics_2d/godot_body_direct_state_2d.cpp +++ b/servers/physics_2d/godot_body_direct_state_2d.cpp @@ -138,7 +138,7 @@ void GodotPhysicsDirectBodyState2D::add_constant_torque(real_t p_torque) { } void GodotPhysicsDirectBodyState2D::set_constant_force(const Vector2 &p_force) { - if (!p_force.is_equal_approx(Vector2())) { + if (!p_force.is_zero_approx()) { body->wakeup(); } body->set_constant_force(p_force); diff --git a/servers/physics_2d/godot_physics_server_2d.cpp b/servers/physics_2d/godot_physics_server_2d.cpp index c728dccd4f..cec31bdc31 100644 --- a/servers/physics_2d/godot_physics_server_2d.cpp +++ b/servers/physics_2d/godot_physics_server_2d.cpp @@ -848,7 +848,7 @@ void GodotPhysicsServer2D::body_set_constant_force(RID p_body, const Vector2 &p_ ERR_FAIL_COND(!body); body->set_constant_force(p_force); - if (!p_force.is_equal_approx(Vector2())) { + if (!p_force.is_zero_approx()) { body->wakeup(); } } diff --git a/servers/physics_3d/godot_body_direct_state_3d.cpp b/servers/physics_3d/godot_body_direct_state_3d.cpp index a8c6086e1c..25088d33f3 100644 --- a/servers/physics_3d/godot_body_direct_state_3d.cpp +++ b/servers/physics_3d/godot_body_direct_state_3d.cpp @@ -145,7 +145,7 @@ void GodotPhysicsDirectBodyState3D::add_constant_torque(const Vector3 &p_torque) } void GodotPhysicsDirectBodyState3D::set_constant_force(const Vector3 &p_force) { - if (!p_force.is_equal_approx(Vector3())) { + if (!p_force.is_zero_approx()) { body->wakeup(); } body->set_constant_force(p_force); @@ -156,7 +156,7 @@ Vector3 GodotPhysicsDirectBodyState3D::get_constant_force() const { } void GodotPhysicsDirectBodyState3D::set_constant_torque(const Vector3 &p_torque) { - if (!p_torque.is_equal_approx(Vector3())) { + if (!p_torque.is_zero_approx()) { body->wakeup(); } body->set_constant_torque(p_torque); diff --git a/servers/physics_3d/godot_collision_solver_3d_sat.cpp b/servers/physics_3d/godot_collision_solver_3d_sat.cpp index 20e9300778..56e644b57b 100644 --- a/servers/physics_3d/godot_collision_solver_3d_sat.cpp +++ b/servers/physics_3d/godot_collision_solver_3d_sat.cpp @@ -629,7 +629,7 @@ public: _FORCE_INLINE_ bool test_axis(const Vector3 &p_axis) { Vector3 axis = p_axis; - if (axis.is_equal_approx(Vector3())) { + if (axis.is_zero_approx()) { // strange case, try an upwards separator axis = Vector3(0.0, 1.0, 0.0); } diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/servers/physics_3d/godot_physics_server_3d.cpp index 9c1535f561..b028c00a31 100644 --- a/servers/physics_3d/godot_physics_server_3d.cpp +++ b/servers/physics_3d/godot_physics_server_3d.cpp @@ -760,7 +760,7 @@ void GodotPhysicsServer3D::body_set_constant_force(RID p_body, const Vector3 &p_ ERR_FAIL_COND(!body); body->set_constant_force(p_force); - if (!p_force.is_equal_approx(Vector3())) { + if (!p_force.is_zero_approx()) { body->wakeup(); } } @@ -776,7 +776,7 @@ void GodotPhysicsServer3D::body_set_constant_torque(RID p_body, const Vector3 &p ERR_FAIL_COND(!body); body->set_constant_torque(p_torque); - if (!p_torque.is_equal_approx(Vector3())) { + if (!p_torque.is_zero_approx()) { body->wakeup(); } } diff --git a/servers/rendering/dummy/environment/gi.h b/servers/rendering/dummy/environment/gi.h index 76d34cd14e..fea7994cfe 100644 --- a/servers/rendering/dummy/environment/gi.h +++ b/servers/rendering/dummy/environment/gi.h @@ -62,6 +62,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override {} virtual float voxel_gi_get_energy(RID p_voxel_gi) const override { return 0.0; } + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override {} + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override { return 1.0; } + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override {} virtual float voxel_gi_get_bias(RID p_voxel_gi) const override { return 0.0; } diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 8d3a45d696..7dac1f5966 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -106,10 +106,10 @@ public: /* SDFGI UPDATE */ - void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(RID p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { return 0; } + void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override { return 0; } + AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return AABB(); } + uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return 0; } /* SKY API */ @@ -140,14 +140,6 @@ public: Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } - RID camera_effects_allocate() override { return RID(); } - void camera_effects_initialize(RID p_rid) override {} - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override {} - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override {} - - 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 {} - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override {} - void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override {} void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override {} @@ -188,7 +180,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override {} - void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_info = nullptr) override {} + void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_info = nullptr) override {} void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override {} @@ -196,8 +188,7 @@ public: void set_time(double p_time, double p_step) override {} void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {} - RID render_buffers_create() override { return RID(); } - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override {} + Ref<RenderSceneBuffers> render_buffers_create() override { return Ref<RenderSceneBuffers>(); } void gi_set_use_half_resolution(bool p_enable) override {} void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {} @@ -212,6 +203,9 @@ public: if (is_environment(p_rid)) { environment_free(p_rid); return true; + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + RSG::camera_attributes->camera_attributes_free(p_rid); + return true; } else { return false; } diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 0c0ea61df5..79f484d513 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -119,6 +119,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override {} virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override {} + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override {} virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override { return PackedVector3Array(); } virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); } virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); } diff --git a/servers/rendering/environment/renderer_gi.h b/servers/rendering/environment/renderer_gi.h index 70d2bb3a9c..0faf683015 100644 --- a/servers/rendering/environment/renderer_gi.h +++ b/servers/rendering/environment/renderer_gi.h @@ -63,6 +63,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0; virtual float voxel_gi_get_energy(RID p_voxel_gi) const = 0; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) = 0; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const = 0; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0; virtual float voxel_gi_get_bias(RID p_voxel_gi) const = 0; diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 4cfded8460..78d4ded617 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -35,6 +35,7 @@ #include "servers/rendering/environment/renderer_gi.h" #include "servers/rendering/renderer_canvas_render.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/storage/camera_attributes_storage.h" #include "servers/rendering/storage/light_storage.h" #include "servers/rendering/storage/material_storage.h" #include "servers/rendering/storage/mesh_storage.h" diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index cc7441776d..27850695b0 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -33,6 +33,8 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" +#include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/storage/camera_attributes_storage.h" using namespace RendererRD; @@ -84,7 +86,7 @@ BokehDOF::~BokehDOF() { } } -void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { +void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of bokeh depth of field with the mobile renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -92,22 +94,39 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, MaterialStorage *material_storage = MaterialStorage::get_singleton(); ERR_FAIL_NULL(material_storage); + bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes); + float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes); + float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes); + bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes); + float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes); + float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes); + float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius. + + bool use_jitter = RSG::camera_attributes->camera_attributes_get_dof_blur_use_jitter(); + RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape(); + RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality(); + // setup our push constant memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant)); - bokeh.push_constant.blur_far_active = p_dof_far; - bokeh.push_constant.blur_far_begin = p_dof_far_begin; - bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size; - - bokeh.push_constant.blur_near_active = p_dof_near; - bokeh.push_constant.blur_near_begin = p_dof_near_begin; - bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size); - bokeh.push_constant.use_jitter = p_use_jitter; + bokeh.push_constant.blur_far_active = dof_far; + bokeh.push_constant.blur_far_begin = dof_far_begin; + bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; // Only used with non-physically-based. + bokeh.push_constant.use_physical_far = dof_far_size < 0.0; + bokeh.push_constant.blur_size_far = bokeh_size; // Only used with physically-based. + + bokeh.push_constant.blur_near_active = dof_near; + bokeh.push_constant.blur_near_begin = dof_near_begin; + bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; // Only used with non-physically-based. + bokeh.push_constant.use_physical_near = dof_near_size < 0.0; + bokeh.push_constant.blur_size_near = bokeh_size; // Only used with physically-based. + + bokeh.push_constant.use_jitter = use_jitter; bokeh.push_constant.jitter_seed = Math::randf() * 1000.0; bokeh.push_constant.z_near = p_cam_znear; bokeh.push_constant.z_far = p_cam_zfar; bokeh.push_constant.orthogonal = p_cam_orthogonal; - bokeh.push_constant.blur_size = p_bokeh_size; + bokeh.push_constant.blur_size = (dof_near_size < 0.0 && dof_far_size < 0.0) ? 32 : bokeh_size; // Cap with physically-based to keep performance reasonable. bokeh.push_constant.second_pass = false; bokeh.push_constant.half_size = false; @@ -150,9 +169,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1); RD::get_singleton()->compute_list_add_barrier(compute_list); - if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) { + if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) { //second pass - BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; + BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -160,9 +179,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, static const int quality_samples[4] = { 6, 12, 12, 24 }; - bokeh.push_constant.steps = quality_samples[p_quality]; + bokeh.push_constant.steps = quality_samples[blur_quality]; - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes) RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0); @@ -187,7 +206,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, //third pass bokeh.push_constant.second_pass = true; - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image1), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1); } else { @@ -200,7 +219,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1); RD::get_singleton()->compute_list_add_barrier(compute_list); - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //forth pass, upscale for low quality shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE); @@ -232,7 +251,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 }; bokeh.push_constant.steps = 0; - bokeh.push_constant.blur_scale = quality_scale[p_quality]; + bokeh.push_constant.blur_scale = quality_scale[blur_quality]; //circle always runs in half size, otherwise too expensive @@ -273,7 +292,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_end(); } -void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { +void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't blur-based depth of field with the clustered renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -281,6 +300,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f MaterialStorage *material_storage = MaterialStorage::get_singleton(); ERR_FAIL_NULL(material_storage); + bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes); + float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes); + float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes); + bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes); + float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes); + float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes); + float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius. + + RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape(); + RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality(); + // setup our base push constant memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant)); @@ -292,7 +322,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f bokeh.push_constant.second_pass = false; bokeh.push_constant.half_size = false; - bokeh.push_constant.blur_size = p_dof_blur_amount; + bokeh.push_constant.blur_size = bokeh_size; // setup our uniforms RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -307,17 +337,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f RD::Uniform u_weight_texture2(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[2] })); RD::Uniform u_weight_texture3(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[3] })); - if (p_dof_far || p_dof_near) { - if (p_dof_far) { + if (dof_far || dof_near) { + if (dof_far) { bokeh.push_constant.blur_far_active = true; - bokeh.push_constant.blur_far_begin = p_dof_far_begin; - bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size; + bokeh.push_constant.blur_far_begin = dof_far_begin; + bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; } - if (p_dof_near) { + if (dof_near) { bokeh.push_constant.blur_near_active = true; - bokeh.push_constant.blur_near_begin = p_dof_near_begin; - bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size; + bokeh.push_constant.blur_near_begin = dof_near_begin; + bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; } { @@ -337,14 +367,14 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f RD::get_singleton()->draw_list_end(); } - if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) { + if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) { // double pass approach - BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; + BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes) bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1; bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1; @@ -354,7 +384,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f static const int quality_samples[4] = { 6, 12, 12, 24 }; bokeh.push_constant.blur_scale = 0.5; - bokeh.push_constant.steps = quality_samples[p_quality]; + bokeh.push_constant.steps = quality_samples[blur_quality]; RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; @@ -373,7 +403,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f // Pass 2 if (!bokeh.push_constant.half_size) { // do not output weight, we're writing back into our base buffer - mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT; + mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT; shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -432,7 +462,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f } static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 }; - bokeh.push_constant.blur_scale = quality_scale[p_quality]; + bokeh.push_constant.blur_scale = quality_scale[blur_quality]; bokeh.push_constant.steps = 0.0; RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.h b/servers/rendering/renderer_rd/effects/bokeh_dof.h index 30b33be168..33dbdfcdc1 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.h +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.h @@ -66,6 +66,11 @@ private: uint32_t use_jitter; float jitter_seed; + uint32_t use_physical_near; + uint32_t use_physical_far; + + float blur_size_near; + float blur_size_far; uint32_t pad[2]; }; @@ -111,8 +116,8 @@ public: BokehDOF(bool p_prefer_raster_effects); ~BokehDOF(); - void bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); - void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); + void bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); + void bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 5507483cee..53237c1dfb 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -654,7 +654,7 @@ void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Re RD::get_singleton()->compute_list_end(); } -void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { +void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -678,7 +678,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con copy.push_constant.glow_white = 0; //actually unused copy.push_constant.glow_luminance_cap = p_luminance_cap; - copy.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also + copy.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also // setup our uniforms RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -705,7 +705,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con RD::get_singleton()->compute_list_end(); } -void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { +void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -713,6 +713,9 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc MaterialStorage *material_storage = MaterialStorage::get_singleton(); ERR_FAIL_NULL(material_storage); + RID half_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_half_texture); + RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture); + memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant)); BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW; @@ -729,7 +732,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc blur_raster.push_constant.glow_white = 0; //actually unused blur_raster.push_constant.glow_luminance_cap = p_luminance_cap; - blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also + blur_raster.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier; @@ -737,14 +740,14 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture })); - RD::Uniform u_rd_texture_half(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_rd_texture_half })); + RD::Uniform u_half_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_half_texture })); RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode); ERR_FAIL_COND(shader.is_null()); //HORIZONTAL - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half))); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); if (p_auto_exposure.is_valid() && p_first_pass) { RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure })); @@ -764,9 +767,9 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc ERR_FAIL_COND(shader.is_null()); //VERTICAL - draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_rd_texture_half), 0); + draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); blur_raster.push_constant.flags = base_flags; @@ -810,9 +813,11 @@ void CopyEffects::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const RD::get_singleton()->compute_list_end(); } -void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size) { +void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer."); + RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture); + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -833,8 +838,8 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebu RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index d25555eee5..0ddb60ebef 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -86,7 +86,7 @@ private: float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; float luminance_multiplier; float res1; @@ -148,7 +148,7 @@ private: float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; // DOF. float camera_z_far; float camera_z_near; @@ -321,11 +321,11 @@ public: void copy_raster(RID p_source_texture, RID p_dest_framebuffer); void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false); - void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); - void gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); + void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0); + void gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0); void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size); - void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size); + void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size); void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false); diff --git a/servers/rendering/renderer_rd/effects/fsr.cpp b/servers/rendering/renderer_rd/effects/fsr.cpp new file mode 100644 index 0000000000..5fde24a926 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/fsr.cpp @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* fsr.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fsr.h" +#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" + +using namespace RendererRD; + +FSR::FSR() { + Vector<String> FSR_upscale_modes; + +#if defined(MACOS_ENABLED) || defined(IOS_ENABLED) + // MoltenVK does not support some of the operations used by the normal mode of FSR. Fallback works just fine though. + FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n"); +#else + // Everyone else can use normal mode when available. + if (RD::get_singleton()->has_feature(RD::SUPPORTS_FSR_HALF_FLOAT)) { + FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_NORMAL\n"); + } else { + FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n"); + } +#endif + + fsr_shader.initialize(FSR_upscale_modes); + + shader_version = fsr_shader.version_create(); + pipeline = RD::get_singleton()->compute_pipeline_create(fsr_shader.version_get_shader(shader_version, 0)); +} + +FSR::~FSR() { + fsr_shader.version_free(shader_version); +} + +void FSR::fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + Size2i internal_size = p_render_buffers->get_internal_size(); + Size2i target_size = p_render_buffers->get_target_size(); + float fsr_upscale_sharpness = p_render_buffers->get_fsr_sharpness(); + + if (!p_render_buffers->has_texture(SNAME("FSR"), SNAME("upscale_texture"))) { + RD::DataFormat format = p_render_buffers->get_base_data_format(); + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + uint32_t layers = 1; // we only need one layer, in multiview we're processing one layer at a time. + + p_render_buffers->create_texture(SNAME("FSR"), SNAME("upscale_texture"), format, usage_bits, RD::TEXTURE_SAMPLES_1, target_size, layers); + } + + RID upscale_texture = p_render_buffers->get_texture(SNAME("FSR"), SNAME("upscale_texture")); + + FSRUpscalePushConstant push_constant; + memset(&push_constant, 0, sizeof(FSRUpscalePushConstant)); + + int dispatch_x = (target_size.x + 15) / 16; + int dispatch_y = (target_size.y + 15) / 16; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline); + + push_constant.resolution_width = internal_size.width; + push_constant.resolution_height = internal_size.height; + push_constant.upscaled_width = target_size.width; + push_constant.upscaled_height = target_size.height; + push_constant.sharpness = fsr_upscale_sharpness; + + RID shader = fsr_shader.version_get_shader(shader_version, 0); + ERR_FAIL_COND(shader.is_null()); + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + //FSR Easc + RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, p_source_rd_texture }); + RD::Uniform u_upscale_texture(RD::UNIFORM_TYPE_IMAGE, 0, { upscale_texture }); + + push_constant.pass = FSR_UPSCALE_PASS_EASU; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_upscale_texture), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + //FSR Rcas + RD::Uniform u_upscale_texture_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, upscale_texture }); + RD::Uniform u_destination_texture(RD::UNIFORM_TYPE_IMAGE, 0, { p_destination_texture }); + + push_constant.pass = FSR_UPSCALE_PASS_RCAS; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_upscale_texture_with_sampler), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_destination_texture), 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant)); + + RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1); + + RD::get_singleton()->compute_list_end(compute_list); +} diff --git a/servers/rendering/renderer_rd/effects/fsr.h b/servers/rendering/renderer_rd/effects/fsr.h new file mode 100644 index 0000000000..1adfba527a --- /dev/null +++ b/servers/rendering/renderer_rd/effects/fsr.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* fsr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FSR_RD_H +#define FSR_RD_H + +#include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" +#include "servers/rendering/renderer_scene_render.h" + +#include "servers/rendering_server.h" + +namespace RendererRD { + +class FSR { +public: + FSR(); + ~FSR(); + + void fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture); + +private: + enum FSRUpscalePass { + FSR_UPSCALE_PASS_EASU = 0, + FSR_UPSCALE_PASS_RCAS = 1 + }; + + struct FSRUpscalePushConstant { + float resolution_width; + float resolution_height; + float upscaled_width; + float upscaled_height; + float sharpness; + int pass; + int _unused0, _unused1; + }; + + FsrUpscaleShaderRD fsr_shader; + RID shader_version; + RID pipeline; +}; + +} // namespace RendererRD + +#endif // FSR_RD_H diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index 874409b885..315bea2e67 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -32,6 +32,7 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" using namespace RendererRD; @@ -333,6 +334,22 @@ SSEffects::SSEffects() { } } } + + // Subsurface scattering + { + Vector<String> sss_modes; + sss_modes.push_back("\n#define USE_11_SAMPLES\n"); + sss_modes.push_back("\n#define USE_17_SAMPLES\n"); + sss_modes.push_back("\n#define USE_25_SAMPLES\n"); + + sss.shader.initialize(sss_modes); + + sss.shader_version = sss.shader.version_create(); + + for (int i = 0; i < sss_modes.size(); i++) { + sss.pipelines[i] = RD::get_singleton()->compute_pipeline_create(sss.shader.version_get_shader(sss.shader_version, i)); + } + } } SSEffects::~SSEffects() { @@ -376,6 +393,11 @@ SSEffects::~SSEffects() { RD::get_singleton()->free(ssao.importance_map_load_counter); } + { + // Cleanup Subsurface scattering + sss.shader.version_free(sss.shader_version); + } + singleton = nullptr; } @@ -1713,3 +1735,73 @@ void SSEffects::ssr_free(SSRRenderBuffers &p_ssr_buffers) { p_ssr_buffers.normal_scaled = RID(); } } + +/* Subsurface scattering */ + +void SSEffects::sub_surface_scattering(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_diffuse, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RenderingServer::SubSurfaceScatteringQuality p_quality) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + // Our intermediate buffer is only created if we haven't created it already. + RD::DataFormat format = p_render_buffers->get_base_data_format(); + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + uint32_t layers = 1; // We only need one layer, we're handling one view at a time + uint32_t mipmaps = 1; // Image::get_image_required_mipmaps(p_screen_size.x, p_screen_size.y, Image::FORMAT_RGBAH); + RID intermediate = p_render_buffers->create_texture(SNAME("SSR"), SNAME("intermediate"), format, usage_bits, RD::TEXTURE_SAMPLES_1, p_screen_size, layers, mipmaps); + + Plane p = p_camera.xform4(Plane(1, 0, -1, 1)); + p.normal /= p.d; + float unit_size = p.normal.x; + + { //scale color and depth to half + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + sss.push_constant.camera_z_far = p_camera.get_z_far(); + sss.push_constant.camera_z_near = p_camera.get_z_near(); + sss.push_constant.orthogonal = p_camera.is_orthogonal(); + sss.push_constant.unit_size = unit_size; + sss.push_constant.screen_size[0] = p_screen_size.x; + sss.push_constant.screen_size[1] = p_screen_size.y; + sss.push_constant.vertical = false; + sss.push_constant.scale = p_scale; + sss.push_constant.depth_scale = p_depth_scale; + + RID shader = sss.shader.version_get_shader(sss.shader_version, p_quality - 1); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sss.pipelines[p_quality - 1]); + + RD::Uniform u_diffuse_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_diffuse })); + RD::Uniform u_diffuse(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_diffuse })); + RD::Uniform u_intermediate_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, intermediate })); + RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ intermediate })); + RD::Uniform u_depth_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_depth })); + + // horizontal + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_diffuse_with_sampler), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_intermediate), 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_depth_with_sampler), 2); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + // vertical + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate_with_sampler), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_diffuse), 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_depth_with_sampler), 2); + + sss.push_constant.vertical = true; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1); + + RD::get_singleton()->compute_list_end(); + } +} diff --git a/servers/rendering/renderer_rd/effects/ss_effects.h b/servers/rendering/renderer_rd/effects/ss_effects.h index c31271ffd2..a60f3a48ab 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.h +++ b/servers/rendering/renderer_rd/effects/ss_effects.h @@ -44,9 +44,12 @@ #include "servers/rendering/renderer_rd/shaders/effects/ssil_blur.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/ssil_importance_map.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/ssil_interleave.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" +class RenderSceneBuffersRD; + namespace RendererRD { class SSEffects { @@ -168,6 +171,9 @@ public: void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, const RID *p_metallic_slices, const Color &p_metallic_mask, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets); void ssr_free(SSRRenderBuffers &p_ssr_buffers); + /* subsurface scattering */ + void sub_surface_scattering(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_diffuse, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality); + private: /* SS Downsampler */ @@ -501,6 +507,29 @@ private: RID shader_version; RID pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_FILTER_MAX]; } ssr_filter; + + /* Subsurface scattering */ + + struct SubSurfaceScatteringPushConstant { + int32_t screen_size[2]; + float camera_z_far; + float camera_z_near; + + uint32_t vertical; + uint32_t orthogonal; + float unit_size; + float scale; + + float depth_scale; + uint32_t pad[3]; + }; + + struct SubSurfaceScattering { + SubSurfaceScatteringPushConstant push_constant; + SubsurfaceScatteringShaderRD shader; + RID shader_version; + RID pipelines[3]; //3 quality levels + } sss; }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/effects/taa.cpp b/servers/rendering/renderer_rd/effects/taa.cpp new file mode 100644 index 0000000000..657385a509 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/taa.cpp @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* taa.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "taa.h" +#include "servers/rendering/renderer_rd/effects/copy_effects.h" +#include "servers/rendering/renderer_rd/storage_rd/material_storage.h" +#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" + +using namespace RendererRD; + +TAA::TAA() { + Vector<String> taa_modes; + taa_modes.push_back("\n#define MODE_TAA_RESOLVE"); + taa_shader.initialize(taa_modes); + shader_version = taa_shader.version_create(); + pipeline = RD::get_singleton()->compute_pipeline_create(taa_shader.version_get_shader(shader_version, 0)); +} + +TAA::~TAA() { + taa_shader.version_free(shader_version); +} + +void TAA::msaa_resolve(Ref<RenderSceneBuffersRD> p_render_buffers) { + if (!p_render_buffers->has_velocity_buffer(true)) { + // nothing to resolve + return; + } + + for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { + RID velocity_buffer_msaa = p_render_buffers->get_velocity_buffer(true, v); + RID velocity_buffer = p_render_buffers->get_velocity_buffer(false, v); + + RD::get_singleton()->texture_resolve_multisample(velocity_buffer_msaa, velocity_buffer); + } +} + +void TAA::resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far) { + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + RID shader = taa_shader.version_get_shader(shader_version, 0); + ERR_FAIL_COND(shader.is_null()); + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + TAAResolvePushConstant push_constant; + memset(&push_constant, 0, sizeof(TAAResolvePushConstant)); + push_constant.resolution_width = p_resolution.width; + push_constant.resolution_height = p_resolution.height; + push_constant.disocclusion_threshold = 0.025f; + push_constant.disocclusion_scale = 10.0f; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline); + + RD::Uniform u_frame_source(RD::UNIFORM_TYPE_IMAGE, 0, { p_frame }); + RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, { default_sampler, p_depth }); + RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 2, { p_velocity }); + RD::Uniform u_prev_velocity(RD::UNIFORM_TYPE_IMAGE, 3, { p_prev_velocity }); + RD::Uniform u_history(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 4, { default_sampler, p_history }); + RD::Uniform u_frame_dest(RD::UNIFORM_TYPE_IMAGE, 5, { p_temp }); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_frame_source, u_depth, u_velocity, u_prev_velocity, u_history, u_frame_dest), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(TAAResolvePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_resolution.width, p_resolution.height, 1); + RD::get_singleton()->compute_list_end(); +} + +void TAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far) { + CopyEffects *copy_effects = CopyEffects::get_singleton(); + + uint32_t view_count = p_render_buffers->get_view_count(); + Size2i internal_size = p_render_buffers->get_internal_size(); + Size2i target_size = p_render_buffers->get_target_size(); + + bool just_allocated = false; + if (!p_render_buffers->has_texture(SNAME("taa"), SNAME("history"))) { + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + p_render_buffers->create_texture(SNAME("taa"), SNAME("history"), p_format, usage_bits); + p_render_buffers->create_texture(SNAME("taa"), SNAME("temp"), p_format, usage_bits); + + p_render_buffers->create_texture(SNAME("taa"), SNAME("prev_velocity"), RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits); + + just_allocated = true; + } + + RD::get_singleton()->draw_command_begin_label("TAA"); + + for (uint32_t v = 0; v < view_count; v++) { + // Get our (cached) slices + RID internal_texture = p_render_buffers->get_internal_texture(v); + RID velocity_buffer = p_render_buffers->get_velocity_buffer(false, v); + RID taa_history = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("history"), v, 0); + RID taa_prev_velocity = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("prev_velocity"), v, 0); + + if (!just_allocated) { + RID depth_texture = p_render_buffers->get_depth_texture(v); + RID taa_temp = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("temp"), v, 0); + resolve(internal_texture, taa_temp, depth_texture, velocity_buffer, taa_prev_velocity, taa_history, Size2(internal_size.x, internal_size.y), p_z_near, p_z_far); + copy_effects->copy_to_rect(taa_temp, internal_texture, Rect2(0, 0, internal_size.x, internal_size.y)); + } + + copy_effects->copy_to_rect(internal_texture, taa_history, Rect2(0, 0, internal_size.x, internal_size.y)); + copy_effects->copy_to_rect(velocity_buffer, taa_prev_velocity, Rect2(0, 0, target_size.x, target_size.y)); + } + + RD::get_singleton()->draw_command_end_label(); +} diff --git a/servers/rendering/renderer_rd/effects/taa.h b/servers/rendering/renderer_rd/effects/taa.h new file mode 100644 index 0000000000..ce4af18866 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/taa.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* taa.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TAA_RD_H +#define TAA_RD_H + +#include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" +#include "servers/rendering/renderer_scene_render.h" + +#include "servers/rendering_server.h" + +namespace RendererRD { + +class TAA { +public: + TAA(); + ~TAA(); + + void msaa_resolve(Ref<RenderSceneBuffersRD> p_render_buffers); + void process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far); + +private: + struct TAAResolvePushConstant { + float resolution_width; + float resolution_height; + float disocclusion_threshold; + float disocclusion_scale; + }; + + TaaResolveShaderRD taa_shader; + RID shader_version; + RID pipeline; + + void resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far); +}; + +} // namespace RendererRD + +#endif // TAA_RD_H diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 38a4a37b8a..3a47b1420b 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -117,7 +117,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton 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.auto_exposure_scale = p_settings.auto_exposure_scale; tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; tonemap.push_constant.use_color_correction = p_settings.use_color_correction; @@ -203,7 +203,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col 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.auto_exposure_scale = p_settings.auto_exposure_scale; tonemap.push_constant.use_color_correction = p_settings.use_color_correction; diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h index 05db4a0cbe..e91118e241 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.h +++ b/servers/rendering/renderer_rd/effects/tone_mapper.h @@ -77,7 +77,7 @@ private: float exposure; // 4 - 84 float white; // 4 - 88 - float auto_exposure_grey; // 4 - 92 + float auto_exposure_scale; // 4 - 92 float luminance_multiplier; // 4 - 96 float pixel_size[2]; // 8 - 104 @@ -124,7 +124,7 @@ public: float white = 1.0; bool use_auto_exposure = false; - float auto_exposure_grey = 0.5; + float auto_exposure_scale = 0.5; RID exposure_texture; float luminance_multiplier = 1.0; diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 68cfd43d90..5ff00aa94c 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -91,47 +91,22 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RD::get_singleton()->draw_list_end(); } -void VRS::create_vrs_texture(const int p_base_width, const int p_base_height, const uint32_t p_view_count, RID &p_vrs_texture, RID &p_vrs_fb) { - // TODO find a way to skip this if VRS is not supported, but we don't have access to VulkanContext here, even though we're in vulkan.. hmmm - +Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const { // TODO we should find some way to store this properly, we're assuming 16x16 as this seems to be the standard but in our vrs_capacities we // obtain a minimum and maximum size, and we should choose something within this range and then make sure that is consistently set when creating // our frame buffer. Also it is important that we make the resulting size we calculate down below available to the end user so they know the size // of the VRS buffer to supply. Size2i texel_size = Size2i(16, 16); - RD::TextureFormat tf; - if (p_view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - } - tf.format = RD::DATA_FORMAT_R8_UINT; - tf.width = p_base_width / texel_size.x; - if (p_base_width % texel_size.x != 0) { - tf.width++; + int width = p_base_size.x / texel_size.x; + if (p_base_size.x % texel_size.x != 0) { + width++; } - tf.height = p_base_height / texel_size.y; - if (p_base_height % texel_size.y != 0) { - tf.height++; + int height = p_base_size.y / texel_size.y; + if (p_base_size.y % texel_size.y != 0) { + height++; } - tf.array_layers = p_view_count; // create a layer for every view - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - tf.samples = RD::TEXTURE_SAMPLES_1; - - p_vrs_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - // by default VRS is assumed to be our VRS attachment, but if we need to write into it, we need a bit more control - Vector<RID> fb; - fb.push_back(p_vrs_texture); - - RD::FramebufferPass pass; - pass.color_attachments.push_back(0); - - Vector<RD::FramebufferPass> passes; - passes.push_back(pass); - - p_vrs_fb = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, p_view_count); + return Size2i(width, height); } void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) { diff --git a/servers/rendering/renderer_rd/effects/vrs.h b/servers/rendering/renderer_rd/effects/vrs.h index dd15df615e..7125c6455d 100644 --- a/servers/rendering/renderer_rd/effects/vrs.h +++ b/servers/rendering/renderer_rd/effects/vrs.h @@ -66,7 +66,7 @@ public: void copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multiview = false); - void create_vrs_texture(const int p_base_width, const int p_base_height, const uint32_t p_view_count, RID &p_vrs_texture, RID &p_vrs_fb); + Size2i get_vrs_texture_size(const Size2i p_base_size) const; void update_vrs_texture(RID p_vrs_fb, RID p_render_target); }; diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 8d59b24f3f..b03415f2e3 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -108,115 +108,6 @@ RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_m return uniform_set; } -void EffectsRD::fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RID p_destination_texture, const Size2i &p_internal_size, const Size2i &p_size, float p_fsr_upscale_sharpness) { - memset(&FSR_upscale.push_constant, 0, sizeof(FSRUpscalePushConstant)); - - int dispatch_x = (p_size.x + 15) / 16; - int dispatch_y = (p_size.y + 15) / 16; - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, FSR_upscale.pipeline); - - FSR_upscale.push_constant.resolution_width = p_internal_size.width; - FSR_upscale.push_constant.resolution_height = p_internal_size.height; - FSR_upscale.push_constant.upscaled_width = p_size.width; - FSR_upscale.push_constant.upscaled_height = p_size.height; - FSR_upscale.push_constant.sharpness = p_fsr_upscale_sharpness; - - //FSR Easc - FSR_upscale.push_constant.pass = FSR_UPSCALE_PASS_EASU; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_secondary_texture), 1); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &FSR_upscale.push_constant, sizeof(FSRUpscalePushConstant)); - - RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1); - RD::get_singleton()->compute_list_add_barrier(compute_list); - - //FSR Rcas - FSR_upscale.push_constant.pass = FSR_UPSCALE_PASS_RCAS; - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_secondary_texture), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_destination_texture), 1); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &FSR_upscale.push_constant, sizeof(FSRUpscalePushConstant)); - - RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1); - - RD::get_singleton()->compute_list_end(compute_list); -} - -void EffectsRD::taa_resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far) { - UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); - ERR_FAIL_NULL(uniform_set_cache); - - RID shader = TAA_resolve.shader.version_get_shader(TAA_resolve.shader_version, 0); - ERR_FAIL_COND(shader.is_null()); - - memset(&TAA_resolve.push_constant, 0, sizeof(TAAResolvePushConstant)); - TAA_resolve.push_constant.resolution_width = p_resolution.width; - TAA_resolve.push_constant.resolution_height = p_resolution.height; - TAA_resolve.push_constant.disocclusion_threshold = 0.025f; - TAA_resolve.push_constant.disocclusion_scale = 10.0f; - - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, TAA_resolve.pipeline); - - RD::Uniform u_frame_source(RD::UNIFORM_TYPE_IMAGE, 0, { p_frame }); - RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, { default_sampler, p_depth }); - RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 2, { p_velocity }); - RD::Uniform u_prev_velocity(RD::UNIFORM_TYPE_IMAGE, 3, { p_prev_velocity }); - RD::Uniform u_history(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 4, { default_sampler, p_history }); - RD::Uniform u_frame_dest(RD::UNIFORM_TYPE_IMAGE, 5, { p_temp }); - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_frame_source, u_depth, u_velocity, u_prev_velocity, u_history, u_frame_dest), 0); - RD::get_singleton()->compute_list_set_push_constant(compute_list, &TAA_resolve.push_constant, sizeof(TAAResolvePushConstant)); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_resolution.width, p_resolution.height, 1); - RD::get_singleton()->compute_list_end(); -} - -void EffectsRD::sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RenderingServer::SubSurfaceScatteringQuality p_quality) { - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); - - Plane p = p_camera.xform4(Plane(1, 0, -1, 1)); - p.normal /= p.d; - float unit_size = p.normal.x; - - { //scale color and depth to half - sss.push_constant.camera_z_far = p_camera.get_z_far(); - sss.push_constant.camera_z_near = p_camera.get_z_near(); - sss.push_constant.orthogonal = p_camera.is_orthogonal(); - sss.push_constant.unit_size = unit_size; - sss.push_constant.screen_size[0] = p_screen_size.x; - sss.push_constant.screen_size[1] = p_screen_size.y; - sss.push_constant.vertical = false; - sss.push_constant.scale = p_scale; - sss.push_constant.depth_scale = p_depth_scale; - - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sss.pipelines[p_quality - 1]); - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_diffuse), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_diffuse2), 1); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth), 2); - - RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant)); - - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1); - - RD::get_singleton()->compute_list_add_barrier(compute_list); - - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_diffuse2), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_diffuse), 1); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth), 2); - - sss.push_constant.vertical = true; - RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant)); - - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1); - - RD::get_singleton()->compute_list_end(); - } -} - 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."); @@ -377,27 +268,6 @@ void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) { } EffectsRD::EffectsRD(bool p_prefer_raster_effects) { - { - Vector<String> FSR_upscale_modes; - -#if defined(MACOS_ENABLED) || defined(IOS_ENABLED) - // MoltenVK does not support some of the operations used by the normal mode of FSR. Fallback works just fine though. - FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n"); -#else - // Everyone else can use normal mode when available. - if (RD::get_singleton()->has_feature(RD::SUPPORTS_FSR_HALF_FLOAT)) { - FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_NORMAL\n"); - } else { - FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n"); - } -#endif - - FSR_upscale.shader.initialize(FSR_upscale_modes); - - FSR_upscale.shader_version = FSR_upscale.shader.version_create(); - FSR_upscale.pipeline = RD::get_singleton()->compute_pipeline_create(FSR_upscale.shader.version_get_shader(FSR_upscale.shader_version, 0)); - } - prefer_raster_effects = p_prefer_raster_effects; if (prefer_raster_effects) { @@ -445,23 +315,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { roughness_limiter.pipeline = RD::get_singleton()->compute_pipeline_create(roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0)); } - if (!prefer_raster_effects) { - { - Vector<String> sss_modes; - sss_modes.push_back("\n#define USE_11_SAMPLES\n"); - sss_modes.push_back("\n#define USE_17_SAMPLES\n"); - sss_modes.push_back("\n#define USE_25_SAMPLES\n"); - - sss.shader.initialize(sss_modes); - - sss.shader_version = sss.shader.version_create(); - - for (int i = 0; i < sss_modes.size(); i++) { - sss.pipelines[i] = RD::get_singleton()->compute_pipeline_create(sss.shader.version_get_shader(sss.shader_version, i)); - } - } - } - { Vector<String> sort_modes; sort_modes.push_back("\n#define MODE_SORT_BLOCK\n"); @@ -477,14 +330,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) { } } - { - Vector<String> taa_modes; - taa_modes.push_back("\n#define MODE_TAA_RESOLVE"); - TAA_resolve.shader.initialize(taa_modes); - TAA_resolve.shader_version = TAA_resolve.shader.version_create(); - TAA_resolve.pipeline = RD::get_singleton()->compute_pipeline_create(TAA_resolve.shader.version_get_shader(TAA_resolve.shader_version, 0)); - } - RD::SamplerState sampler; sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; @@ -523,8 +368,6 @@ EffectsRD::~EffectsRD() { RD::get_singleton()->free(default_mipmap_sampler); RD::get_singleton()->free(index_buffer); //array gets freed as dependency - FSR_upscale.shader.version_free(FSR_upscale.shader_version); - TAA_resolve.shader.version_free(TAA_resolve.shader_version); if (prefer_raster_effects) { luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version); } else { @@ -532,7 +375,6 @@ EffectsRD::~EffectsRD() { } if (!prefer_raster_effects) { roughness_limiter.shader.version_free(roughness_limiter.shader_version); - sss.shader.version_free(sss.shader_version); } sort.shader.version_free(sort.shader_version); } diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index 94cd26fae9..b05af73cf3 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -33,13 +33,10 @@ #include "core/math/projection.h" #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" -#include "servers/rendering/renderer_rd/shaders/fsr_upscale.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/sort.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl.gen.h" -#include "servers/rendering/renderer_rd/shaders/taa_resolve.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" @@ -48,42 +45,6 @@ class EffectsRD { private: bool prefer_raster_effects; - enum FSRUpscalePass { - FSR_UPSCALE_PASS_EASU = 0, - FSR_UPSCALE_PASS_RCAS = 1 - }; - - struct FSRUpscalePushConstant { - float resolution_width; - float resolution_height; - float upscaled_width; - float upscaled_height; - float sharpness; - int pass; - int _unused0, _unused1; - }; - - struct FSRUpscale { - FSRUpscalePushConstant push_constant; - FsrUpscaleShaderRD shader; - RID shader_version; - RID pipeline; - } FSR_upscale; - - struct TAAResolvePushConstant { - float resolution_width; - float resolution_height; - float disocclusion_threshold; - float disocclusion_scale; - }; - - struct TAAResolve { - TAAResolvePushConstant push_constant; - TaaResolveShaderRD shader; - RID shader_version; - RID pipeline; - } TAA_resolve; - enum LuminanceReduceMode { LUMINANCE_REDUCE_READ, LUMINANCE_REDUCE, @@ -143,27 +104,6 @@ private: } roughness_limiter; - struct SubSurfaceScatteringPushConstant { - int32_t screen_size[2]; - float camera_z_far; - float camera_z_near; - - uint32_t vertical; - uint32_t orthogonal; - float unit_size; - float scale; - - float depth_scale; - uint32_t pad[3]; - }; - - struct SubSurfaceScattering { - SubSurfaceScatteringPushConstant push_constant; - SubsurfaceScatteringShaderRD shader; - RID shader_version; - RID pipelines[3]; //3 quality levels - } sss; - enum SortMode { SORT_MODE_BLOCK, SORT_MODE_STEP, @@ -230,16 +170,11 @@ private: public: bool get_prefer_raster_effects(); - void fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RID p_destination_texture, const Size2i &p_internal_size, const Size2i &p_size, float p_fsr_upscale_sharpness); - void taa_resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far); - void 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 = false); void luminance_reduction_raster(RID p_source_texture, const Size2i p_source_size, const Vector<RID> p_reduce, Vector<RID> p_fb, RID p_prev_luminance, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false); void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); - void sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality); - void sort_buffer(RID p_uniform_set, int p_size); EffectsRD(bool p_prefer_raster_effects); diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index f11057ad37..a41552cd5c 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -473,7 +473,7 @@ Fog::FogShaderData::~FogShaderData() { //////////////////////////////////////////////////////////////////////////////// // Volumetric Fog -Fog::VolumetricFog::VolumetricFog(const Vector3i &fog_size, RID p_sky_shader) { +void Fog::VolumetricFog::init(const Vector3i &fog_size, RID p_sky_shader) { width = fog_size.x; height = fog_size.y; depth = fog_size.z; @@ -591,6 +591,8 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RENDER_TIMESTAMP("> Volumetric Fog"); RD::get_singleton()->draw_command_begin_label("Volumetric Fog"); + Ref<VolumetricFog> fog = p_settings.vfog; + if (p_fog_volumes.size() > 0) { RD::get_singleton()->draw_command_begin_label("Render Volumetric Fog Volumes"); @@ -623,9 +625,9 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P params.z_far = z_far; params.time = p_settings.time; - params.fog_volume_size[0] = p_settings.vfog->width; - params.fog_volume_size[1] = p_settings.vfog->height; - params.fog_volume_size[2] = p_settings.vfog->depth; + params.fog_volume_size[0] = fog->width; + params.fog_volume_size[1] = fog->height; + params.fog_volume_size[2] = fog->depth; params.use_temporal_reprojection = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_temporal_reprojection(p_settings.env); params.temporal_frame = RSG::rasterizer->get_frame_number() % VolumetricFog::MAX_TEMPORAL_FRAMES; @@ -638,7 +640,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->buffer_update(volumetric_fog.volume_ubo, 0, sizeof(VolumetricFogShader::VolumeUBO), ¶ms, RD::BARRIER_MASK_COMPUTE); - if (p_settings.vfog->fog_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_settings.vfog->fog_uniform_set)) { + if (fog->fog_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->fog_uniform_set)) { Vector<RD::Uniform> uniforms; { @@ -649,7 +651,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 1; - u.append_id(p_settings.vfog->emissive_map); + u.append_id(fog->emissive_map); uniforms.push_back(u); } @@ -669,7 +671,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 3; - u.append_id(p_settings.vfog->density_map); + u.append_id(fog->density_map); uniforms.push_back(u); } @@ -681,11 +683,11 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 4; - u.append_id(p_settings.vfog->light_map); + u.append_id(fog->light_map); uniforms.push_back(u); } - p_settings.vfog->fog_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.default_shader_rd, VolumetricFogShader::FogSet::FOG_SET_UNIFORMS); + fog->fog_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.default_shader_rd, VolumetricFogShader::FogSet::FOG_SET_UNIFORMS); } RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); @@ -731,7 +733,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P if (volume_type != RS::FOG_VOLUME_SHAPE_WORLD) { // Local fog volume. Vector3i points[8]; - Vector3 fog_size = Vector3(p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + Vector3 fog_size = Vector3(fog->width, fog->height, fog->depth); float volumetric_fog_detail_spread = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_detail_spread(p_settings.env); points[0] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); points[1] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, extents.y, extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); @@ -742,7 +744,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P points[6] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(extents.x, -extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); points[7] = _point_get_position_in_froxel_volume(fog_volume_instance->transform.xform(Vector3(-extents.x, -extents.y, -extents.z)), fog_end, fog_near_size, fog_far_size, volumetric_fog_detail_spread, fog_size, p_cam_transform); - min = Vector3i(int32_t(p_settings.vfog->width) - 1, int32_t(p_settings.vfog->height) - 1, int32_t(p_settings.vfog->depth) - 1); + min = Vector3i(int32_t(fog->width) - 1, int32_t(fog->height) - 1, int32_t(fog->depth) - 1); max = Vector3i(1, 1, 1); for (int j = 0; j < 8; j++) { @@ -753,9 +755,9 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P kernel_size = max - min; } else { // Volume type global runs on all cells - extents = Vector3(p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + extents = Vector3(fog->width, fog->height, fog->depth); min = Vector3i(0, 0, 0); - kernel_size = Vector3i(int32_t(p_settings.vfog->width), int32_t(p_settings.vfog->height), int32_t(p_settings.vfog->depth)); + kernel_size = Vector3i(int32_t(fog->width), int32_t(fog->height), int32_t(fog->depth)); } if (kernel_size.x == 0 || kernel_size.y == 0 || kernel_size.z == 0) { @@ -777,7 +779,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shader_data->pipeline); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->fog_uniform_set, VolumetricFogShader::FogSet::FOG_SET_UNIFORMS); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->fog_uniform_set, VolumetricFogShader::FogSet::FOG_SET_UNIFORMS); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::FogPushConstant)); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, volumetric_fog.base_uniform_set, VolumetricFogShader::FogSet::FOG_SET_BASE); if (material->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material->uniform_set)) { // Material may not have a uniform set. @@ -795,7 +797,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_end(); } - if (p_settings.vfog->process_uniform_set_density.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_settings.vfog->process_uniform_set_density)) { + if (fog->process_uniform_set_density.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->process_uniform_set_density)) { //re create uniform set if needed Vector<RD::Uniform> uniforms; Vector<RD::Uniform> copy_uniforms; @@ -875,7 +877,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 8; - u.append_id(p_settings.vfog->light_density_map); + u.append_id(fog->light_density_map); uniforms.push_back(u); copy_uniforms.push_back(u); } @@ -884,7 +886,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 9; - u.append_id(p_settings.vfog->fog_map); + u.append_id(fog->fog_map); uniforms.push_back(u); } @@ -892,7 +894,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 9; - u.append_id(p_settings.vfog->prev_light_density_map); + u.append_id(fog->prev_light_density_map); copy_uniforms.push_back(u); } @@ -909,7 +911,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 11; - u.append_id(p_settings.voxel_gl_buffer); + u.append_id(p_settings.voxel_gi_buffer); uniforms.push_back(u); copy_uniforms.push_back(u); } @@ -944,7 +946,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 15; - u.append_id(p_settings.vfog->prev_light_density_map); + u.append_id(fog->prev_light_density_map); uniforms.push_back(u); } { @@ -955,7 +957,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 16; - u.append_id(p_settings.vfog->density_map); + u.append_id(fog->density_map); uniforms.push_back(u); } { @@ -966,7 +968,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 17; - u.append_id(p_settings.vfog->light_map); + u.append_id(fog->light_map); uniforms.push_back(u); } @@ -978,7 +980,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P u.uniform_type = RD::UNIFORM_TYPE_IMAGE; #endif u.binding = 18; - u.append_id(p_settings.vfog->emissive_map); + u.append_id(fog->emissive_map); uniforms.push_back(u); } @@ -992,9 +994,9 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P uniforms.push_back(u); } - p_settings.vfog->copy_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY), 0); + fog->copy_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY), 0); - p_settings.vfog->process_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0); + fog->process_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0); RID aux7 = uniforms.write[7].get_id(0); RID aux8 = uniforms.write[8].get_id(0); @@ -1002,17 +1004,17 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P uniforms.write[7].set_id(0, aux8); uniforms.write[8].set_id(0, aux7); - p_settings.vfog->process_uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0); + fog->process_uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG), 0); uniforms.remove_at(8); uniforms.write[7].set_id(0, aux7); - p_settings.vfog->process_uniform_set_density = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY), 0); + fog->process_uniform_set_density = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY), 0); } - bool using_sdfgi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_enabled(p_settings.env) && (p_settings.sdfgi != nullptr); + bool using_sdfgi = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_gi_inject(p_settings.env) > 0.0001 && RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_enabled(p_settings.env) && (p_settings.sdfgi.is_valid()); if (using_sdfgi) { - if (p_settings.vfog->sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_settings.vfog->sdfgi_uniform_set)) { + if (fog->sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->sdfgi_uniform_set)) { Vector<RD::Uniform> uniforms; { @@ -1039,12 +1041,12 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P uniforms.push_back(u); } - p_settings.vfog->sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI), 1); + fog->sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.process_shader.version_get_shader(volumetric_fog.process_shader_version, VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI), 1); } } - p_settings.vfog->length = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_length(p_settings.env); - p_settings.vfog->spread = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_detail_spread(p_settings.env); + fog->length = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_length(p_settings.env); + fog->spread = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_detail_spread(p_settings.env); VolumetricFogShader::ParamsUBO params; @@ -1079,9 +1081,9 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P params.ambient_color[2] = ambient_color.b; params.sky_contribution = RendererSceneRenderRD::get_singleton()->environment_get_ambient_sky_contribution(p_settings.env); - params.fog_volume_size[0] = p_settings.vfog->width; - params.fog_volume_size[1] = p_settings.vfog->height; - params.fog_volume_size[2] = p_settings.vfog->depth; + params.fog_volume_size[0] = fog->width; + params.fog_volume_size[1] = fog->height; + params.fog_volume_size[2] = fog->depth; params.directional_light_count = p_directional_light_count; @@ -1149,19 +1151,19 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[using_sdfgi ? VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI : VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->process_uniform_set_density, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set_density, 0); if (using_sdfgi) { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->sdfgi_uniform_set, 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->sdfgi_uniform_set, 1); } - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth); RD::get_singleton()->compute_list_add_barrier(compute_list); // Copy fog to history buffer if (RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_temporal_reprojection(p_settings.env)) { RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_COPY]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->copy_uniform_set, 0); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->copy_uniform_set, 0); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth); RD::get_singleton()->compute_list_add_barrier(compute_list); } RD::get_singleton()->draw_command_end_label(); @@ -1172,8 +1174,8 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RENDER_TIMESTAMP("Filter Fog"); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FILTER]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->process_uniform_set, 0); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set, 0); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth); RD::get_singleton()->compute_list_end(); //need restart for buffer update @@ -1183,8 +1185,8 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FILTER]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->process_uniform_set2, 0); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.vfog->width, p_settings.vfog->height, p_settings.vfog->depth); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set2, 0); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, fog->depth); RD::get_singleton()->compute_list_add_barrier(compute_list); RD::get_singleton()->draw_command_end_label(); @@ -1194,8 +1196,8 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->draw_command_begin_label("Integrate Fog"); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.process_pipelines[VolumetricFogShader::VOLUMETRIC_FOG_PROCESS_SHADER_FOG]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_settings.vfog->process_uniform_set, 0); - RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.vfog->width, p_settings.vfog->height, 1); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->process_uniform_set, 0); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, 1); RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_RASTER); diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h index b6958acfee..9ecd5699dc 100644 --- a/servers/rendering/renderer_rd/environment/fog.h +++ b/servers/rendering/renderer_rd/environment/fog.h @@ -38,8 +38,11 @@ #include "servers/rendering/renderer_rd/environment/gi.h" #include "servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h" #include "servers/rendering/storage/utilities.h" +#define RB_SCOPE_FOG SNAME("Fog") + namespace RendererRD { class Fog : public RendererFog { @@ -261,7 +264,10 @@ public: void fog_instance_free(RID p_rid); /* Volumetric FOG */ - struct VolumetricFog { + class VolumetricFog : public RenderBufferCustomDataRD { + GDCLASS(VolumetricFog, RenderBufferCustomDataRD) + + public: enum { MAX_TEMPORAL_FRAMES = 16 }; @@ -290,7 +296,10 @@ public: int last_shadow_filter = -1; - VolumetricFog(const Vector3i &fog_size, RID p_sky_shader); + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{}; + virtual void free_data() override{}; + + void init(const Vector3i &fog_size, RID p_sky_shader); ~VolumetricFog(); }; @@ -304,7 +313,7 @@ public: uint32_t max_cluster_elements; bool volumetric_fog_filter_active; RID shadow_sampler; - RID voxel_gl_buffer; + RID voxel_gi_buffer; RID shadow_atlas_depth; RID omni_light_buffer; RID spot_light_buffer; @@ -312,11 +321,11 @@ public: RID directional_light_buffer; // Objects related to our render buffer - VolumetricFog *vfog; + Ref<VolumetricFog> vfog; ClusterBuilderRD *cluster_builder; GI *gi; - GI::SDFGI *sdfgi; - GI::RenderBuffersGI *rbgi; + Ref<GI::SDFGI> sdfgi; + Ref<GI::RenderBuffersGI> rbgi; RID env; SkyRD *sky; }; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 66e984174c..ced0f6380f 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -34,6 +34,7 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" @@ -287,6 +288,19 @@ float GI::voxel_gi_get_energy(RID p_voxel_gi) const { return voxel_gi->energy; } +void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) { + VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); + ERR_FAIL_COND(!voxel_gi); + + voxel_gi->baked_exposure = p_baked_exposure; +} + +float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const { + VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); + ERR_FAIL_COND_V(!voxel_gi, 0); + return voxel_gi->baked_exposure; +} + void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_bias) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); @@ -371,6 +385,13 @@ RID GI::voxel_gi_get_sdf_texture(RID p_voxel_gi) { return voxel_gi->sdf_texture; } +Dependency *GI::voxel_gi_get_dependency(RID p_voxel_gi) const { + VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); + ERR_FAIL_COND_V(!voxel_gi, nullptr); + + return &voxel_gi->dependency; +} + //////////////////////////////////////////////////////////////////////////////// // SDFGI @@ -1108,7 +1129,11 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re reads_sky = RendererSceneRenderRD::get_singleton()->environment_get_sdfgi_read_sky_light(p_env); } -void GI::SDFGI::erase() { +void GI::SDFGI::free_data() { + // we don't free things here, we handle SDFGI differently at the moment destructing the object when it needs to change. +} + +GI::SDFGI::~SDFGI() { for (uint32_t i = 0; i < cascades.size(); i++) { const SDFGI::Cascade &c = cascades[i]; RD::get_singleton()->free(c.light_data); @@ -1292,7 +1317,7 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { push_constant.y_mult = y_mult; if (reads_sky && p_env.is_valid()) { - push_constant.sky_energy = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); + push_constant.sky_energy = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy_multiplier(p_env); if (RendererSceneRenderRD::get_singleton()->environment_get_background(p_env) == RS::ENV_BG_CLEAR_COLOR) { push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_COLOR; @@ -1840,6 +1865,11 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r c.probe_world_offset[2] = probe_ofs.z; c.to_cell = 1.0 / cascades[i].cell_size; + c.exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + c.exposure_normalization = exposure_normalization / cascades[i].baked_exposure_normalization; + } } RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data, RD::BARRIER_MASK_COMPUTE); @@ -1876,6 +1906,14 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r lights[idx].color[2] = color.b; lights[idx].type = RS::LIGHT_DIRECTIONAL; lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); idx++; @@ -1920,7 +1958,25 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; lights[idx].type = RSG::light_storage->light_get_type(li->light); + lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (lights[idx].type == RS::LIGHT_OMNI) { + lights[idx].energy *= 1.0 / (Math_PI * 4.0); + } else if (lights[idx].type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + lights[idx].energy *= 1.0 / Math_PI; + } + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); lights[idx].attenuation = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); @@ -1938,10 +1994,9 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } } -void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render) { +void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render, float p_exposure_normalization) { //print_line("rendering region " + itos(p_region)); - RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but... + ERR_FAIL_COND(p_render_buffers.is_null()); // we wouldn't be here if this failed but... AABB bounds; Vector3i from; Vector3i size; @@ -1960,7 +2015,7 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(cascades[cascade].cell_size)); - p_scene_render->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing); + p_scene_render->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing, p_exposure_normalization); if (cascade_next != cascade) { RD::get_singleton()->draw_command_begin_label("SDFGI Pre-Process Cascade"); @@ -1989,6 +2044,7 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } cascades[cascade].all_dynamic_lights_dirty = true; + cascades[cascade].baked_exposure_normalization = p_exposure_normalization; push_constant.grid_size = cascade_size; push_constant.cascade = cascade; @@ -2297,9 +2353,8 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } } -void GI::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) { - RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but... +void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) { + ERR_FAIL_COND(p_render_buffers.is_null()); // we wouldn't be here if this failed but... RD::get_singleton()->draw_command_begin_label("SDFGI Render Static Lights"); @@ -2358,7 +2413,25 @@ void GI::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_co lights[idx].color[0] = color.r; lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; + lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (lights[idx].type == RS::LIGHT_OMNI) { + lights[idx].energy *= 1.0 / (Math_PI * 4.0); + } else if (lights[idx].type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + lights[idx].energy *= 1.0 / Math_PI; + } + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); lights[idx].attenuation = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); @@ -2794,6 +2867,22 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID l.attenuation = RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_ATTENUATION); l.energy = RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + + if (p_scene_render->is_using_physical_light_units()) { + l.energy *= RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_INTENSITY); + + l.energy *= gi->voxel_gi_get_baked_exposure_normalization(probe); + + // Convert from Luminous Power to Luminous Intensity + if (l.type == RS::LIGHT_OMNI) { + l.energy *= 1.0 / (Math_PI * 4.0); + } else if (l.type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + l.energy *= 1.0 / Math_PI; + } + } + l.radius = to_cell.basis.xform(Vector3(RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_RANGE), 0, 0)).length(); Color color = RSG::light_storage->light_get_color(light).srgb_to_linear(); l.color[0] = color.r; @@ -3003,7 +3092,12 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID } p_scene_render->cull_argument[0] = instance; - p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size)); + float exposure_normalization = 1.0; + if (p_scene_render->is_using_physical_light_units()) { + exposure_normalization = gi->voxel_gi_get_baked_exposure_normalization(probe); + } + + p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size), exposure_normalization); VoxelGIDynamicPushConstant push_constant; memset(&push_constant, 0, sizeof(VoxelGIDynamicPushConstant)); @@ -3488,25 +3582,27 @@ void GI::free() { } } -GI::SDFGI *GI::create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) { - SDFGI *sdfgi = memnew(SDFGI); +Ref<GI::SDFGI> GI::create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) { + Ref<SDFGI> sdfgi; + sdfgi.instantiate(); sdfgi->create(p_env, p_world_position, p_requested_history_size, this); return sdfgi; } -void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) { +void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) { + ERR_FAIL_COND(p_render_buffers.is_null()); + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + ERR_FAIL_NULL(texture_storage); r_voxel_gi_instances_used = 0; - // feels a little dirty to use our container this way but.... - RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(rb == nullptr); - - RID voxel_gi_buffer = p_scene_render->render_buffers_get_voxel_gi_buffer(p_render_buffers); + Ref<RenderBuffersGI> rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI); + ERR_FAIL_COND(rbgi.is_null()); + RID voxel_gi_buffer = rbgi->get_voxel_gi_buffer(); VoxelGIData voxel_gi_data[MAX_VOXEL_GI_INSTANCES]; bool voxel_gi_instances_changed = false; @@ -3517,7 +3613,7 @@ void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_tra for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) { RID texture; if (i < (int)p_voxel_gi_instances.size()) { - VoxelGIInstance *gipi = get_probe_instance(p_voxel_gi_instances[i]); + VoxelGIInstance *gipi = voxel_gi_instance_owner.get_or_null(p_voxel_gi_instances[i]); if (gipi) { texture = gipi->texture; @@ -3555,6 +3651,11 @@ void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_tra gipd.normal_bias = voxel_gi_get_normal_bias(base_probe); gipd.blend_ambient = !voxel_gi_is_interior(base_probe); gipd.mipmaps = gipi->mipmaps.size(); + gipd.exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + gipd.exposure_normalization = exposure_normalization / voxel_gi_get_baked_exposure_normalization(base_probe); + } } r_voxel_gi_instances_used++; @@ -3564,28 +3665,30 @@ void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_tra texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); } - if (texture != rb->rbgi.voxel_gi_textures[i]) { + if (texture != rbgi->voxel_gi_textures[i]) { voxel_gi_instances_changed = true; - rb->rbgi.voxel_gi_textures[i] = texture; + rbgi->voxel_gi_textures[i] = texture; } } if (voxel_gi_instances_changed) { for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - if (RD::get_singleton()->uniform_set_is_valid(rb->rbgi.uniform_set[v])) { - RD::get_singleton()->free(rb->rbgi.uniform_set[v]); + if (RD::get_singleton()->uniform_set_is_valid(rbgi->uniform_set[v])) { + RD::get_singleton()->free(rbgi->uniform_set[v]); } - rb->rbgi.uniform_set[v] = RID(); + rbgi->uniform_set[v] = RID(); } - if (rb->volumetric_fog) { - if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->fog_uniform_set)) { - RD::get_singleton()->free(rb->volumetric_fog->fog_uniform_set); - RD::get_singleton()->free(rb->volumetric_fog->process_uniform_set); - RD::get_singleton()->free(rb->volumetric_fog->process_uniform_set2); + if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { + Ref<Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); + + if (RD::get_singleton()->uniform_set_is_valid(fog->fog_uniform_set)) { + RD::get_singleton()->free(fog->fog_uniform_set); + RD::get_singleton()->free(fog->process_uniform_set); + RD::get_singleton()->free(fog->process_uniform_set2); } - rb->volumetric_fog->fog_uniform_set = RID(); - rb->volumetric_fog->process_uniform_set = RID(); - rb->volumetric_fog->process_uniform_set2 = RID(); + fog->fog_uniform_set = RID(); + fog->process_uniform_set = RID(); + fog->process_uniform_set2 = RID(); } } @@ -3598,7 +3701,14 @@ void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_tra } } -void GI::RenderBuffersGI::free() { +RID GI::RenderBuffersGI::get_voxel_gi_buffer() { + if (voxel_gi_buffer.is_null()) { + voxel_gi_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::VoxelGIData) * GI::MAX_VOXEL_GI_INSTANCES); + } + return voxel_gi_buffer; +} + +void GI::RenderBuffersGI::free_data() { for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { if (RD::get_singleton()->uniform_set_is_valid(uniform_set[v])) { RD::get_singleton()->free(uniform_set[v]); @@ -3611,28 +3721,13 @@ void GI::RenderBuffersGI::free() { scene_data_ubo = RID(); } - if (ambient_buffer.is_valid()) { - RD::get_singleton()->free(ambient_buffer); - RD::get_singleton()->free(reflection_buffer); - ambient_buffer = RID(); - reflection_buffer = RID(); - - // these are automatically freed when we free the textures, so just reset.. - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - ambient_slice[v] = RID(); - reflection_slice[v] = RID(); - } - - view_count = 0; - } - if (voxel_gi_buffer.is_valid()) { RD::get_singleton()->free(voxel_gi_buffer); voxel_gi_buffer = RID(); } } -void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, const RID *p_vrs_slices, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render) { +void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); @@ -3640,65 +3735,38 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::get_singleton()->draw_command_begin_label("GI Render"); - RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(rb == nullptr); + ERR_FAIL_COND(p_render_buffers.is_null()); - if (rb->rbgi.ambient_buffer.is_null() || rb->rbgi.using_half_size_gi != half_resolution || rb->rbgi.view_count != p_view_count) { - // Free our old buffer if applicable - if (rb->rbgi.ambient_buffer.is_valid()) { - RD::get_singleton()->free(rb->rbgi.ambient_buffer); - RD::get_singleton()->free(rb->rbgi.reflection_buffer); + Ref<RenderBuffersGI> rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI); + ERR_FAIL_COND(rbgi.is_null()); - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - rb->rbgi.ambient_slice[v] = RID(); - rb->rbgi.reflection_slice[v] = RID(); - } - } + Size2i internal_size = p_render_buffers->get_internal_size(); + + if (rbgi->using_half_size_gi != half_resolution) { + p_render_buffers->clear_context(RB_SCOPE_GI); + } - // Remember the view count we're using - rb->rbgi.view_count = p_view_count; + if (!p_render_buffers->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) { + Size2i size = internal_size; + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - // Create textures for our ambient and reflection data - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - tf.width = rb->internal_width; - tf.height = rb->internal_height; if (half_resolution) { - tf.width >>= 1; - tf.height >>= 1; - } - if (p_view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - tf.array_layers = p_view_count; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; - } - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - rb->rbgi.ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); - RD::get_singleton()->set_resource_name(rb->rbgi.ambient_buffer, "GI Ambient Buffer"); - rb->rbgi.reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); - RD::get_singleton()->set_resource_name(rb->rbgi.reflection_buffer, "GI Reflection Buffer"); - rb->rbgi.using_half_size_gi = half_resolution; - - if (p_view_count == 1) { - // Just copy, we don't need to create slices - rb->rbgi.ambient_slice[0] = rb->rbgi.ambient_buffer; - rb->rbgi.reflection_slice[0] = rb->rbgi.reflection_buffer; - } else { - for (uint32_t v = 0; v < p_view_count; v++) { - rb->rbgi.ambient_slice[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->rbgi.ambient_buffer, v, 0); - rb->rbgi.reflection_slice[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->rbgi.reflection_buffer, v, 0); - } + size.x >>= 1; + size.y >>= 1; } + + p_render_buffers->create_texture(RB_SCOPE_GI, RB_TEX_AMBIENT, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, usage_bits, RD::TEXTURE_SAMPLES_1, size); + p_render_buffers->create_texture(RB_SCOPE_GI, RB_TEX_REFLECTION, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, usage_bits, RD::TEXTURE_SAMPLES_1, size); + + rbgi->using_half_size_gi = half_resolution; } // Setup our scene data { SceneData scene_data; - if (rb->rbgi.scene_data_ubo.is_null()) { - rb->rbgi.scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SceneData)); + if (rbgi->scene_data_ubo.is_null()) { + rbgi->scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SceneData)); } for (uint32_t v = 0; v < p_view_count; v++) { @@ -3712,10 +3780,10 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, // Note that we will be ignoring the origin of this transform. RendererRD::MaterialStorage::store_transform(p_cam_transform, scene_data.cam_transform); - scene_data.screen_size[0] = rb->internal_width; - scene_data.screen_size[1] = rb->internal_height; + scene_data.screen_size[0] = internal_size.x; + scene_data.screen_size[1] = internal_size.y; - RD::get_singleton()->buffer_update(rb->rbgi.scene_data_ubo, 0, sizeof(SceneData), &scene_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(rbgi->scene_data_ubo, 0, sizeof(SceneData), &scene_data, RD::BARRIER_MASK_COMPUTE); } // Now compute the contents of our buffers. @@ -3737,22 +3805,28 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, push_constant.z_far = p_projections[0].get_z_far(); // these are only used if we have 1 view, else we use the projections in our scene data - push_constant.proj_info[0] = -2.0f / (rb->internal_width * p_projections[0].matrix[0][0]); - push_constant.proj_info[1] = -2.0f / (rb->internal_height * p_projections[0].matrix[1][1]); + push_constant.proj_info[0] = -2.0f / (internal_size.x * p_projections[0].matrix[0][0]); + push_constant.proj_info[1] = -2.0f / (internal_size.y * p_projections[0].matrix[1][1]); push_constant.proj_info[2] = (1.0f - p_projections[0].matrix[0][2]) / p_projections[0].matrix[0][0]; push_constant.proj_info[3] = (1.0f + p_projections[0].matrix[1][2]) / p_projections[0].matrix[1][1]; - bool use_sdfgi = rb->sdfgi != nullptr; + bool use_sdfgi = p_render_buffers->has_custom_data(RB_SCOPE_SDFGI); bool use_voxel_gi_instances = push_constant.max_voxel_gi_instances > 0; + Ref<SDFGI> sdfgi; + if (use_sdfgi) { + sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); + } + uint32_t pipeline_specialization = 0; - if (rb->rbgi.using_half_size_gi) { + if (rbgi->using_half_size_gi) { pipeline_specialization |= SHADER_SPECIALIZATION_HALF_RES; } if (p_view_count > 1) { pipeline_specialization |= SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX; } - if (p_vrs_slices[0].is_valid()) { + bool has_vrs_texture = p_render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE); + if (has_vrs_texture) { pipeline_specialization |= SHADER_SPECIALIZATION_USE_VRS; } @@ -3762,15 +3836,15 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, push_constant.view_index = v; // setup our uniform set - if (rb->rbgi.uniform_set[v].is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->rbgi.uniform_set[v])) { + if (rbgi->uniform_set[v].is_null() || !RD::get_singleton()->uniform_set_is_valid(rbgi->uniform_set[v])) { Vector<RD::Uniform> uniforms; { RD::Uniform u; u.binding = 1; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { - u.append_id(rb->sdfgi->cascades[j].sdf_tex); + if (use_sdfgi && j < sdfgi->cascades.size()) { + u.append_id(sdfgi->cascades[j].sdf_tex); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); } @@ -3782,8 +3856,8 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { - u.append_id(rb->sdfgi->cascades[j].light_tex); + if (use_sdfgi && j < sdfgi->cascades.size()) { + u.append_id(sdfgi->cascades[j].light_tex); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); } @@ -3795,8 +3869,8 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, u.binding = 3; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { - u.append_id(rb->sdfgi->cascades[j].light_aniso_0_tex); + if (use_sdfgi && j < sdfgi->cascades.size()) { + u.append_id(sdfgi->cascades[j].light_aniso_0_tex); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); } @@ -3808,8 +3882,8 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, u.binding = 4; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { - if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { - u.append_id(rb->sdfgi->cascades[j].light_aniso_1_tex); + if (use_sdfgi && j < sdfgi->cascades.size()) { + u.append_id(sdfgi->cascades[j].light_aniso_1_tex); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); } @@ -3820,8 +3894,8 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 5; - if (rb->sdfgi) { - u.append_id(rb->sdfgi->occlusion_texture); + if (use_sdfgi) { + u.append_id(sdfgi->occlusion_texture); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); } @@ -3846,7 +3920,7 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 9; - u.append_id(rb->rbgi.ambient_slice[v]); + u.append_id(p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_AMBIENT, v, 0)); uniforms.push_back(u); } @@ -3854,7 +3928,7 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 10; - u.append_id(rb->rbgi.reflection_slice[v]); + u.append_id(p_render_buffers->get_texture_slice(RB_SCOPE_GI, RB_TEX_REFLECTION, v, 0)); uniforms.push_back(u); } @@ -3862,8 +3936,8 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 11; - if (rb->sdfgi) { - u.append_id(rb->sdfgi->lightprobe_texture); + if (use_sdfgi) { + u.append_id(sdfgi->lightprobe_texture); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE)); } @@ -3873,7 +3947,7 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 12; - u.append_id(rb->views[v].view_depth); + u.append_id(p_render_buffers->get_depth_texture(v)); uniforms.push_back(u); } { @@ -3902,7 +3976,7 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 16; - u.append_id(rb->rbgi.voxel_gi_buffer); + u.append_id(rbgi->get_voxel_gi_buffer()); uniforms.push_back(u); } { @@ -3910,7 +3984,7 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 17; for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) { - u.append_id(rb->rbgi.voxel_gi_textures[i]); + u.append_id(rbgi->voxel_gi_textures[i]); } uniforms.push_back(u); } @@ -3918,29 +3992,29 @@ void GI::process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 18; - u.append_id(rb->rbgi.scene_data_ubo); + u.append_id(rbgi->scene_data_ubo); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 19; - RID buffer = p_vrs_slices[v].is_valid() ? p_vrs_slices[v] : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_VRS); + RID buffer = has_vrs_texture ? p_render_buffers->get_texture_slice(RB_SCOPE_VRS, RB_TEXTURE, v, 0) : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_VRS); u.append_id(buffer); uniforms.push_back(u); } - rb->rbgi.uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0); + rbgi->uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0); } RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[pipeline_specialization][mode]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->rbgi.uniform_set[v], 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rbgi->uniform_set[v], 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); - if (rb->rbgi.using_half_size_gi) { - RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width >> 1, rb->internal_height >> 1, 1); + if (rbgi->using_half_size_gi) { + RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.x >> 1, internal_size.y >> 1, 1); } else { - RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width, rb->internal_height, 1); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.x, internal_size.y, 1); } } @@ -3964,21 +4038,21 @@ void GI::voxel_gi_instance_free(RID p_rid) { } void GI::voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) { - VoxelGIInstance *voxel_gi = get_probe_instance(p_probe); + VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe); ERR_FAIL_COND(!voxel_gi); voxel_gi->transform = p_xform; } bool GI::voxel_gi_needs_update(RID p_probe) const { - VoxelGIInstance *voxel_gi = get_probe_instance(p_probe); + VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!voxel_gi, false); return voxel_gi->last_probe_version != voxel_gi_get_version(voxel_gi->probe); } void GI::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) { - VoxelGIInstance *voxel_gi = get_probe_instance(p_probe); + VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe); ERR_FAIL_COND(!voxel_gi); voxel_gi->update(p_update_light_instances, p_light_instances, p_dynamic_objects, p_scene_render); diff --git a/servers/rendering/renderer_rd/environment/gi.h b/servers/rendering/renderer_rd/environment/gi.h index 8860445c3b..e567c67a3b 100644 --- a/servers/rendering/renderer_rd/environment/gi.h +++ b/servers/rendering/renderer_rd/environment/gi.h @@ -44,10 +44,17 @@ #include "servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering/rendering_device.h" #include "servers/rendering/storage/utilities.h" +#define RB_SCOPE_GI SNAME("rbgi") +#define RB_SCOPE_SDFGI SNAME("sdfgi") + +#define RB_TEX_AMBIENT SNAME("ambient") +#define RB_TEX_REFLECTION SNAME("reflection") + // Forward declare RenderDataRD and RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound struct RenderDataRD; class RendererSceneRenderRD; @@ -76,6 +83,7 @@ public: float dynamic_range = 2.0; float energy = 1.0; + float baked_exposure = 1.0; float bias = 1.4; float normal_bias = 0.0; float propagation = 0.5; @@ -88,6 +96,60 @@ public: Dependency dependency; }; + /* VOXEL_GI INSTANCE */ + + //@TODO VoxelGIInstance is still directly used in the render code, we'll address this when we refactor the render code itself. + + struct VoxelGIInstance { + // access to our containers + GI *gi = nullptr; + + RID probe; + RID texture; + RID write_buffer; + + struct Mipmap { + RID texture; + RID uniform_set; + RID second_bounce_uniform_set; + RID write_uniform_set; + uint32_t level; + uint32_t cell_offset; + uint32_t cell_count; + }; + Vector<Mipmap> mipmaps; + + struct DynamicMap { + RID texture; //color normally, or emission on first pass + RID fb_depth; //actual depth buffer for the first pass, float depth for later passes + RID depth; //actual depth buffer for the first pass, float depth for later passes + RID normal; //normal buffer for the first pass + RID albedo; //emission buffer for the first pass + RID orm; //orm buffer for the first pass + RID fb; //used for rendering, only valid on first map + RID uniform_set; + uint32_t size; + int mipmap; // mipmap to write to, -1 if no mipmap assigned + }; + + Vector<DynamicMap> dynamic_maps; + + int slot = -1; + uint32_t last_probe_version = 0; + uint32_t last_probe_data_version = 0; + + //uint64_t last_pass = 0; + uint32_t render_index = 0; + + bool has_dynamic_object_data = false; + + Transform3D transform; + + void update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render); + void debug(RD::DrawListID p_draw_list, RID p_framebuffer, const Projection &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha); + void free_resources(); + }; + private: static GI *singleton; @@ -97,6 +159,8 @@ private: /* VOXEL_GI INSTANCE */ + mutable RID_Owner<VoxelGIInstance> voxel_gi_instance_owner; + struct VoxelGILight { uint32_t type; float energy; @@ -369,9 +433,40 @@ private: public: static GI *get_singleton() { return singleton; } + /* GI */ + + enum { + MAX_VOXEL_GI_INSTANCES = 8 + }; + + // Struct for use in render buffer + class RenderBuffersGI : public RenderBufferCustomDataRD { + GDCLASS(RenderBuffersGI, RenderBufferCustomDataRD) + + private: + RID voxel_gi_buffer; + + public: + RID voxel_gi_textures[MAX_VOXEL_GI_INSTANCES]; + + RID full_buffer; + RID full_dispatch; + RID full_mask; + + /* GI buffers */ + bool using_half_size_gi = false; + + RID uniform_set[RendererSceneRender::MAX_RENDER_VIEWS]; + RID scene_data_ubo; + + RID get_voxel_gi_buffer(); + + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{}; + virtual void free_data() override; + }; + /* VOXEL GI API */ - VoxelGI *get_voxel_gi(RID p_rid) { return voxel_gi_owner.get_or_null(p_rid); }; bool owns_voxel_gi(RID p_rid) { return voxel_gi_owner.owns(p_rid); }; virtual RID voxel_gi_allocate() override; @@ -398,6 +493,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) override; virtual float voxel_gi_get_energy(RID p_voxel_gi) const override; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) override; virtual float voxel_gi_get_bias(RID p_voxel_gi) const override; @@ -418,72 +516,23 @@ public: RID voxel_gi_get_sdf_texture(RID p_voxel_gi); - /* VOXEL_GI INSTANCE */ + Dependency *voxel_gi_get_dependency(RID p_voxel_gi) const; - //@TODO VoxelGIInstance is still directly used in the render code, we'll address this when we refactor the render code itself. - - struct VoxelGIInstance { - // access to our containers - GI *gi = nullptr; - - RID probe; - RID texture; - RID write_buffer; - - struct Mipmap { - RID texture; - RID uniform_set; - RID second_bounce_uniform_set; - RID write_uniform_set; - uint32_t level; - uint32_t cell_offset; - uint32_t cell_count; - }; - Vector<Mipmap> mipmaps; - - struct DynamicMap { - RID texture; //color normally, or emission on first pass - RID fb_depth; //actual depth buffer for the first pass, float depth for later passes - RID depth; //actual depth buffer for the first pass, float depth for later passes - RID normal; //normal buffer for the first pass - RID albedo; //emission buffer for the first pass - RID orm; //orm buffer for the first pass - RID fb; //used for rendering, only valid on first map - RID uniform_set; - uint32_t size; - int mipmap; // mipmap to write to, -1 if no mipmap assigned - }; - - Vector<DynamicMap> dynamic_maps; - - int slot = -1; - uint32_t last_probe_version = 0; - uint32_t last_probe_data_version = 0; - - //uint64_t last_pass = 0; - uint32_t render_index = 0; - - bool has_dynamic_object_data = false; - - Transform3D transform; - - void update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render); - void debug(RD::DrawListID p_draw_list, RID p_framebuffer, const Projection &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha); - void free_resources(); - }; - - mutable RID_Owner<VoxelGIInstance> voxel_gi_instance_owner; - - _FORCE_INLINE_ VoxelGIInstance *get_probe_instance(RID p_probe) const { - return voxel_gi_instance_owner.get_or_null(p_probe); - }; + /* VOXEL_GI INSTANCE */ _FORCE_INLINE_ RID voxel_gi_instance_get_texture(RID p_probe) { - VoxelGIInstance *voxel_gi = get_probe_instance(p_probe); + VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!voxel_gi, RID()); return voxel_gi->texture; }; + _FORCE_INLINE_ void voxel_gi_instance_set_render_index(RID p_probe, uint32_t p_index) { + VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe); + ERR_FAIL_NULL(voxel_gi); + + voxel_gi->render_index = p_index; + }; + bool voxel_gi_instance_owns(RID p_rid) const { return voxel_gi_instance_owner.owns(p_rid); } @@ -494,7 +543,10 @@ public: /* SDFGI */ - struct SDFGI { + class SDFGI : public RenderBufferCustomDataRD { + GDCLASS(SDFGI, RenderBufferCustomDataRD) + + public: enum { MAX_CASCADES = 8, CASCADE_SIZE = 128, @@ -512,6 +564,7 @@ public: float to_cell; int32_t probe_offset[3]; uint32_t pad; + float pad2[4]; }; //cascade blocks are full-size for volume (128^3), half size for albedo/emission @@ -551,6 +604,8 @@ public: RID integrate_uniform_set; RID lights_buffer; + float baked_exposure_normalization = 1.0; + bool all_dynamic_lights_dirty = true; }; @@ -617,8 +672,11 @@ public: int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically RID integrate_sky_uniform_set; + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{}; + virtual void free_data() override; + ~SDFGI(); + void create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi); - void erase(); void update(RID p_env, const Vector3 &p_world_position); void update_light(); void update_probes(RID p_env, RendererRD::SkyRD::Sky *p_sky); @@ -630,8 +688,8 @@ public: void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); void pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data, RendererSceneRenderRD *p_scene_render); - void render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render); - void render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render); + void render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render, float p_exposure_normalization); + void render_static_lights(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render); }; RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16; @@ -648,34 +706,6 @@ public: int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; } - /* GI */ - enum { - MAX_VOXEL_GI_INSTANCES = 8 - }; - - // Struct for use in render buffer - struct RenderBuffersGI { - RID voxel_gi_textures[MAX_VOXEL_GI_INSTANCES]; - RID voxel_gi_buffer; - - RID full_buffer; - RID full_dispatch; - RID full_mask; - - /* GI buffers */ - RID ambient_buffer; - RID ambient_slice[RendererSceneRender::MAX_RENDER_VIEWS]; - RID reflection_buffer; - RID reflection_slice[RendererSceneRender::MAX_RENDER_VIEWS]; - bool using_half_size_gi = false; - uint32_t view_count = 1; - - RID uniform_set[RendererSceneRender::MAX_RENDER_VIEWS]; - RID scene_data_ubo; - - void free(); - }; - struct SDFGIData { float grid_size[3]; uint32_t max_cascades; @@ -705,6 +735,8 @@ public: float to_probe; // 1/bounds * grid_size int32_t probe_world_offset[3]; float to_cell; // 1/bounds * grid_size + float pad[3]; + float exposure_normalization; }; ProbeCascadeData cascades[SDFGI::MAX_CASCADES]; @@ -720,6 +752,9 @@ public: float normal_bias; // 4 - 88 uint32_t blend_ambient; // 4 - 92 uint32_t mipmaps; // 4 - 96 + + float pad[3]; // 12 - 108 + float exposure_normalization; // 4 - 112 }; struct SceneData { @@ -775,10 +810,10 @@ public: void init(RendererRD::SkyRD *p_sky); void free(); - SDFGI *create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size); + Ref<SDFGI> create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size); - void setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render); - void process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, const RID *p_vrs_slices, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render); + void setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render); + void process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances); RID voxel_gi_instance_create(RID p_base); void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 21a8858c0c..65d1d9e705 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -35,6 +35,7 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" #include "servers/rendering/rendering_server_globals.h" @@ -292,7 +293,7 @@ static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_ar p_array[11] = 0; } -void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier) { +void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier) { SkyPushConstant sky_push_constant; memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); @@ -307,7 +308,6 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC sky_push_constant.position[0] = p_position.x; sky_push_constant.position[1] = p_position.y; sky_push_constant.position[2] = p_position.z; - sky_push_constant.multiplier = p_multiplier; sky_push_constant.time = p_time; sky_push_constant.luminance_multiplier = p_luminance_multiplier; store_transform_3x3(p_orientation, sky_push_constant.orientation); @@ -762,7 +762,7 @@ Ref<Image> SkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, con RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; // Could be RGBA16 tf.width = p_size.width; tf.height = p_size.height; tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; @@ -868,7 +868,7 @@ void SkyRD::init() { actions.renames["COLOR"] = "color"; actions.renames["ALPHA"] = "alpha"; actions.renames["EYEDIR"] = "cube_normal"; - actions.renames["POSITION"] = "params.position_multiplier.xyz"; + actions.renames["POSITION"] = "params.position"; actions.renames["SKY_COORDS"] = "panorama_coords"; actions.renames["SCREEN_UV"] = "uv"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; @@ -1110,7 +1110,7 @@ SkyRD::~SkyRD() { RD::get_singleton()->free(index_buffer); //array gets freed as dependency } -void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { +void SkyRD::setup(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -1220,6 +1220,14 @@ void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_ligh float sign = light_storage->light_is_negative(base) ? -1 : 1; sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_camera_attributes.is_valid()) { + sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes); + } + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); sky_light_data.color[0] = linear_col.r; sky_light_data.color[1] = linear_col.g; @@ -1287,24 +1295,25 @@ void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_ligh //setup fog variables sky_scene_state.ubo.volumetric_fog_enabled = false; if (p_render_buffers.is_valid()) { - if (p_scene_render->render_buffers_has_volumetric_fog(p_render_buffers)) { + if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { + Ref<RendererRD::Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); sky_scene_state.ubo.volumetric_fog_enabled = true; - float fog_end = p_scene_render->render_buffers_get_volumetric_fog_end(p_render_buffers); + float fog_end = fog->length; if (fog_end > 0.0) { sky_scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end; } else { sky_scene_state.ubo.volumetric_fog_inv_length = 1.0; } - float fog_detail_spread = p_scene_render->render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup + float fog_detail_spread = fog->spread; //reverse lookup if (fog_detail_spread > 0.0) { sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread; } else { sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0; } - sky_scene_state.fog_uniform_set = p_scene_render->render_buffers_get_volumetric_fog_sky_uniform_set(p_render_buffers); + sky_scene_state.fog_uniform_set = fog->sky_uniform_set; } } @@ -1319,6 +1328,9 @@ void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_ligh sky_scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy; sky_scene_state.ubo.fog_sun_scatter = RendererSceneRenderRD::get_singleton()->environment_get_fog_sun_scatter(p_env); + sky_scene_state.ubo.fog_sky_affect = RendererSceneRenderRD::get_singleton()->environment_get_fog_sky_affect(p_env); + sky_scene_state.ubo.volumetric_fog_sky_affect = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_sky_affect(p_env); + RD::get_singleton()->buffer_update(sky_scene_state.uniform_buffer, 0, sizeof(SkySceneState::UBO), &sky_scene_state.ubo); } @@ -1351,8 +1363,6 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D ERR_FAIL_COND(!shader_data); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); - bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY; RS::SkyMode sky_mode = sky->mode; @@ -1415,7 +1425,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1434,7 +1444,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1449,7 +1459,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1475,7 +1485,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D } sky->processing_layer = 1; } - + sky->baked_exposure = p_luminance_multiplier; sky->reflection.dirty = false; } else { @@ -1491,7 +1501,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D } } -void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time) { +void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -1536,7 +1546,6 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1567,7 +1576,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth 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, 1.0); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1580,7 +1589,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth 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, 1.0); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1594,7 +1603,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); - _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); + _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1634,7 +1643,6 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1665,7 +1673,7 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio 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, p_luminance_multiplier); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1678,7 +1686,7 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio 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, p_luminance_multiplier); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } } @@ -1728,7 +1736,6 @@ void SkyRD::draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_vie Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1759,7 +1766,7 @@ void SkyRD::draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_vie 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, p_luminance_multiplier); + _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); } void SkyRD::invalidate_sky(Sky *p_sky) { @@ -1881,6 +1888,13 @@ RID SkyRD::sky_get_material(RID p_sky) const { return sky->material; } +float SkyRD::sky_get_baked_exposure(RID p_sky) const { + Sky *sky = get_sky(p_sky); + ERR_FAIL_COND_V(!sky, 1.0); + + return sky->baked_exposure; +} + RID SkyRD::allocate_sky_rid() { return sky_owner.allocate_rid(); } diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h index 1edecf52e6..45c4f9bda7 100644 --- a/servers/rendering/renderer_rd/environment/sky.h +++ b/servers/rendering/renderer_rd/environment/sky.h @@ -42,6 +42,7 @@ // Forward declare RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound class RendererSceneRenderRD; +class RenderSceneBuffersRD; namespace RendererRD { @@ -100,10 +101,9 @@ private: float orientation[12]; // 48 - 48 float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 80 float position[3]; // 12 - 92 - float multiplier; // 4 - 96 - float time; // 4 - 100 - float luminance_multiplier; // 4 - 104 - float pad[2]; // 8 - 112 // Using pad to align on 16 bytes + float time; // 4 - 96 + float pad[3]; // 12 - 108 + float luminance_multiplier; // 4 - 112 // 128 is the max size of a push constant. We can replace "pad" but we can't add any more. }; @@ -143,25 +143,28 @@ private: virtual ~SkyShaderData(); }; - void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier); + void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier); public: struct SkySceneState { struct UBO { - uint32_t volumetric_fog_enabled; - float volumetric_fog_inv_length; - float volumetric_fog_detail_spread; - - float fog_aerial_perspective; - - float fog_light_color[3]; - float fog_sun_scatter; - - uint32_t fog_enabled; - float fog_density; - - float z_far; - uint32_t directional_light_count; + uint32_t volumetric_fog_enabled; // 4 - 4 + float volumetric_fog_inv_length; // 4 - 8 + float volumetric_fog_detail_spread; // 4 - 12 + float volumetric_fog_sky_affect; // 4 - 16 + + uint32_t fog_enabled; // 4 - 20 + float fog_sky_affect; // 4 - 24 + float fog_density; // 4 - 28 + float fog_sun_scatter; // 4 - 32 + + float fog_light_color[3]; // 12 - 44 + float fog_aerial_perspective; // 4 - 48 + + float z_far; // 4 - 52 + uint32_t directional_light_count; // 4 - 56 + uint32_t pad1; // 4 - 60 + uint32_t pad2; // 4 - 64 }; UBO ubo; @@ -264,6 +267,7 @@ public: bool dirty = false; int processing_layer = 0; Sky *dirty_list = nullptr; + float baked_exposure = 1.0; //State to track when radiance cubemap needs updating SkyMaterialData *prev_material = nullptr; @@ -296,9 +300,9 @@ public: void set_texture_format(RD::DataFormat p_texture_format); ~SkyRD(); - void setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); + void setup(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void update(RID p_env, const Projection &p_projection, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); - void draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time); // only called by clustered renderer + void draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); // only called by clustered renderer void update_res_buffers(RID p_env, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); void draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); @@ -306,6 +310,8 @@ public: void update_dirty_skys(); RID sky_get_material(RID p_sky) const; + RID sky_get_radiance_texture_rd(RID p_sky) const; + float sky_get_baked_exposure(RID p_sky) const; RID allocate_sky_rid(); void initialize_sky_rid(RID p_rid); @@ -315,8 +321,6 @@ public: void sky_set_mode(RID p_sky, RS::SkyMode p_mode); void sky_set_material(RID p_sky, RID p_material); Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size); - - RID sky_get_radiance_texture_rd(RID p_sky) const; }; } // namespace RendererRD 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 8754e90647..a0f6e69fd5 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -30,6 +30,7 @@ #include "render_forward_clustered.h" #include "core/config/project_settings.h" +#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h" #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/storage_rd/light_storage.h" #include "servers/rendering/renderer_rd/storage_rd/mesh_storage.h" @@ -41,286 +42,93 @@ using namespace RendererSceneRenderImplementation; -RenderForwardClustered::RenderBufferDataForwardClustered::~RenderBufferDataForwardClustered() { - clear(); -} - void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular() { - if (!specular.is_valid()) { - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - if (view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - tf.array_layers = view_count; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; - } - tf.width = width; - tf.height = height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - if (msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - } + ERR_FAIL_NULL(render_buffers); - specular = RD::get_singleton()->texture_create(tf, RD::TextureView()); - if (view_count == 1) { - specular_views[0] = specular; + if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR)) { + RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; } else { - for (uint32_t v = 0; v < view_count; v++) { - specular_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), specular, v, 0); - } + usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; } - if (msaa == RS::VIEWPORT_MSAA_DISABLED) { - { - Vector<RID> fb; - fb.push_back(specular); - - specular_only_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count); - } - - } else { - tf.samples = texture_samples; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - specular_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - if (view_count == 1) { - specular_msaa_views[0] = specular_msaa; - } else { - for (uint32_t v = 0; v < view_count; v++) { - specular_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), specular_msaa, v, 0); - } - } + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, format, usage_bits); - { - Vector<RID> fb; - fb.push_back(specular_msaa); - - specular_only_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count); - } + if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, format, usage_bits, texture_samples); } } } -void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_velocity() { - if (!velocity_buffer.is_valid()) { - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R16G16_SFLOAT; - tf.width = width; - tf.height = height; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - - if (msaa != RS::VIEWPORT_MSAA_DISABLED) { - RD::TextureFormat tf_aa = tf; - tf_aa.samples = texture_samples; - tf_aa.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - velocity_buffer_msaa = RD::get_singleton()->texture_create(tf_aa, RD::TextureView()); - - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - } +void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_normal_roughness_texture() { + ERR_FAIL_NULL(render_buffers); - velocity_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); - } -} + if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS)) { + RD::DataFormat format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; -void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi() { - if (!voxelgi_buffer.is_valid()) { - RD::TextureFormat tf; - if (view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - tf.array_layers = view_count; + if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; - } - tf.format = RD::DATA_FORMAT_R8G8_UINT; - tf.width = width; - tf.height = height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; - - if (msaa != RS::VIEWPORT_MSAA_DISABLED) { - RD::TextureFormat tf_aa = tf; - tf_aa.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - tf_aa.samples = texture_samples; - voxelgi_buffer_msaa = RD::get_singleton()->texture_create(tf_aa, RD::TextureView()); - - if (view_count == 1) { - voxelgi_msaa_views[0] = voxelgi_buffer_msaa; - } else { - for (uint32_t v = 0; v < view_count; v++) { - voxelgi_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), voxelgi_buffer_msaa, v, 0); - } - } - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; } - tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; - - voxelgi_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS, format, usage_bits); - if (view_count == 1) { - voxelgi_views[0] = voxelgi_buffer; - } else { - for (uint32_t v = 0; v < view_count; v++) { - voxelgi_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), voxelgi_buffer, v, 0); - } - } - - Vector<RID> fb; - if (msaa != RS::VIEWPORT_MSAA_DISABLED) { - fb.push_back(depth_msaa); - fb.push_back(normal_roughness_buffer_msaa); - fb.push_back(voxelgi_buffer_msaa); - } else { - fb.push_back(depth); - fb.push_back(normal_roughness_buffer); - fb.push_back(voxelgi_buffer); + if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS_MSAA, format, usage_bits, texture_samples); } - - depth_normal_roughness_voxelgi_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count); } } -void RenderForwardClustered::RenderBufferDataForwardClustered::clear() { - // note, slices are freed automatically when the parent texture is freed so we just clear them. - for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) { - color_views[v] = RID(); - depth_views[v] = RID(); - specular_views[v] = RID(); - specular_msaa_views[v] = RID(); - color_msaa_views[v] = RID(); - depth_msaa_views[v] = RID(); - normal_roughness_views[v] = RID(); - normal_roughness_msaa_views[v] = RID(); - voxelgi_views[v] = RID(); - voxelgi_msaa_views[v] = RID(); - vrs_views[v] = RID(); - } - - if (voxelgi_buffer != RID()) { - RD::get_singleton()->free(voxelgi_buffer); - voxelgi_buffer = RID(); +void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi() { + ERR_FAIL_NULL(render_buffers); - if (voxelgi_buffer_msaa.is_valid()) { - RD::get_singleton()->free(voxelgi_buffer_msaa); - voxelgi_buffer_msaa = RID(); + if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI)) { + RD::DataFormat format = RD::DATA_FORMAT_R8G8_UINT; + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + if (render_buffers->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED) { + usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; } - depth_normal_roughness_voxelgi_fb = RID(); - } - - if (color_msaa.is_valid()) { - RD::get_singleton()->free(color_msaa); - color_msaa = RID(); - } - - if (depth_msaa.is_valid()) { - RD::get_singleton()->free(depth_msaa); - depth_msaa = RID(); - } + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, format, usage_bits); - if (specular.is_valid()) { - if (specular_msaa.is_valid()) { - RD::get_singleton()->free(specular_msaa); - specular_msaa = RID(); + if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, format, usage_bits, texture_samples); } - RD::get_singleton()->free(specular); - specular = RID(); } +} - color = RID(); - color_only_fb = RID(); - depth = RID(); - depth_fb = RID(); - - color_framebuffers.clear(); // Color pass framebuffers are freed automatically by their dependency relations - - if (normal_roughness_buffer.is_valid()) { - RD::get_singleton()->free(normal_roughness_buffer); - normal_roughness_buffer = RID(); - - if (normal_roughness_buffer_msaa.is_valid()) { - RD::get_singleton()->free(normal_roughness_buffer_msaa); - normal_roughness_buffer_msaa = RID(); - } - - depth_normal_roughness_fb = RID(); +void RenderForwardClustered::RenderBufferDataForwardClustered::free_data() { + // JIC, should already have been cleared + if (render_buffers) { + render_buffers->clear_context(RB_SCOPE_FORWARD_CLUSTERED); } if (!render_sdfgi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_sdfgi_uniform_set)) { RD::get_singleton()->free(render_sdfgi_uniform_set); } - - if (velocity_buffer != RID()) { - RD::get_singleton()->free(velocity_buffer); - velocity_buffer = RID(); - } - - if (velocity_buffer_msaa != RID()) { - RD::get_singleton()->free(velocity_buffer_msaa); - velocity_buffer_msaa = RID(); - } } -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, bool p_use_taa, uint32_t p_view_count, RID p_vrs_texture) { - clear(); - - msaa = p_msaa; - use_taa = p_use_taa; - vrs = p_vrs_texture; - - width = p_width; - height = p_height; - view_count = p_view_count; - - color = p_color_buffer; - depth = p_depth_buffer; - - if (vrs.is_valid()) { - if (view_count == 1) { - // just reuse - vrs_views[0] = vrs; - } else { - // create slices - for (uint32_t v = 0; v < view_count; v++) { - vrs_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), vrs, v, 0); - } - } +void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RenderSceneBuffersRD *p_render_buffers) { + if (render_buffers) { + // JIC + free_data(); } - if (p_msaa == RS::VIEWPORT_MSAA_DISABLED) { - { - Vector<RID> fb; - fb.push_back(p_color_buffer); - fb.push_back(depth); - if (vrs.is_valid()) { - fb.push_back(vrs); - } + render_buffers = p_render_buffers; + ERR_FAIL_NULL(render_buffers); - color_only_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); - } - { - Vector<RID> fb; - fb.push_back(depth); + bool msaa_3d = render_buffers->get_msaa_3d(); - depth_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); - } - } else { - RD::TextureFormat tf; - if (view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - } - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - tf.width = p_width; - tf.height = p_height; - tf.array_layers = view_count; // create a layer for every view - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { + RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { RD::TEXTURE_SAMPLES_1, @@ -329,148 +137,112 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_c RD::TEXTURE_SAMPLES_8, }; - texture_samples = ts[p_msaa]; - tf.samples = texture_samples; + texture_samples = ts[msaa_3d]; - color_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + p_render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples); - tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; - tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - depth_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + p_render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples); + } +} - if (view_count == 1) { - // just reuse - color_views[0] = color; - depth_views[0] = depth; - color_msaa_views[0] = color_msaa; - depth_msaa_views[0] = depth_msaa; - } else { - // create slices - for (uint32_t v = 0; v < view_count; v++) { - color_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), color, v, 0); - depth_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), depth, v, 0); - color_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), color_msaa, v, 0); - depth_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), depth_msaa, v, 0); - } - } +RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_only_fb() { + ERR_FAIL_NULL_V(render_buffers, RID()); - { - Vector<RID> fb; - fb.push_back(color_msaa); - fb.push_back(depth_msaa); - if (vrs.is_valid()) { - fb.push_back(vrs); - } + bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED; - color_only_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); - } - { - Vector<RID> fb; - fb.push_back(depth_msaa); + RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture(); + RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); - depth_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count); - } + if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) { + RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE); + + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth, vrs_texture); + } else { + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth); } } RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(uint32_t p_color_pass_flags) { - if (color_framebuffers.has(p_color_pass_flags)) { - return color_framebuffers[p_color_pass_flags]; - } + ERR_FAIL_NULL_V(render_buffers, RID()); + bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED; - bool use_msaa = msaa != RS::VIEWPORT_MSAA_DISABLED; - - Vector<RID> fb; - fb.push_back(use_msaa ? color_msaa : color); + int v_count = (p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) ? render_buffers->get_view_count() : 1; + RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture(); + RID specular; if (p_color_pass_flags & COLOR_PASS_FLAG_SEPARATE_SPECULAR) { ensure_specular(); - fb.push_back(use_msaa ? specular_msaa : specular); - } else { - fb.push_back(RID()); + specular = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_SPECULAR_MSAA : RB_TEX_SPECULAR); } + RID velocity_buffer; if (p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) { - ensure_velocity(); - fb.push_back(use_msaa ? velocity_buffer_msaa : velocity_buffer); - } else { - fb.push_back(RID()); + render_buffers->ensure_velocity(); + velocity_buffer = render_buffers->get_velocity_buffer(use_msaa); } - fb.push_back(use_msaa ? depth_msaa : depth); + RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); - if (vrs.is_valid()) { - fb.push_back(vrs); - } + if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) { + RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE); - int v_count = (p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) ? view_count : 1; - RID framebuffer = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, v_count); - color_framebuffers[p_color_pass_flags] = framebuffer; - return framebuffer; + return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth, vrs_texture); + } else { + return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth); + } } -void RenderForwardClustered::_allocate_normal_roughness_texture(RenderBufferDataForwardClustered *rb) { - ERR_FAIL_COND_MSG(rb->view_count > 2, "Only support up to two views for roughness texture"); +RID RenderForwardClustered::RenderBufferDataForwardClustered::get_depth_fb(DepthFrameBufferType p_type) { + ERR_FAIL_NULL_V(render_buffers, RID()); + bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED; - if (rb->normal_roughness_buffer.is_valid()) { - return; - } + RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture(); - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - tf.width = rb->width; - tf.height = rb->height; - if (rb->view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - tf.array_layers = rb->view_count; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; - } - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + switch (p_type) { + case DEPTH_FB: { + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth); + } break; + case DEPTH_FB_ROUGHNESS: { + ensure_normal_roughness_texture(); - if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - } + RID normal_roughness_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_ROUGHNESS_MSAA : RB_TEX_ROUGHNESS); - rb->normal_roughness_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth, normal_roughness_buffer); + } break; + case DEPTH_FB_ROUGHNESS_VOXELGI: { + ensure_normal_roughness_texture(); + ensure_voxelgi(); - if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { - Vector<RID> fb; - fb.push_back(rb->depth); - fb.push_back(rb->normal_roughness_buffer); - rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count); - } else { - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.samples = rb->texture_samples; - rb->normal_roughness_buffer_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + RID normal_roughness_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_ROUGHNESS_MSAA : RB_TEX_ROUGHNESS); + RID voxelgi_buffer = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_VOXEL_GI_MSAA : RB_TEX_VOXEL_GI); - Vector<RID> fb; - fb.push_back(rb->depth_msaa); - fb.push_back(rb->normal_roughness_buffer_msaa); - rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count); + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), depth, normal_roughness_buffer, voxelgi_buffer); + } break; + default: { + ERR_FAIL_V(RID()); + } break; } +} - if (rb->view_count == 1) { - rb->normal_roughness_views[0] = rb->normal_roughness_buffer; - if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - rb->normal_roughness_msaa_views[0] = rb->normal_roughness_buffer_msaa; - } - } else { - for (uint32_t v = 0; v < rb->view_count; v++) { - rb->normal_roughness_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->normal_roughness_buffer, v, 0); - if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - rb->normal_roughness_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->normal_roughness_buffer_msaa, v, 0); - } - } - } +RID RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_only_fb() { + bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED; + + RID specular = render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, use_msaa ? RB_TEX_SPECULAR_MSAA : RB_TEX_SPECULAR); + + return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), specular); } -RendererSceneRenderRD::RenderBufferData *RenderForwardClustered::_create_render_buffer_data() { - return memnew(RenderBufferDataForwardClustered); +void RenderForwardClustered::setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) { + Ref<RenderBufferDataForwardClustered> data; + data.instantiate(); + p_render_buffers->set_custom_data(RB_SCOPE_FORWARD_CLUSTERED, data); + + Ref<RendererRD::GI::RenderBuffersGI> rbgi; + rbgi.instantiate(); + p_render_buffers->set_custom_data(RB_SCOPE_GI, rbgi); } bool RenderForwardClustered::free(RID p_rid) { @@ -801,6 +573,8 @@ void RenderForwardClustered::_render_list_with_threads(RenderListParameters *p_p } void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_pancake_shadows, int p_index) { + Ref<RenderSceneBuffersRD> rd = p_render_data->render_buffers; + //Projection projection = p_render_data->cam_projection; //projection.flip_y(); // Vulkan and modern APIs use Y-Down Projection correction; @@ -868,22 +642,23 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat scene_state.ubo.volumetric_fog_enabled = false; scene_state.ubo.fog_enabled = false; - if (p_render_data->render_buffers.is_valid()) { - RenderBufferDataForwardClustered *render_buffers = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_data->render_buffers)); - if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) { + if (rd.is_valid()) { + if (rd->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { scene_state.ubo.gi_upscale_for_msaa = true; } - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers)) { + if (rd->has_custom_data(RB_SCOPE_FOG)) { + Ref<RendererRD::Fog::VolumetricFog> fog = rd->get_custom_data(RB_SCOPE_FOG); + scene_state.ubo.volumetric_fog_enabled = true; - float fog_end = render_buffers_get_volumetric_fog_end(p_render_data->render_buffers); + float fog_end = fog->length; if (fog_end > 0.0) { scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end; } else { scene_state.ubo.volumetric_fog_inv_length = 1.0; } - float fog_detail_spread = render_buffers_get_volumetric_fog_detail_spread(p_render_data->render_buffers); //reverse lookup + float fog_detail_spread = fog->spread; //reverse lookup if (fog_detail_spread > 0.0) { scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread; } else { @@ -906,8 +681,9 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -916,9 +692,9 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -987,13 +763,31 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat scene_state.ubo.ss_effects_flags = 0; } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment) / _render_buffers_get_luminance_multiplier(); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky.sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); scene_state.ubo.roughness_limiter_amount = screen_space_roughness_limiter_get_amount(); scene_state.ubo.roughness_limiter_limit = screen_space_roughness_limiter_get_limit(); - if (p_render_data->render_buffers.is_valid()) { - RenderBufferDataForwardClustered *render_buffers = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_data->render_buffers)); - if (render_buffers->use_taa || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { + if (rd.is_valid()) { + if (rd->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { memcpy(&scene_state.prev_ubo, &scene_state.ubo, sizeof(SceneState::UBO)); Projection prev_correction; @@ -1383,7 +1177,7 @@ void RenderForwardClustered::_setup_voxelgis(const PagedArray<RID> &p_voxelgis) } } -void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { +void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { scene_state.lightmaps_used = 0; for (int i = 0; i < (int)p_lightmaps.size(); i++) { if (i >= (int)scene_state.max_lightmaps) { @@ -1395,6 +1189,13 @@ void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps Basis to_lm = lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + scene_state.lightmaps[i].exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float baked_exposure = RendererRD::LightStorage::get_singleton()->lightmap_get_baked_exposure_normalization(lightmap); + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; + } + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = RendererRD::LightStorage::get_singleton()->lightmap_uses_spherical_harmonics(lightmap); @@ -1406,9 +1207,11 @@ void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps } void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { - RenderBufferDataForwardClustered *render_buffer = nullptr; - if (p_render_data->render_buffers.is_valid()) { - render_buffer = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_data->render_buffers)); + Ref<RenderSceneBuffersRD> rb; + Ref<RenderBufferDataForwardClustered> rb_data; + if (p_render_data && p_render_data->render_buffers.is_valid()) { + rb = p_render_data->render_buffers; + rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } static const int texture_multisamples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8 }; @@ -1436,11 +1239,10 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool reverse_cull = false; bool using_ssil = p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); - if (render_buffer) { - screen_size.x = render_buffer->width; - screen_size.y = render_buffer->height; + if (rb.is_valid()) { + screen_size = rb->get_internal_size(); - if (render_buffer->use_taa || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { + if (rb->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) { color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS; } @@ -1468,17 +1270,14 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co switch (depth_pass_mode) { case PASS_MODE_DEPTH: { - depth_framebuffer = render_buffer->depth_fb; + depth_framebuffer = rb_data->get_depth_fb(); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { - _allocate_normal_roughness_texture(render_buffer); - depth_framebuffer = render_buffer->depth_normal_roughness_fb; + depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS); depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: { - _allocate_normal_roughness_texture(render_buffer); - render_buffer->ensure_voxelgi(); - depth_framebuffer = render_buffer->depth_normal_roughness_voxelgi_fb; + depth_framebuffer = rb_data->get_depth_fb(RenderBufferDataForwardClustered::DEPTH_FB_ROUGHNESS_VOXELGI); depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; @@ -1490,8 +1289,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW; } - color_framebuffer = render_buffer->get_color_pass_fb(color_pass_flags); - color_only_framebuffer = render_buffer->color_only_fb; + color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); + color_only_framebuffer = rb_data->get_color_only_fb(); } 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; @@ -1513,9 +1312,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co scene_state.ubo.viewport_size[0] = screen_size.x; scene_state.ubo.viewport_size[1] = screen_size.y; + scene_state.ubo.emissive_exposure_normalization = -1.0; + RD::get_singleton()->draw_command_begin_label("Render Setup"); - _setup_lightmaps(*p_render_data->lightmaps, p_render_data->cam_transform); + _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->cam_transform); _setup_voxelgis(*p_render_data->voxel_gi_instances); _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); @@ -1529,16 +1330,18 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_end_label(); - bool using_sss = render_buffer && scene_state.used_sss && sub_surface_scattering_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; + bool using_sss = rb_data.is_valid() && scene_state.used_sss && sub_surface_scattering_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; if (using_sss && !using_separate_specular) { using_separate_specular = true; color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR; - color_framebuffer = render_buffer->get_color_pass_fb(color_pass_flags); + color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags); } RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; + // We invert luminance_multiplier for sky so that we can combine it with exposure value. + float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); Color clear_color; bool keep_color = false; @@ -1547,24 +1350,30 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (is_environment(p_render_data->environment)) { RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + + if (p_render_data->camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { clear_color = p_default_bg_color; - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_get_fog_enabled(p_render_data->environment)) { + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; + if ((rb.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(p_render_data->environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_get_fog_enabled(p_render_data->environment)) { + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; + if ((rb.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1594,11 +1403,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co projection = correction * p_render_data->cam_projection; } - sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, projection, p_render_data->cam_transform, screen_size, this); + sky.setup(p_render_data->environment, rb, *p_render_data->lights, p_render_data->camera_attributes, projection, p_render_data->cam_transform, screen_size, this); + + sky_energy_multiplier *= bg_energy_multiplier; RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { - sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time); + sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, sky_energy_multiplier); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid @@ -1614,7 +1425,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid(); - bool using_ssao = depth_pre_pass && p_render_data->render_buffers.is_valid() && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); + bool using_ssao = depth_pre_pass && rb.is_valid() && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); bool continue_depth = false; if (depth_pre_pass) { //depth pre pass @@ -1637,7 +1448,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID()); bool finish_depth = using_ssao || using_sdfgi || using_voxelgi; - 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, depth_pass_mode, 0, render_buffer == nullptr, p_render_data->directional_light_soft_shadows, 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_mesh_lod_threshold, p_render_data->view_count); + 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, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, 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_mesh_lod_threshold, p_render_data->view_count); _render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear); RD::get_singleton()->draw_command_end_label(); @@ -1646,19 +1457,19 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _pre_resolve_render(p_render_data, using_sdfgi || using_voxelgi); } - if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + if (rb.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { RENDER_TIMESTAMP("Resolve Depth Pre-Pass (MSAA)"); RD::get_singleton()->draw_command_begin_label("Resolve Depth Pre-Pass (MSAA)"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI) { if (needs_pre_resolve) { RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, RD::BARRIER_MASK_COMPUTE); } - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - resolve_effects->resolve_gi(render_buffer->depth_msaa_views[v], render_buffer->normal_roughness_msaa_views[v], using_voxelgi ? render_buffer->voxelgi_msaa_views[v] : RID(), render_buffer->depth_views[v], render_buffer->normal_roughness_views[v], using_voxelgi ? render_buffer->voxelgi_views[v] : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + resolve_effects->resolve_gi(rb_data->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } } else if (finish_depth) { - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } } RD::get_singleton()->draw_command_end_label(); @@ -1667,15 +1478,20 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co continue_depth = !finish_depth; } - RID nullrids[RendererSceneRender::MAX_RENDER_VIEWS]; - _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, render_buffer ? render_buffer->normal_roughness_views : nullrids, render_buffer ? render_buffer->voxelgi_buffer : RID(), render_buffer ? render_buffer->vrs_views : nullrids); + RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS]; + if (rb_data.is_valid() && rb_data->has_normal_roughness()) { + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + normal_roughness_views[v] = rb_data->get_normal_roughness(v); + } + } + _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, normal_roughness_views, rb_data.is_valid() && rb_data->has_voxelgi() ? rb_data->get_voxelgi() : RID()); RD::get_singleton()->draw_command_begin_label("Render Opaque Pass"); scene_state.ubo.directional_light_count = p_render_data->directional_light_count; scene_state.ubo.opaque_prepass_threshold = 0.0f; - _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()); + _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, rb.is_valid()); RENDER_TIMESTAMP("Render Opaque Pass"); @@ -1691,22 +1507,22 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co Vector<Color> c; { Color cc = clear_color.srgb_to_linear(); - if (using_separate_specular || render_buffer) { + if (using_separate_specular || rb_data.is_valid()) { cc.a = 0; //subsurf scatter must be 0 } c.push_back(cc); - if (render_buffer) { + if (rb_data.is_valid()) { c.push_back(Color(0, 0, 0, 0)); // Separate specular c.push_back(Color(0, 0, 0, 0)); // Motion vectors } } - 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, color_pass_flags, render_buffer == nullptr, p_render_data->directional_light_soft_shadows, 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_mesh_lod_threshold, p_render_data->view_count); + 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, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, 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_mesh_lod_threshold, p_render_data->view_count); _render_list_with_threads(&render_list_params, color_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); if (will_continue_color && using_separate_specular) { // close the specular framebuffer, as it's no longer used - RD::get_singleton()->draw_list_begin(render_buffer->specular_only_fb, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE); + RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE); RD::get_singleton()->draw_list_end(); } } @@ -1741,7 +1557,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co for (uint32_t v = 0; v < p_render_data->view_count; v++) { cms[v] = (dc * p_render_data->view_projection[v]) * Projection(p_render_data->cam_transform.affine_inverse()); } - _debug_sdfgi_probes(p_render_data->render_buffers, color_only_framebuffer, p_render_data->view_count, cms, will_continue_color, will_continue_depth); + _debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->view_count, cms, will_continue_color, will_continue_depth); } if (draw_sky || draw_sky_fog_only) { @@ -1753,28 +1569,28 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, 1, &projection, p_render_data->cam_transform, time); + sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); } - if (render_buffer && !can_continue_color && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + if (rb.is_valid() && !can_continue_color && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { // Handle views individual, might want to look at rewriting our resolve to do both layers in one pass. - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa_views[v], render_buffer->color_views[v]); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v)); } if (using_separate_specular) { - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->specular_msaa_views[v], render_buffer->specular_views[v]); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RD::get_singleton()->texture_resolve_multisample(rb_data->get_specular_msaa(v), rb_data->get_specular(v)); } } } - if (render_buffer && !can_continue_depth && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]); + if (rb.is_valid() && !can_continue_depth && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } } @@ -1782,19 +1598,23 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (using_sss) { RENDER_TIMESTAMP("Sub-Surface Scattering"); RD::get_singleton()->draw_command_begin_label("Process Sub-Surface Scattering"); - _process_sss(p_render_data->render_buffers, p_render_data->cam_projection); + _process_sss(rb, p_render_data->cam_projection); RD::get_singleton()->draw_command_end_label(); } if (using_ssr) { RENDER_TIMESTAMP("Screen-Space Reflections"); RD::get_singleton()->draw_command_begin_label("Process Screen-Space Reflections"); - _process_ssr(p_render_data->render_buffers, color_only_framebuffer, render_buffer->normal_roughness_views, render_buffer->specular, render_buffer->specular_views, Color(0, 0, 0, 1), p_render_data->environment, p_render_data->view_projection, p_render_data->view_eye_offset, render_buffer->msaa == RS::VIEWPORT_MSAA_DISABLED); + RID specular_views[RendererSceneRender::MAX_RENDER_VIEWS]; + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + specular_views[v] = rb_data->get_specular(v); + } + _process_ssr(rb, color_only_framebuffer, normal_roughness_views, rb_data->get_specular(), specular_views, Color(0, 0, 0, 1), p_render_data->environment, p_render_data->view_projection, p_render_data->view_eye_offset, rb->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED); RD::get_singleton()->draw_command_end_label(); } else { //just mix specular back RENDER_TIMESTAMP("Merge Specular"); - copy_effects->merge_specular(color_only_framebuffer, render_buffer->specular, render_buffer->msaa == RS::VIEWPORT_MSAA_DISABLED ? RID() : render_buffer->color, RID(), p_render_data->view_count); + copy_effects->merge_specular(color_only_framebuffer, rb_data->get_specular(), rb->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED ? RID() : rb->get_internal_texture(), RID(), p_render_data->view_count); } } @@ -1818,8 +1638,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co { uint32_t transparent_color_pass_flags = (color_pass_flags | COLOR_PASS_FLAG_TRANSPARENT) & ~(COLOR_PASS_FLAG_SEPARATE_SPECULAR); - RID alpha_framebuffer = render_buffer ? render_buffer->get_color_pass_fb(transparent_color_pass_flags) : color_only_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(), false, PASS_MODE_COLOR, transparent_color_pass_flags, render_buffer == nullptr, p_render_data->directional_light_soft_shadows, 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_mesh_lod_threshold, p_render_data->view_count); + RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_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(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, 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_mesh_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); } @@ -1829,13 +1649,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Resolve"); - if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - for (uint32_t v = 0; v < render_buffer->view_count; v++) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa_views[v], render_buffer->color_views[v]); - resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]); + if (rb.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v)); + resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } - if (render_buffer->use_taa) { // TODO make TAA stereo capable, this will need to be handled in a separate PR - RD::get_singleton()->texture_resolve_multisample(render_buffer->velocity_buffer_msaa, render_buffer->velocity_buffer); + if (taa && rb->get_use_taa()) { + taa->msaa_resolve(rb); } } @@ -1844,17 +1664,17 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Copy framebuffer for SSIL"); if (using_ssil) { RENDER_TIMESTAMP("Copy Final Framebuffer (SSIL)"); - _copy_framebuffer_to_ssil(p_render_data->render_buffers); + _copy_framebuffer_to_ssil(rb); } RD::get_singleton()->draw_command_end_label(); - if (render_buffer && render_buffer->use_taa) { + if (rb.is_valid() && taa && rb->get_use_taa()) { RENDER_TIMESTAMP("TAA") - _process_taa(p_render_data->render_buffers, render_buffer->velocity_buffer, p_render_data->z_near, p_render_data->z_far); + taa->process(rb, _render_buffers_get_color_format(), p_render_data->z_near, p_render_data->z_far); } - if (p_render_data->render_buffers.is_valid()) { - _debug_draw_cluster(p_render_data->render_buffers); + if (rb.is_valid()) { + _debug_draw_cluster(rb); RENDER_TIMESTAMP("Tonemap"); @@ -2000,7 +1820,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); @@ -2018,6 +1838,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = false; scene_state.ubo.opaque_prepass_threshold = 0.0f; + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2064,6 +1885,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; scene_state.ubo.opaque_prepass_threshold = 0.0; + scene_state.ubo.emissive_exposure_normalization = -1.0; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2119,7 +1941,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) { +void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { RENDER_TIMESTAMP("Render SDFGI"); RD::get_singleton()->draw_command_begin_label("Render SDFGI Voxel"); @@ -2131,9 +1953,6 @@ void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i _update_render_base_uniform_set(); - RenderBufferDataForwardClustered *render_buffer = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_buffers)); - ERR_FAIL_COND(!render_buffer); - PassMode pass_mode = PASS_MODE_SDF; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); render_list[RENDER_LIST_SECONDARY].sort_by_key(); @@ -2187,6 +2006,7 @@ void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i RendererRD::MaterialStorage::store_transform(to_bounds.affine_inverse() * render_data.cam_transform, scene_state.ubo.sdf_to_bounds); + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture); @@ -2407,9 +2227,11 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - RenderBufferDataForwardClustered *rb = nullptr; + Ref<RenderSceneBuffersRD> rb; // handy for not having to fully type out p_render_data->render_buffers all the time... + Ref<RenderBufferDataForwardClustered> rb_data; if (p_render_data && p_render_data->render_buffers.is_valid()) { - rb = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_data->render_buffers)); + rb = p_render_data->render_buffers; + rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); } //default render buffer and scene state uniform set @@ -2538,8 +2360,12 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID dbt = rb ? render_buffers_get_back_depth_texture(p_render_data->render_buffers) : RID(); - RID texture = (dbt.is_valid()) ? dbt : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); + RID texture; + if (rb.is_valid() && rb->has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH)) { + texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH); + } else { + texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); + } u.append_id(texture); uniforms.push_back(u); } @@ -2547,7 +2373,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID bbt = rb ? render_buffers_get_back_buffer_texture(p_render_data->render_buffers) : RID(); + RID bbt = rb_data.is_valid() ? rb->get_back_buffer_texture() : RID(); RID texture = bbt.is_valid() ? bbt : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); @@ -2558,7 +2384,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 11; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID texture = rb && rb->normal_roughness_buffer.is_valid() ? rb->normal_roughness_buffer : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_NORMAL); + RID texture = rb_data.is_valid() && rb_data->has_normal_roughness() ? rb_data->get_normal_roughness() : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_NORMAL); u.append_id(texture); uniforms.push_back(u); } @@ -2567,7 +2393,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 12; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID aot = rb ? render_buffers_get_ao_texture(p_render_data->render_buffers) : RID(); + RID aot = rb_data.is_valid() ? rb->get_ao_texture() : RID(); RID texture = aot.is_valid() ? aot : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); @@ -2577,8 +2403,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 13; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID ambient_buffer = rb ? render_buffers_get_gi_ambient_texture(p_render_data->render_buffers) : RID(); - RID texture = ambient_buffer.is_valid() ? ambient_buffer : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); + RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT) : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } @@ -2587,8 +2412,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 14; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID reflection_buffer = rb ? render_buffers_get_gi_reflection_texture(p_render_data->render_buffers) : RID(); - RID texture = reflection_buffer.is_valid() ? reflection_buffer : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); + RID texture = rb_data.is_valid() && rb->has_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) ? rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION) : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); } @@ -2597,9 +2421,11 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend u.binding = 15; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID t; - if (rb && render_buffers_is_sdfgi_enabled(p_render_data->render_buffers)) { - t = render_buffers_get_sdfgi_irradiance_probes(p_render_data->render_buffers); - } else { + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + t = sdfgi->lightprobe_texture; + } + if (t.is_null()) { t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); } u.append_id(t); @@ -2609,18 +2435,27 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 16; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - if (rb && render_buffers_is_sdfgi_enabled(p_render_data->render_buffers)) { - u.append_id(render_buffers_get_sdfgi_occlusion_texture(p_render_data->render_buffers)); - } else { - u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE)); + RID t; + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + t = sdfgi->occlusion_texture; } + if (t.is_null()) { + t = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); + } + u.append_id(t); uniforms.push_back(u); } { RD::Uniform u; u.binding = 17; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.append_id(rb ? render_buffers_get_voxel_gi_buffer(p_render_data->render_buffers) : render_buffers_get_default_voxel_gi_buffer()); + RID voxel_gi; + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_GI)) { + Ref<RendererRD::GI::RenderBuffersGI> rbgi = rb->get_custom_data(RB_SCOPE_GI); + voxel_gi = rbgi->get_voxel_gi_buffer(); + } + u.append_id(voxel_gi.is_valid() ? voxel_gi : render_buffers_get_default_voxel_gi_buffer()); uniforms.push_back(u); } { @@ -2628,8 +2463,9 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend u.binding = 18; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID vfog = RID(); - if (rb && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) { - vfog = render_buffers_get_volumetric_fog_texture(p_render_data->render_buffers); + if (rb_data.is_valid() && rb->has_custom_data(RB_SCOPE_FOG)) { + Ref<RendererRD::Fog::VolumetricFog> fog = rb->get_custom_data(RB_SCOPE_FOG); + vfog = fog->fog_map; if (vfog.is_null()) { vfog = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); } @@ -2643,7 +2479,7 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend RD::Uniform u; u.binding = 19; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID ssil = rb ? render_buffers_get_ssil_texture(p_render_data->render_buffers) : RID(); + RID ssil = rb_data.is_valid() ? rb->get_ssil_texture() : RID(); RID texture = ssil.is_valid() ? ssil : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); @@ -2786,16 +2622,14 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te return UniformSetCacheRD::get_singleton()->get_cache_vec(scene_shader.default_shader_sdfgi_rd, RENDER_PASS_UNIFORM_SET, uniforms); } -RID RenderForwardClustered::_render_buffers_get_normal_texture(RID p_render_buffers) { - RenderBufferDataForwardClustered *rb = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_buffers)); +RID RenderForwardClustered::_render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) { + Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); - return rb->msaa == RS::VIEWPORT_MSAA_DISABLED ? rb->normal_roughness_buffer : rb->normal_roughness_buffer_msaa; + return p_render_buffers->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED ? rb_data->get_normal_roughness() : rb_data->get_normal_roughness_msaa(); } -RID RenderForwardClustered::_render_buffers_get_velocity_texture(RID p_render_buffers) { - RenderBufferDataForwardClustered *rb = static_cast<RenderBufferDataForwardClustered *>(render_buffers_get_data(p_render_buffers)); - - return rb->msaa == RS::VIEWPORT_MSAA_DISABLED ? rb->velocity_buffer : rb->velocity_buffer_msaa; +RID RenderForwardClustered::_render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) { + return p_render_buffers->get_velocity_buffer(p_render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED); } RenderForwardClustered *RenderForwardClustered::singleton = nullptr; @@ -3341,9 +3175,15 @@ RenderForwardClustered::RenderForwardClustered() { _update_shader_quality_settings(); resolve_effects = memnew(RendererRD::Resolve()); + taa = memnew(RendererRD::TAA); } RenderForwardClustered::~RenderForwardClustered() { + if (taa != nullptr) { + memdelete(taa); + taa = nullptr; + } + if (resolve_effects != nullptr) { memdelete(resolve_effects); resolve_effects = nullptr; 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 7e71406af8..35379cd69b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -33,12 +33,22 @@ #include "core/templates/paged_allocator.h" #include "servers/rendering/renderer_rd/effects/resolve.h" +#include "servers/rendering/renderer_rd/effects/taa.h" #include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h" #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" #include "servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl.gen.h" #include "servers/rendering/renderer_rd/storage_rd/utilities.h" +#define RB_SCOPE_FORWARD_CLUSTERED SNAME("forward_clustered") + +#define RB_TEX_SPECULAR SNAME("specular") +#define RB_TEX_SPECULAR_MSAA SNAME("specular_msaa") +#define RB_TEX_ROUGHNESS SNAME("normal_roughnesss") +#define RB_TEX_ROUGHNESS_MSAA SNAME("normal_roughnesss_msaa") +#define RB_TEX_VOXEL_GI SNAME("voxel_gi") +#define RB_TEX_VOXEL_GI_MSAA SNAME("voxel_gi_msaa") + namespace RendererSceneRenderImplementation { class RenderForwardClustered : public RendererSceneRenderRD { @@ -81,73 +91,67 @@ class RenderForwardClustered : public RendererSceneRenderRD { /* Framebuffer */ - struct RenderBufferDataForwardClustered : public RenderBufferData { - //for rendering, may be MSAAd - - RID color; - RID depth; - RID specular; - RID normal_roughness_buffer; - RID voxelgi_buffer; - RID velocity_buffer; + class RenderBufferDataForwardClustered : public RenderBufferCustomDataRD { + GDCLASS(RenderBufferDataForwardClustered, RenderBufferCustomDataRD) - RS::ViewportMSAA msaa; - RD::TextureSamples texture_samples; - bool use_taa; + private: + RenderSceneBuffersRD *render_buffers = nullptr; + RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1; - RID color_msaa; - RID depth_msaa; - RID specular_msaa; - RID normal_roughness_buffer_msaa; - RID voxelgi_buffer_msaa; - RID velocity_buffer_msaa; + public: + //for rendering, may be MSAAd - RID depth_fb; - RID depth_normal_roughness_fb; - RID depth_normal_roughness_voxelgi_fb; - RID color_only_fb; - RID specular_only_fb; + enum DepthFrameBufferType { + DEPTH_FB, + DEPTH_FB_ROUGHNESS, + DEPTH_FB_ROUGHNESS_VOXELGI + }; - RID vrs; + RID render_sdfgi_uniform_set; - int width, height; - HashMap<uint32_t, RID> color_framebuffers; + RID get_color_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA); } + RID get_color_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA, p_layer, 0); } - // for multiview - uint32_t view_count = 1; - RID color_views[RendererSceneRender::MAX_RENDER_VIEWS]; // we should rewrite this so we get access to the existing views in our renderer, something we can address when we reorg this - RID depth_views[RendererSceneRender::MAX_RENDER_VIEWS]; // we should rewrite this so we get access to the existing views in our renderer, something we can address when we reorg this - RID specular_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID specular_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID color_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID depth_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID normal_roughness_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID voxelgi_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID voxelgi_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS]; - RID vrs_views[RendererSceneRender::MAX_RENDER_VIEWS]; + RID get_depth_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA); } + RID get_depth_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA, p_layer, 0); } - RID render_sdfgi_uniform_set; void ensure_specular(); + bool has_specular() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); } + RID get_specular() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); } + RID get_specular(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, p_layer, 0); } + RID get_specular_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, p_layer, 0); } + + void ensure_normal_roughness_texture(); + bool has_normal_roughness() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS); } + RID get_normal_roughness() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS); } + RID get_normal_roughness(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS, p_layer, 0); } + RID get_normal_roughness_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS_MSAA); } + RID get_normal_roughness_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS_MSAA, p_layer, 0); } + void ensure_voxelgi(); - void ensure_velocity(); - void clear(); - 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, bool p_use_taa, uint32_t p_view_count, RID p_vrs_texture); + bool has_voxelgi() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI); } + RID get_voxelgi() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI); } + RID get_voxelgi(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, p_layer, 0); } + RID get_voxelgi_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, p_layer, 0); } + + RID get_color_only_fb(); RID get_color_pass_fb(uint32_t p_color_pass_flags); + RID get_depth_fb(DepthFrameBufferType p_type = DEPTH_FB); + RID get_specular_only_fb(); - ~RenderBufferDataForwardClustered(); + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override; + virtual void free_data() override; }; - virtual RenderBufferData *_create_render_buffer_data() override; - void _allocate_normal_roughness_texture(RenderBufferDataForwardClustered *rb); + virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override; RID render_base_uniform_set; uint64_t lightmap_texture_array_version = 0xFFFFFFFF; virtual void _base_uniforms_changed() override; - virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) override; - virtual RID _render_buffers_get_velocity_texture(RID p_render_buffers) override; + virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override; + virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override; bool base_uniform_set_updated = false; void _update_render_base_uniform_set(); @@ -218,6 +222,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct LightmapData { float normal_xform[12]; + float pad[3]; + float exposure_normalization; }; struct LightmapCaptureData { @@ -324,7 +330,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint32_t pancake_shadows; float taa_jitter[2]; - uint32_t pad[2]; + float emissive_exposure_normalization; // Needed to normalize emissive when using physical units. + float IBL_exposure_normalization; }; struct PushConstant { @@ -397,7 +404,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); void _setup_voxelgis(const PagedArray<RID> &p_voxelgis); - void _setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); + void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); struct RenderElementInfo { enum { MAX_REPEATS = (1 << 20) - 1 }; @@ -609,6 +616,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { virtual void _update_shader_quality_settings() override; RendererRD::Resolve *resolve_effects = nullptr; + RendererRD::TAA *taa = nullptr; protected: virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override; @@ -618,9 +626,9 @@ protected: virtual void _render_shadow_process() override; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) override; + virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override; public: 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 ffd47cc163..46d90e75fb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -69,183 +69,175 @@ void RenderForwardMobile::_map_forward_id(ForwardIDType p_type, ForwardID p_id, /* Render buffer */ -void RenderForwardMobile::RenderBufferDataForwardMobile::clear() { - if (color_msaa.is_valid()) { - RD::get_singleton()->free(color_msaa); - color_msaa = RID(); +void RenderForwardMobile::RenderBufferDataForwardMobile::free_data() { + // this should already be done but JIC.. + if (render_buffers) { + render_buffers->clear_context(RB_SCOPE_MOBILE); } +} - if (depth_msaa.is_valid()) { - RD::get_singleton()->free(depth_msaa); - depth_msaa = RID(); +void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RenderSceneBuffersRD *p_render_buffers) { + if (render_buffers) { + // JIC + free_data(); } - color = RID(); - depth = RID(); - for (int i = 0; i < FB_CONFIG_MAX; i++) { - color_fbs[i] = RID(); - } -} + render_buffers = p_render_buffers; + ERR_FAIL_NULL(render_buffers); // Huh? really? -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, bool p_use_taa, uint32_t p_view_count, RID p_vrs_texture) { - clear(); + RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d(); + if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { + // Create our MSAA textures... - msaa = p_msaa; - vrs = p_vrs_texture; + RD::DataFormat format = render_buffers->get_base_data_format(); + uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - Size2i target_size = RD::get_singleton()->texture_size(p_target_buffer); + const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { + RD::TEXTURE_SAMPLES_1, + RD::TEXTURE_SAMPLES_2, + RD::TEXTURE_SAMPLES_4, + RD::TEXTURE_SAMPLES_8, + }; - width = p_width; - height = p_height; - bool is_scaled = (target_size.width != p_width) || (target_size.height != p_height); - view_count = p_view_count; + texture_samples = ts[msaa_3d]; - color = p_color_buffer; - depth = p_depth_buffer; + render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples); - // We are creating 4 configurations here for our framebuffers. + format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; - if (p_msaa == RS::VIEWPORT_MSAA_DISABLED) { - Vector<RID> fb; - fb.push_back(p_color_buffer); // 0 - color buffer - fb.push_back(depth); // 1 - depth buffer - if (vrs.is_valid()) { - fb.push_back(vrs); // 2 - vrs texture - } + render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples); + } +} - // Now define our subpasses - Vector<RD::FramebufferPass> passes; - RD::FramebufferPass pass; +RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(FramebufferConfigType p_config_type) { + ERR_FAIL_NULL_V(render_buffers, RID()); - // re-using the same attachments - pass.color_attachments.push_back(0); - pass.depth_attachment = 1; - if (vrs.is_valid()) { - pass.vrs_attachment = 2; - } + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + ERR_FAIL_NULL_V(texture_storage, RID()); - // - 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); + // We use our framebuffer cache here instead of building these in RenderBufferDataForwardMobile::configure + // This approach ensures we only build the framebuffers we actually need for this viewport. + // In the (near) future this means that if we cycle through a texture chain for our render target, we'll also support + // this. - // - 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); + RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d(); + bool use_msaa = msaa_3d != RS::VIEWPORT_MSAA_DISABLED; - // - 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); + uint32_t view_count = render_buffers->get_view_count(); - if (!is_scaled) { - // - add blit to 2D pass - int target_buffer_id = fb.size(); - fb.push_back(p_target_buffer); // 2/3 - target buffer + RID vrs_texture; + if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) { + vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE); + } - RD::FramebufferPass blit_pass; - blit_pass.color_attachments.push_back(target_buffer_id); - blit_pass.input_attachments.push_back(0); - passes.push_back(blit_pass); // this doesn't need VRS + Vector<RID> textures; + int color_buffer_id = 0; + textures.push_back(use_msaa ? get_color_msaa() : render_buffers->get_internal_texture()); // 0 - color buffer + textures.push_back(use_msaa ? get_depth_msaa() : render_buffers->get_depth_texture()); // 1 - depth buffer + if (vrs_texture.is_valid()) { + textures.push_back(vrs_texture); // 2 - vrs texture + } + if (use_msaa) { + color_buffer_id = textures.size(); + textures.push_back(render_buffers->get_internal_texture()); // color buffer for resolve - 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(); + // TODO add support for resolving depth buffer!!! + } - RD::TextureFormat tf; - if (view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } else { - tf.texture_type = RD::TEXTURE_TYPE_2D; - } - tf.format = color_format; - tf.width = p_width; - tf.height = p_height; - tf.array_layers = view_count; // create a layer for every view - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + // Now define our subpasses + Vector<RD::FramebufferPass> passes; - const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { - RD::TEXTURE_SAMPLES_1, - RD::TEXTURE_SAMPLES_2, - RD::TEXTURE_SAMPLES_4, - RD::TEXTURE_SAMPLES_8, - }; + // Define our base pass, we'll be re-using this + RD::FramebufferPass pass; + pass.color_attachments.push_back(0); + pass.depth_attachment = 1; + if (vrs_texture.is_valid()) { + pass.vrs_attachment = 2; + } - texture_samples = ts[p_msaa]; - tf.samples = texture_samples; + switch (p_config_type) { + case FB_CONFIG_ONE_PASS: { + // just one pass + if (use_msaa) { + // Add resolve + pass.resolve_attachments.push_back(color_buffer_id); + } + passes.push_back(pass); - color_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); + } break; + case FB_CONFIG_TWO_SUBPASSES: { + // - opaque pass + passes.push_back(pass); - tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; - tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; + // - add sky pass + if (use_msaa) { + // add resolve + pass.resolve_attachments.push_back(color_buffer_id); + } + passes.push_back(pass); - depth_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); + } break; + case FB_CONFIG_THREE_SUBPASSES: { + // - opaque pass + passes.push_back(pass); - { - Vector<RID> fb; - fb.push_back(color_msaa); // 0 - msaa color buffer - fb.push_back(depth_msaa); // 1 - msaa depth buffer - if (vrs.is_valid()) { - fb.push_back(vrs); // 2 - vrs texture + // - add sky pass + passes.push_back(pass); + + // - add alpha pass + if (use_msaa) { + // add resolve + pass.resolve_attachments.push_back(color_buffer_id); } + passes.push_back(pass); - // Now define our subpasses - Vector<RD::FramebufferPass> passes; - RD::FramebufferPass pass; + return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); + } break; + case FB_CONFIG_FOUR_SUBPASSES: { + Size2i target_size = render_buffers->get_target_size(); + Size2i internal_size = render_buffers->get_internal_size(); - // re-using the same attachments - pass.color_attachments.push_back(0); - pass.depth_attachment = 1; - if (vrs.is_valid()) { - pass.vrs_attachment = 2; - } + // can't do our blit pass if resolutions don't match + ERR_FAIL_COND_V(target_size != internal_size, RID()); // - opaque pass passes.push_back(pass); // - add sky pass - int color_buffer_id = fb.size(); - fb.push_back(color); // 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(color_buffer_id); - two_passes.push_back(pass); // sky subpass with resolve + passes.push_back(pass); - color_fbs[FB_CONFIG_TWO_SUBPASSES] = RD::get_singleton()->framebuffer_create_multipass(fb, two_passes, RenderingDevice::INVALID_ID, view_count); + // - add alpha pass + if (use_msaa) { + // add resolve + pass.resolve_attachments.push_back(color_buffer_id); } - - // - 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); - { - // 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); - } + // - add blit to 2D pass + RID render_target = render_buffers->get_render_target(); + ERR_FAIL_COND_V(render_target.is_null(), RID()); + RID target_buffer = texture_storage->render_target_get_rd_texture(render_target); + ERR_FAIL_COND_V(target_buffer.is_null(), RID()); - if (!is_scaled) { - // - add blit to 2D pass - int target_buffer_id = fb.size(); - fb.push_back(p_target_buffer); // target buffer - RD::FramebufferPass blit_pass; - blit_pass.color_attachments.push_back(target_buffer_id); - blit_pass.input_attachments.push_back(color_buffer_id); - passes.push_back(blit_pass); + int target_buffer_id = textures.size(); + textures.push_back(target_buffer); // target buffer - 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(); - } - } - } + RD::FramebufferPass blit_pass; + blit_pass.input_attachments.push_back(color_buffer_id); // Read from our (resolved) color buffer + blit_pass.color_attachments.push_back(target_buffer_id); // Write into our target buffer + // this doesn't need VRS + passes.push_back(blit_pass); + + return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, view_count); + } break; + default: + break; + }; + + return RID(); } RID RenderForwardMobile::reflection_probe_create_framebuffer(RID p_color, RID p_depth) { @@ -274,12 +266,11 @@ RID RenderForwardMobile::reflection_probe_create_framebuffer(RID p_color, RID p_ return RD::get_singleton()->framebuffer_create_multipass(fb, passes); } -RenderForwardMobile::RenderBufferDataForwardMobile::~RenderBufferDataForwardMobile() { - clear(); -} +void RenderForwardMobile::setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) { + Ref<RenderBufferDataForwardMobile> data; + data.instantiate(); -RendererSceneRenderRD::RenderBufferData *RenderForwardMobile::_create_render_buffer_data() { - return memnew(RenderBufferDataForwardMobile); + p_render_buffers->set_custom_data(RB_SCOPE_MOBILE, data); } bool RenderForwardMobile::free(RID p_rid) { @@ -314,9 +305,11 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ //there should always be enough uniform buffers for render passes, otherwise bugs ERR_FAIL_INDEX_V(p_index, (int)scene_state.uniform_buffers.size(), RID()); - RenderBufferDataForwardMobile *rb = nullptr; + Ref<RenderBufferDataForwardMobile> rb_data; + Ref<RenderSceneBuffersRD> rb; if (p_render_data && p_render_data->render_buffers.is_valid()) { - rb = static_cast<RenderBufferDataForwardMobile *>(render_buffers_get_data(p_render_data->render_buffers)); + rb = p_render_data->render_buffers; + rb_data = rb->get_custom_data(RB_SCOPE_MOBILE); } // default render buffer and scene state uniform set @@ -442,8 +435,12 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ RD::Uniform u; u.binding = 9; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID dbt = rb ? render_buffers_get_back_depth_texture(p_render_data->render_buffers) : RID(); - RID texture = (dbt.is_valid()) ? dbt : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); + RID texture; + if (rb.is_valid() && rb->has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH)) { + texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH); + } else { + texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_DEPTH); + } u.append_id(texture); uniforms.push_back(u); } @@ -451,7 +448,7 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ RD::Uniform u; u.binding = 10; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; - RID bbt = rb ? render_buffers_get_back_buffer_texture(p_render_data->render_buffers) : RID(); + RID bbt = rb_data.is_valid() ? rb->get_back_buffer_texture() : RID(); RID texture = bbt.is_valid() ? bbt : texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); u.append_id(texture); uniforms.push_back(u); @@ -469,7 +466,7 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ return render_pass_uniform_sets[p_index]; } -void RenderForwardMobile::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { +void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { // This probably needs to change... scene_state.lightmaps_used = 0; for (int i = 0; i < (int)p_lightmaps.size(); i++) { @@ -482,6 +479,13 @@ void RenderForwardMobile::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, c Basis to_lm = lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + scene_state.lightmaps[i].exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float baked_exposure = RendererRD::LightStorage::get_singleton()->lightmap_get_baked_exposure_normalization(lightmap); + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; + } + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = RendererRD::LightStorage::get_singleton()->lightmap_uses_spherical_harmonics(lightmap); @@ -493,9 +497,9 @@ void RenderForwardMobile::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, c } void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { - RenderBufferDataForwardMobile *render_buffer = nullptr; + Ref<RenderBufferDataForwardMobile> rb_data; if (p_render_data->render_buffers.is_valid()) { - render_buffer = static_cast<RenderBufferDataForwardMobile *>(render_buffers_get_data(p_render_data->render_buffers)); + rb_data = p_render_data->render_buffers->get_custom_data(RB_SCOPE_MOBILE); } RENDER_TIMESTAMP("Setup 3D Scene"); @@ -531,15 +535,14 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = p_render_data->instances->size(); } - if (render_buffer) { + if (rb_data.is_valid()) { // setup rendering to render buffer - screen_size.x = render_buffer->width; - screen_size.y = render_buffer->height; + screen_size = p_render_data->render_buffers->get_internal_size(); - if (render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES].is_null()) { + if (rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES).is_null()) { // can't do blit subpass using_subpass_post_process = false; - } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || environment_get_auto_exposure(p_render_data->environment) || camera_effects_uses_dof(p_render_data->camera_effects))) { + } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) || RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes))) { // can't do blit subpass using_subpass_post_process = false; } @@ -552,13 +555,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (using_subpass_post_process) { // all as subpasses - framebuffer = render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES]; + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES); } else if (using_subpass_transparent) { // our tonemap pass is separate - framebuffer = render_buffer->color_fbs[FB_CONFIG_THREE_SUBPASSES]; + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_THREE_SUBPASSES); } else { // only opaque and sky as subpasses - framebuffer = render_buffer->color_fbs[FB_CONFIG_TWO_SUBPASSES]; + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::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); @@ -580,10 +583,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color scene_state.ubo.viewport_size[0] = screen_size.x; scene_state.ubo.viewport_size[1] = screen_size.y; + scene_state.ubo.emissive_exposure_normalization = -1.0; RD::get_singleton()->draw_command_begin_label("Render Setup"); - _setup_lightmaps(*p_render_data->lightmaps, p_render_data->cam_transform); + _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->cam_transform); _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); _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) @@ -594,6 +598,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; + // We invert luminance_multiplier for sky so that we can combine it with exposure value. + float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); Color clear_color = p_default_bg_color; bool keep_color = false; @@ -602,15 +608,21 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (is_environment(p_render_data->environment)) { RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + + if (p_render_data->camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { clear_color = p_default_bg_color; - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; /* - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_get_fog_enabled(p_render_data->environment)) { + if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -618,11 +630,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(p_render_data->environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; /* - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_get_fog_enabled(p_render_data->environment)) { + if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -653,11 +665,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color projection = correction * p_render_data->cam_projection; } - sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, projection, p_render_data->cam_transform, screen_size, this); + sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, projection, p_render_data->cam_transform, screen_size, this); + + sky_energy_multiplier *= bg_energy_multiplier; RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { - sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, sky_energy_multiplier); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid @@ -681,16 +695,16 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.update_res_buffers(p_render_data->environment, 1, &projection, p_render_data->cam_transform, time); + sky.update_res_buffers(p_render_data->environment, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.update_res_buffers(p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + sky.update_res_buffers(p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); // Setup Sky resolution buffers } RID nullrids[RendererSceneRender::MAX_RENDER_VIEWS]; - _pre_opaque_render(p_render_data, false, false, false, nullrids, RID(), nullrids); + _pre_opaque_render(p_render_data, false, false, false, nullrids, RID()); uint32_t spec_constant_base_flags = 0; @@ -710,7 +724,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } { - if (render_buffer) { + if (rb_data.is_valid()) { RD::get_singleton()->draw_command_begin_label("Render 3D Pass"); } else { RD::get_singleton()->draw_command_begin_label("Render Reflection Probe Pass"); @@ -741,8 +755,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // regular forward for now Vector<Color> c; c.push_back(clear_color.srgb_to_linear()); // our render buffer - if (render_buffer) { - if (render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + if (rb_data.is_valid()) { + if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { c.push_back(clear_color.srgb_to_linear()); // our resolve buffer } if (using_subpass_post_process) { @@ -780,9 +794,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.draw(draw_list, p_render_data->environment, framebuffer, 1, &projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.draw(draw_list, p_render_data->environment, framebuffer, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.draw(draw_list, p_render_data->environment, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.draw(draw_list, p_render_data->environment, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass @@ -841,8 +855,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // 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); + if (rb_data.is_valid() && using_subpass_post_process) { + _post_process_subpass(p_render_data->render_buffers->get_internal_texture(), framebuffer, p_render_data); } RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass @@ -851,7 +865,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } else { RENDER_TIMESTAMP("Render Transparent"); - framebuffer = render_buffer->color_fbs[FB_CONFIG_ONE_PASS]; + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::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); @@ -879,7 +893,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } - if (render_buffer && !using_subpass_post_process) { + if (rb_data.is_valid() && !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 @@ -890,7 +904,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Post process pass } - if (render_buffer) { + if (rb_data.is_valid()) { _disable_clear_request(p_render_data); } } @@ -999,7 +1013,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { /* */ -void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); @@ -1009,6 +1023,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = false; scene_state.ubo.opaque_prepass_threshold = 0.0f; + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; RenderDataRD render_data; render_data.cam_projection = p_cam_projection; @@ -1054,6 +1069,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; + scene_state.ubo.emissive_exposure_normalization = -1.0; RenderDataRD render_data; render_data.instances = &p_instances; @@ -1112,7 +1128,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> RD::get_singleton()->draw_command_end_label(); } -void RenderForwardMobile::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) { +void RenderForwardMobile::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { // we don't do GI in low end.. } @@ -1344,15 +1360,11 @@ void RenderForwardMobile::_update_render_base_uniform_set() { } } -RID RenderForwardMobile::_render_buffers_get_normal_texture(RID p_render_buffers) { - // RenderBufferDataForwardMobile *rb = (RenderBufferDataForwardMobile *)render_buffers_get_data(p_render_buffers); - - // We don't have this. This is for debugging - // return rb->normal_roughness_buffer; +RID RenderForwardMobile::_render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) { return RID(); } -RID RenderForwardMobile::_render_buffers_get_velocity_texture(RID p_render_buffers) { +RID RenderForwardMobile::_render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) { return RID(); } @@ -1605,37 +1617,6 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, //time global variables scene_state.ubo.time = time; - /* - scene_state.ubo.gi_upscale_for_msaa = false; - scene_state.ubo.volumetric_fog_enabled = false; - scene_state.ubo.fog_enabled = false; - - if (p_render_data->render_buffers.is_valid()) { - RenderBufferDataForwardMobile *render_buffers = static_cast<RenderBufferDataForwardMobile *>(render_buffers_get_data(p_render_data->render_buffers)); - if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) { - scene_state.ubo.gi_upscale_for_msaa = true; - } - - if (render_buffers_has_volumetric_fog(p_render_data->render_buffers)) { - scene_state.ubo.volumetric_fog_enabled = true; - float fog_end = render_buffers_get_volumetric_fog_end(p_render_data->render_buffers); - if (fog_end > 0.0) { - scene_state.ubo.volumetric_fog_inv_length = 1.0 / fog_end; - } else { - scene_state.ubo.volumetric_fog_inv_length = 1.0; - } - - float fog_detail_spread = render_buffers_get_volumetric_fog_detail_spread(p_render_data->render_buffers); //reverse lookup - if (fog_detail_spread > 0.0) { - scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread; - } else { - scene_state.ubo.volumetric_fog_detail_spread = 1.0; - } - } - } - - */ - if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { scene_state.ubo.use_ambient_light = true; scene_state.ubo.ambient_light_color_energy[0] = 1; @@ -1650,8 +1631,9 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -1660,9 +1642,9 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -1726,6 +1708,25 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, scene_state.ubo.ssao_enabled = false; } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment) / _render_buffers_get_luminance_multiplier(); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky.sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); scene_state.ubo.roughness_limiter_amount = screen_space_roughness_limiter_get_amount(); scene_state.ubo.roughness_limiter_limit = screen_space_roughness_limiter_get_limit(); 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 4a7112eb81..da1cd85fb3 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -37,6 +37,8 @@ #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" #include "servers/rendering/renderer_rd/storage_rd/utilities.h" +#define RB_SCOPE_MOBILE SNAME("mobile") + namespace RendererSceneRenderImplementation { class RenderForwardMobile : public RendererSceneRenderRD { @@ -107,43 +109,38 @@ 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; - // RID normal_roughness_buffer; + class RenderBufferDataForwardMobile : public RenderBufferCustomDataRD { + GDCLASS(RenderBufferDataForwardMobile, RenderBufferCustomDataRD); - RS::ViewportMSAA msaa; - RD::TextureSamples texture_samples; - - RID color_msaa; - RID depth_msaa; - // RID normal_roughness_buffer_msaa; + public: + // 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 FramebufferConfigType { + 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 + }; - RID vrs; + RID get_color_msaa() const { return render_buffers->get_texture(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA); } + RID get_color_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA, p_layer, 0); } - RID color_fbs[FB_CONFIG_MAX]; - int width, height; - uint32_t view_count; + RID get_depth_msaa() const { return render_buffers->get_texture(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA); } + RID get_depth_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA, p_layer, 0); } - void clear(); - 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, bool p_use_taa, uint32_t p_view_count, RID p_vrs_texture); + RID get_color_fbs(FramebufferConfigType p_config_type); + virtual void free_data() override; + virtual void configure(RenderSceneBuffersRD *p_render_buffers) override; - ~RenderBufferDataForwardMobile(); + private: + RenderSceneBuffersRD *render_buffers = nullptr; + RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1; }; - virtual RenderBufferData *_create_render_buffer_data() override; + virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override; /* Rendering */ @@ -216,17 +213,17 @@ protected: virtual void _render_shadow_process() override; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) override; + virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override; uint64_t lightmap_texture_array_version = 0xFFFFFFFF; virtual void _base_uniforms_changed() override; void _update_render_base_uniform_set(); - virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) override; - virtual RID _render_buffers_get_velocity_texture(RID p_render_buffers) override; + virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override; + virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> 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_element_info(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1); @@ -235,7 +232,7 @@ protected: static RenderForwardMobile *singleton; void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); - void _setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); + void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); RID render_base_uniform_set; LocalVector<RID> render_pass_uniform_sets; @@ -244,6 +241,8 @@ protected: struct LightmapData { float normal_xform[12]; + float pad[3]; + float exposure_normalization; }; struct LightmapCaptureData { @@ -315,8 +314,8 @@ protected: float reflection_multiplier; uint32_t pancake_shadows; - uint32_t pad1; - uint32_t pad2; + float emissive_exposure_normalization; // Needed to normalize emissive when using physical units. + float IBL_exposure_normalization; // Adjusts for baked exposure. uint32_t pad3; }; diff --git a/servers/rendering/renderer_rd/framebuffer_cache_rd.h b/servers/rendering/renderer_rd/framebuffer_cache_rd.h index f360e0fc6b..f50d6baa30 100644 --- a/servers/rendering/renderer_rd/framebuffer_cache_rd.h +++ b/servers/rendering/renderer_rd/framebuffer_cache_rd.h @@ -200,7 +200,7 @@ public: RID get_cache(Args... args) { uint32_t h = hash_murmur3_one_32(1); //1 view h = hash_murmur3_one_32(sizeof...(Args), h); - h = _hash_args(h, args...); + h = _hash_rids(h, args...); h = hash_murmur3_one_32(0, h); // 0 passes h = hash_fmix32(h); @@ -228,7 +228,7 @@ public: RID get_cache_multiview(uint32_t p_views, Args... args) { uint32_t h = hash_murmur3_one_32(p_views); h = hash_murmur3_one_32(sizeof...(Args), h); - h = _hash_args(h, args...); + h = _hash_rids(h, args...); h = hash_murmur3_one_32(0, h); // 0 passes h = hash_fmix32(h); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index fa83428367..cb7c643dfb 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -37,6 +37,7 @@ #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/storage/camera_attributes_storage.h" void get_vogel_disk(float *r_kernel, int p_sample_count) { const float golden_angle = 2.4; @@ -50,16 +51,21 @@ void get_vogel_disk(float *r_kernel, int p_sample_count) { } } -void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); +void RendererSceneRenderRD::sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) { + Ref<RenderSceneBuffersRD> rb = p_render_buffers; + ERR_FAIL_COND(rb.is_null()); + Ref<RendererRD::GI::SDFGI> sdfgi; + if (rb->has_custom_data(RB_SCOPE_SDFGI)) { + sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + } + bool needs_sdfgi = p_environment.is_valid() && environment_get_sdfgi_enabled(p_environment); if (!needs_sdfgi) { - if (rb->sdfgi != nullptr) { - //erase it - rb->sdfgi->erase(); - memdelete(rb->sdfgi); - rb->sdfgi = nullptr; + if (sdfgi.is_valid()) { + // delete it + sdfgi.unref(); + rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } return; } @@ -67,35 +73,34 @@ void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge]; - if (rb->sdfgi && (rb->sdfgi->num_cascades != environment_get_sdfgi_cascades(p_environment) || rb->sdfgi->min_cell_size != environment_get_sdfgi_min_cell_size(p_environment) || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != environment_get_sdfgi_use_occlusion(p_environment) || rb->sdfgi->y_scale_mode != environment_get_sdfgi_y_scale(p_environment))) { + if (sdfgi.is_valid() && (sdfgi->num_cascades != environment_get_sdfgi_cascades(p_environment) || sdfgi->min_cell_size != environment_get_sdfgi_min_cell_size(p_environment) || requested_history_size != sdfgi->history_size || sdfgi->uses_occlusion != environment_get_sdfgi_use_occlusion(p_environment) || sdfgi->y_scale_mode != environment_get_sdfgi_y_scale(p_environment))) { //configuration changed, erase - rb->sdfgi->erase(); - memdelete(rb->sdfgi); - rb->sdfgi = nullptr; + sdfgi.unref(); + rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } - RendererRD::GI::SDFGI *sdfgi = rb->sdfgi; - if (sdfgi == nullptr) { + if (sdfgi.is_null()) { // re-create - rb->sdfgi = gi.create_sdfgi(p_environment, p_world_position, requested_history_size); + sdfgi = gi.create_sdfgi(p_environment, p_world_position, requested_history_size); + rb->set_custom_data(RB_SCOPE_SDFGI, sdfgi); } else { //check for updates - rb->sdfgi->update(p_environment, p_world_position); + sdfgi->update(p_environment, p_world_position); } } -int RendererSceneRenderRD::sdfgi_get_pending_region_count(RID p_render_buffers) const { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - - ERR_FAIL_COND_V(rb == nullptr, 0); +int RendererSceneRenderRD::sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const { + Ref<RenderSceneBuffersRD> rb = p_render_buffers; + ERR_FAIL_COND_V(rb.is_null(), 0); - if (rb->sdfgi == nullptr) { + if (!rb->has_custom_data(RB_SCOPE_SDFGI)) { return 0; } + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); int dirty_count = 0; - for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { - const RendererRD::GI::SDFGI::Cascade &c = rb->sdfgi->cascades[i]; + for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) { + const RendererRD::GI::SDFGI::Cascade &c = sdfgi->cascades[i]; if (c.dirty_regions == RendererRD::GI::SDFGI::Cascade::DIRTY_ALL) { dirty_count++; @@ -111,28 +116,32 @@ int RendererSceneRenderRD::sdfgi_get_pending_region_count(RID p_render_buffers) return dirty_count; } -AABB RendererSceneRenderRD::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const { +AABB RendererSceneRenderRD::sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(rb == nullptr, AABB()); - ERR_FAIL_COND_V(rb->sdfgi == nullptr, AABB()); - int c = rb->sdfgi->get_pending_region_data(p_region, from, size, bounds); + Ref<RenderSceneBuffersRD> rb = p_render_buffers; + ERR_FAIL_COND_V(rb.is_null(), AABB()); + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + ERR_FAIL_COND_V(sdfgi.is_null(), AABB()); + + int c = sdfgi->get_pending_region_data(p_region, from, size, bounds); ERR_FAIL_COND_V(c == -1, AABB()); return bounds; } -uint32_t RendererSceneRenderRD::sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const { +uint32_t RendererSceneRenderRD::sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const { AABB bounds; Vector3i from; Vector3i size; - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(rb == nullptr, -1); - ERR_FAIL_COND_V(rb->sdfgi == nullptr, -1); - return rb->sdfgi->get_pending_region_data(p_region, from, size, bounds); + Ref<RenderSceneBuffersRD> rb = p_render_buffers; + ERR_FAIL_COND_V(rb.is_null(), -1); + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + ERR_FAIL_COND_V(sdfgi.is_null(), -1); + + return sdfgi->get_pending_region_data(p_region, from, size, bounds); } RID RendererSceneRenderRD::sky_allocate() { @@ -246,7 +255,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba } if (use_cube_map) { - Ref<Image> panorama = sky_bake_panorama(environment_get_sky(p_env), environment_get_bg_energy(p_env), p_bake_irradiance, p_size); + Ref<Image> panorama = sky_bake_panorama(environment_get_sky(p_env), environment_get_bg_energy_multiplier(p_env), p_bake_irradiance, p_size); if (use_ambient_light) { for (int x = 0; x < p_size.width; x++) { for (int y = 0; y < p_size.height; y++) { @@ -256,12 +265,12 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba } return panorama; } else { - const float bg_energy = environment_get_bg_energy(p_env); + const float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_env); Color panorama_color = ((environment_background == RS::ENV_BG_CLEAR_COLOR) ? RSG::texture_storage->get_default_clear_color() : environment_get_bg_color(p_env)); panorama_color = panorama_color.srgb_to_linear(); - panorama_color.r *= bg_energy; - panorama_color.g *= bg_energy; - panorama_color.b *= bg_energy; + panorama_color.r *= bg_energy_multiplier; + panorama_color.g *= bg_energy_multiplier; + panorama_color.b *= bg_energy_multiplier; if (use_ambient_light) { panorama_color = ambient_color.lerp(panorama_color, ambient_color_sky_mix); @@ -1083,45 +1092,6 @@ int RendererSceneRenderRD::get_directional_light_shadow_size(RID p_light_intance ////////////////////////////////////////////////// -RID RendererSceneRenderRD::camera_effects_allocate() { - return camera_effects_owner.allocate_rid(); -} -void RendererSceneRenderRD::camera_effects_initialize(RID p_rid) { - camera_effects_owner.initialize_rid(p_rid, CameraEffects()); -} - -void RendererSceneRenderRD::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { - dof_blur_quality = p_quality; - dof_blur_use_jitter = p_use_jitter; -} - -void RendererSceneRenderRD::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { - dof_blur_bokeh_shape = p_shape; -} - -void RendererSceneRenderRD::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) { - CameraEffects *camfx = camera_effects_owner.get_or_null(p_camera_effects); - ERR_FAIL_COND(!camfx); - - camfx->dof_blur_far_enabled = p_far_enable; - camfx->dof_blur_far_distance = p_far_distance; - camfx->dof_blur_far_transition = p_far_transition; - - camfx->dof_blur_near_enabled = p_near_enable; - camfx->dof_blur_near_distance = p_near_distance; - camfx->dof_blur_near_transition = p_near_transition; - - camfx->dof_blur_amount = p_amount; -} - -void RendererSceneRenderRD::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { - CameraEffects *camfx = camera_effects_owner.get_or_null(p_camera_effects); - ERR_FAIL_COND(!camfx); - - camfx->override_exposure_enabled = p_enable; - camfx->override_exposure = p_exposure; -} - RID RendererSceneRenderRD::light_instance_create(RID p_light) { RID li = light_instance_owner.make_rid(LightInstance()); @@ -1255,191 +1225,44 @@ void RendererSceneRenderRD::voxel_gi_update(RID p_probe, bool p_update_light_ins gi.voxel_gi_update(p_probe, p_update_light_instances, p_light_instances, p_dynamic_objects, this); } -void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); +void RendererSceneRenderRD::_debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) { + ERR_FAIL_COND(p_render_buffers.is_null()); - if (!rb->sdfgi) { + if (!p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { return; //nothing to debug } - rb->sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms, p_will_continue_color, p_will_continue_depth); -} + Ref<RendererRD::GI::SDFGI> sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); -//////////////////////////////// -RID RendererSceneRenderRD::render_buffers_create() { - RenderBuffers rb; - rb.data = _create_render_buffer_data(); - return render_buffers_owner.make_rid(rb); + sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms, p_will_continue_color, p_will_continue_depth); } -void RendererSceneRenderRD::_allocate_blur_textures(RenderBuffers *rb) { - ERR_FAIL_COND(!rb->blur[0].texture.is_null()); - - uint32_t mipmaps_required = Image::get_image_required_mipmaps(rb->width, rb->height, Image::FORMAT_RGBAH); - - RD::TextureFormat tf; - tf.format = _render_buffers_get_color_format(); // RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - tf.width = rb->internal_width; - tf.height = rb->internal_height; - tf.texture_type = rb->view_count > 1 ? RD::TEXTURE_TYPE_2D_ARRAY : RD::TEXTURE_TYPE_2D; - tf.array_layers = rb->view_count; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - if (_render_buffers_can_be_storage()) { - tf.usage_bits += RD::TEXTURE_USAGE_STORAGE_BIT; - } else { - tf.usage_bits += RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - } - tf.mipmaps = mipmaps_required; - - rb->sss_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - tf.width = rb->internal_width; - tf.height = rb->internal_height; - rb->blur[0].texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - //the second one is smaller (only used for separatable part of blur) - tf.width >>= 1; - tf.height >>= 1; - tf.mipmaps--; - rb->blur[1].texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - for (uint32_t l = 0; l < rb->view_count; l++) { - RenderBuffers::Blur::Layer ll[2]; - int base_width = rb->internal_width; - int base_height = rb->internal_height; - - for (uint32_t i = 0; i < mipmaps_required; i++) { - RenderBuffers::Blur::Mipmap mm; - mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[0].texture, l, i); - - mm.width = base_width; - mm.height = base_height; - - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(mm.texture); - - mm.fb = RD::get_singleton()->framebuffer_create(fb); - } - - if (!_render_buffers_can_be_storage()) { - // and half texture, this is an intermediate result so just allocate a texture, is this good enough? - tf.width = MAX(1, base_width >> 1); - tf.height = base_height; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; - tf.mipmaps = 1; - - mm.half_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - Vector<RID> half_fb; - half_fb.push_back(mm.half_texture); - mm.half_fb = RD::get_singleton()->framebuffer_create(half_fb); - } - - ll[0].mipmaps.push_back(mm); - - if (i > 0) { - mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->blur[1].texture, l, i - 1); - - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(mm.texture); - - mm.fb = RD::get_singleton()->framebuffer_create(fb); - - // We can re-use the half texture here as it is an intermediate result - } - - ll[1].mipmaps.push_back(mm); - } - - base_width = MAX(1, base_width >> 1); - base_height = MAX(1, base_height >> 1); - } - - rb->blur[0].layers.push_back(ll[0]); - rb->blur[1].layers.push_back(ll[1]); - } - - if (!_render_buffers_can_be_storage()) { - // create 4 weight textures, 2 full size, 2 half size - - tf.format = RD::DATA_FORMAT_R16_SFLOAT; // We could probably use DATA_FORMAT_R8_SNORM if we don't pre-multiply by blur_size but that depends on whether we can remove DEPTH_GAP - tf.width = rb->internal_width; - tf.height = rb->internal_height; - tf.texture_type = RD::TEXTURE_TYPE_2D; - tf.array_layers = 1; // Our DOF effect handles one eye per turn - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - tf.mipmaps = 1; - for (uint32_t i = 0; i < 4; i++) { - // associated blur texture - RID texture; - if (i == 1) { - texture = rb->blur[0].layers[0].mipmaps[0].texture; - } else if (i == 2) { - texture = rb->blur[1].layers[0].mipmaps[0].texture; - } else if (i == 3) { - texture = rb->blur[0].layers[0].mipmaps[1].texture; - } - - // create weight texture - rb->weight_buffers[i].weight = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - // create frame buffer - Vector<RID> fb; - if (i != 0) { - fb.push_back(texture); - } - fb.push_back(rb->weight_buffers[i].weight); - rb->weight_buffers[i].fb = RD::get_singleton()->framebuffer_create(fb); +//////////////////////////////// +Ref<RenderSceneBuffers> RendererSceneRenderRD::render_buffers_create() { + Ref<RenderSceneBuffersRD> rb; + rb.instantiate(); - if (i == 1) { - // next 2 are half size - tf.width = MAX(1u, tf.width >> 1); - tf.height = MAX(1u, tf.height >> 1); - } - } + rb->set_can_be_storage(_render_buffers_can_be_storage()); + rb->set_max_cluster_elements(max_cluster_elements); + rb->set_base_data_format(_render_buffers_get_color_format()); + if (ss_effects) { + rb->set_sseffects(ss_effects); } -} - -void RendererSceneRenderRD::_allocate_depth_backbuffer_textures(RenderBuffers *rb) { - ERR_FAIL_COND(!rb->depth_back_texture.is_null()); - - { - RD::TextureFormat tf; - if (rb->view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } - // We're not using this as a depth stencil, just copying our data into this. May need to look into using a different format on mobile, maybe R16? - tf.format = RD::DATA_FORMAT_R32_SFLOAT; - - tf.width = rb->width; - tf.height = rb->height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.array_layers = rb->view_count; // create a layer for every view - - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; // set this as color attachment because we're copying data into it, it's not actually used as a depth buffer - - rb->depth_back_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + if (vrs) { + rb->set_vrs(vrs); } - if (!_render_buffers_can_be_storage()) { - // create framebuffer so we can write into this... - - Vector<RID> fb; - fb.push_back(rb->depth_back_texture); + setup_render_buffer_data(rb); - rb->depth_back_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count); - } + return rb; } -void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) { +void RendererSceneRenderRD::_allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb) { ERR_FAIL_COND(!rb->luminance.current.is_null()); - int w = rb->internal_width; - int h = rb->internal_height; + Size2i internal_size = rb->get_internal_size(); + int w = internal_size.x; + int h = internal_size.y; while (true) { w = MAX(w / 8, 1); @@ -1485,211 +1308,60 @@ void RendererSceneRenderRD::_allocate_luminance_textures(RenderBuffers *rb) { } } -void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) { - if (rb->views.size() > 1) { // if 1 these are copies ofs rb->internal_texture, rb->depth_texture and rb->texture_fb - for (int i = 0; i < rb->views.size(); i++) { - if (rb->views[i].view_fb.is_valid()) { - RD::get_singleton()->free(rb->views[i].view_fb); - } - if (rb->views[i].view_texture.is_valid()) { - RD::get_singleton()->free(rb->views[i].view_texture); - } - if (rb->views[i].view_depth.is_valid()) { - RD::get_singleton()->free(rb->views[i].view_depth); - } - } - } - rb->views.clear(); - - if (rb->texture_fb.is_valid()) { - RD::get_singleton()->free(rb->texture_fb); - rb->texture_fb = RID(); - } +void RendererSceneRenderRD::_process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera) { + ERR_FAIL_COND(p_render_buffers.is_null()); - if (rb->internal_texture == rb->texture && rb->internal_texture.is_valid()) { - RD::get_singleton()->free(rb->internal_texture); - rb->texture = RID(); - rb->internal_texture = RID(); - rb->upscale_texture = RID(); - } else { - if (rb->texture.is_valid()) { - RD::get_singleton()->free(rb->texture); - rb->texture = RID(); - } - - if (rb->internal_texture.is_valid()) { - RD::get_singleton()->free(rb->internal_texture); - rb->internal_texture = RID(); - } - - if (rb->upscale_texture.is_valid()) { - RD::get_singleton()->free(rb->upscale_texture); - rb->upscale_texture = RID(); - } - } - - if (rb->depth_texture.is_valid()) { - RD::get_singleton()->free(rb->depth_texture); - rb->depth_texture = RID(); - } - - if (rb->depth_back_fb.is_valid()) { - RD::get_singleton()->free(rb->depth_back_fb); - rb->depth_back_fb = RID(); - } - - if (rb->depth_back_texture.is_valid()) { - RD::get_singleton()->free(rb->depth_back_texture); - rb->depth_back_texture = RID(); - } - - if (rb->sss_texture.is_valid()) { - RD::get_singleton()->free(rb->sss_texture); - rb->sss_texture = RID(); - } - - if (rb->vrs_fb.is_valid()) { - RD::get_singleton()->free(rb->vrs_fb); - rb->vrs_fb = RID(); - } - - if (rb->vrs_texture.is_valid()) { - RD::get_singleton()->free(rb->vrs_texture); - rb->vrs_texture = RID(); - } - - for (int i = 0; i < 2; i++) { - for (int l = 0; l < rb->blur[i].layers.size(); l++) { - for (int m = 0; m < rb->blur[i].layers[l].mipmaps.size(); m++) { - // do we free the texture slice here? or is it enough to free the main texture? - - // do free the mobile extra stuff - if (rb->blur[i].layers[l].mipmaps[m].fb.is_valid()) { - RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].fb); - } - // texture and framebuffer in both blur mipmaps are shared, so only free from the first one - if (i == 0) { - if (rb->blur[i].layers[l].mipmaps[m].half_fb.is_valid()) { - RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].half_fb); - } - if (rb->blur[i].layers[l].mipmaps[m].half_texture.is_valid()) { - RD::get_singleton()->free(rb->blur[i].layers[l].mipmaps[m].half_texture); - } - } - } - } - rb->blur[i].layers.clear(); - - if (rb->blur[i].texture.is_valid()) { - RD::get_singleton()->free(rb->blur[i].texture); - rb->blur[i].texture = RID(); - } - } - - for (int i = 0; i < rb->luminance.fb.size(); i++) { - RD::get_singleton()->free(rb->luminance.fb[i]); - } - rb->luminance.fb.clear(); - - for (int i = 0; i < rb->luminance.reduce.size(); i++) { - RD::get_singleton()->free(rb->luminance.reduce[i]); - } - rb->luminance.reduce.clear(); - - if (rb->luminance.current_fb.is_valid()) { - RD::get_singleton()->free(rb->luminance.current_fb); - rb->luminance.current_fb = RID(); - } - - if (rb->luminance.current.is_valid()) { - RD::get_singleton()->free(rb->luminance.current); - rb->luminance.current = RID(); - } - - if (rb->ss_effects.linear_depth.is_valid()) { - RD::get_singleton()->free(rb->ss_effects.linear_depth); - rb->ss_effects.linear_depth = RID(); - rb->ss_effects.linear_depth_slices.clear(); - } - - ss_effects->ssao_free(rb->ss_effects.ssao); - ss_effects->ssil_free(rb->ss_effects.ssil); - ss_effects->ssr_free(rb->ssr); - - if (rb->taa.history.is_valid()) { - RD::get_singleton()->free(rb->taa.history); - rb->taa.history = RID(); - } - - if (rb->taa.temp.is_valid()) { - RD::get_singleton()->free(rb->taa.temp); - rb->taa.temp = RID(); - } - - if (rb->taa.prev_velocity.is_valid()) { - RD::get_singleton()->free(rb->taa.prev_velocity); - rb->taa.prev_velocity = RID(); - } - - rb->rbgi.free(); -} - -void RendererSceneRenderRD::_process_sss(RID p_render_buffers, const Projection &p_camera) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - - bool can_use_effects = rb->internal_width >= 8 && rb->internal_height >= 8; + Size2i internal_size = p_render_buffers->get_internal_size(); + bool can_use_effects = internal_size.x >= 8 && internal_size.y >= 8; if (!can_use_effects) { //just copy return; } - if (rb->blur[0].texture.is_null()) { - _allocate_blur_textures(rb); - } + p_render_buffers->allocate_blur_textures(); - RendererCompositorRD::singleton->get_effects()->sub_surface_scattering(rb->internal_texture, rb->sss_texture, rb->depth_texture, p_camera, Size2i(rb->internal_width, rb->internal_height), sss_scale, sss_depth_scale, sss_quality); + for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) { + RID internal_texture = p_render_buffers->get_internal_texture(v); + RID depth_texture = p_render_buffers->get_depth_texture(v); + ss_effects->sub_surface_scattering(p_render_buffers, internal_texture, depth_texture, p_camera, internal_size, sss_scale, sss_depth_scale, sss_quality); + } } -void RendererSceneRenderRD::_process_ssr(RID p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive) { +void RendererSceneRenderRD::_process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive) { ERR_FAIL_NULL(ss_effects); + ERR_FAIL_COND(p_render_buffers.is_null()); - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - - bool can_use_effects = rb->internal_width >= 8 && rb->internal_height >= 8; + Size2i internal_size = p_render_buffers->get_internal_size(); + bool can_use_effects = internal_size.x >= 8 && internal_size.y >= 8; + uint32_t view_count = p_render_buffers->get_view_count(); if (!can_use_effects) { //just copy - copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : rb->internal_texture, RID(), rb->view_count); + copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : p_render_buffers->get_internal_texture(), RID(), view_count); return; } ERR_FAIL_COND(p_environment.is_null()); - ERR_FAIL_COND(!environment_get_ssr_enabled(p_environment)); - Size2i half_size = Size2i(rb->internal_width / 2, rb->internal_height / 2); - if (rb->ssr.output.is_null()) { - ss_effects->ssr_allocate_buffers(rb->ssr, _render_buffers_get_color_format(), ssr_roughness_quality, half_size, rb->view_count); + Size2i half_size = Size2i(internal_size.x / 2, internal_size.y / 2); + if (p_render_buffers->ssr.output.is_null()) { + ss_effects->ssr_allocate_buffers(p_render_buffers->ssr, _render_buffers_get_color_format(), ssr_roughness_quality, half_size, view_count); } RID texture_slices[RendererSceneRender::MAX_RENDER_VIEWS]; RID depth_slices[RendererSceneRender::MAX_RENDER_VIEWS]; - for (uint32_t v = 0; v < rb->view_count; v++) { - texture_slices[v] = rb->views[v].view_texture; - depth_slices[v] = rb->views[v].view_depth; + for (uint32_t v = 0; v < view_count; v++) { + texture_slices[v] = p_render_buffers->get_internal_texture(v); + depth_slices[v] = p_render_buffers->get_depth_texture(v); } - ss_effects->screen_space_reflection(rb->ssr, texture_slices, p_normal_slices, ssr_roughness_quality, p_metallic_slices, p_metallic_mask, depth_slices, half_size, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), rb->view_count, p_projections, p_eye_offsets); - copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : rb->internal_texture, rb->ssr.output, rb->view_count); + ss_effects->screen_space_reflection(p_render_buffers->ssr, texture_slices, p_normal_slices, ssr_roughness_quality, p_metallic_slices, p_metallic_mask, depth_slices, half_size, environment_get_ssr_max_steps(p_environment), environment_get_ssr_fade_in(p_environment), environment_get_ssr_fade_out(p_environment), environment_get_ssr_depth_tolerance(p_environment), view_count, p_projections, p_eye_offsets); + copy_effects->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : p_render_buffers->get_internal_texture(), p_render_buffers->ssr.output, view_count); } -void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection) { +void RendererSceneRenderRD::_process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection) { ERR_FAIL_NULL(ss_effects); - - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - + ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_environment.is_null()); RENDER_TIMESTAMP("Process SSAO"); @@ -1708,18 +1380,15 @@ void RendererSceneRenderRD::_process_ssao(RID p_render_buffers, RID p_environmen settings.blur_passes = ssao_blur_passes; settings.fadeout_from = ssao_fadeout_from; settings.fadeout_to = ssao_fadeout_to; - settings.full_screen_size = Size2i(rb->internal_width, rb->internal_height); + settings.full_screen_size = p_render_buffers->get_internal_size(); - ss_effects->ssao_allocate_buffers(rb->ss_effects.ssao, settings, rb->ss_effects.linear_depth); - ss_effects->generate_ssao(rb->ss_effects.ssao, p_normal_buffer, p_projection, settings); + ss_effects->ssao_allocate_buffers(p_render_buffers->ss_effects.ssao, settings, p_render_buffers->ss_effects.linear_depth); + ss_effects->generate_ssao(p_render_buffers->ss_effects.ssao, p_normal_buffer, p_projection, settings); } -void RendererSceneRenderRD::_process_ssil(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection, const Transform3D &p_transform) { +void RendererSceneRenderRD::_process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection, const Transform3D &p_transform) { ERR_FAIL_NULL(ss_effects); - - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - + ERR_FAIL_COND(p_render_buffers.is_null()); ERR_FAIL_COND(p_environment.is_null()); RENDER_TIMESTAMP("Process SSIL"); @@ -1736,95 +1405,71 @@ void RendererSceneRenderRD::_process_ssil(RID p_render_buffers, RID p_environmen settings.blur_passes = ssil_blur_passes; settings.fadeout_from = ssil_fadeout_from; settings.fadeout_to = ssil_fadeout_to; - settings.full_screen_size = Size2i(rb->width, rb->height); + settings.full_screen_size = p_render_buffers->get_internal_size(); Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_projection; Transform3D transform = p_transform; transform.set_origin(Vector3(0.0, 0.0, 0.0)); - Projection last_frame_projection = rb->ss_effects.last_frame_projection * Projection(rb->ss_effects.last_frame_transform.affine_inverse()) * Projection(transform) * projection.inverse(); + Projection last_frame_projection = p_render_buffers->ss_effects.last_frame_projection * Projection(p_render_buffers->ss_effects.last_frame_transform.affine_inverse()) * Projection(transform) * projection.inverse(); - ss_effects->ssil_allocate_buffers(rb->ss_effects.ssil, settings, rb->ss_effects.linear_depth); - ss_effects->screen_space_indirect_lighting(rb->ss_effects.ssil, p_normal_buffer, p_projection, last_frame_projection, settings); - rb->ss_effects.last_frame_projection = projection; - rb->ss_effects.last_frame_transform = transform; + ss_effects->ssil_allocate_buffers(p_render_buffers->ss_effects.ssil, settings, p_render_buffers->ss_effects.linear_depth); + ss_effects->screen_space_indirect_lighting(p_render_buffers->ss_effects.ssil, p_normal_buffer, p_projection, last_frame_projection, settings); + p_render_buffers->ss_effects.last_frame_projection = projection; + p_render_buffers->ss_effects.last_frame_transform = transform; } -void RendererSceneRenderRD::_copy_framebuffer_to_ssil(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); +void RendererSceneRenderRD::_copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers) { + ERR_FAIL_COND(p_render_buffers.is_null()); - if (rb->ss_effects.ssil.last_frame.is_valid()) { - copy_effects->copy_to_rect(rb->texture, rb->ss_effects.ssil.last_frame, Rect2i(0, 0, rb->width, rb->height)); + if (p_render_buffers->ss_effects.ssil.last_frame.is_valid()) { + Size2i size = p_render_buffers->get_internal_size(); + RID texture = p_render_buffers->get_internal_texture(); + copy_effects->copy_to_rect(texture, p_render_buffers->ss_effects.ssil.last_frame, Rect2i(0, 0, size.x, size.y)); - int width = rb->width; - int height = rb->height; - for (int i = 0; i < rb->ss_effects.ssil.last_frame_slices.size() - 1; i++) { + int width = size.x; + int height = size.y; + for (int i = 0; i < p_render_buffers->ss_effects.ssil.last_frame_slices.size() - 1; i++) { width = MAX(1, width >> 1); height = MAX(1, height >> 1); - copy_effects->make_mipmap(rb->ss_effects.ssil.last_frame_slices[i], rb->ss_effects.ssil.last_frame_slices[i + 1], Size2i(width, height)); + copy_effects->make_mipmap(p_render_buffers->ss_effects.ssil.last_frame_slices[i], p_render_buffers->ss_effects.ssil.last_frame_slices[i + 1], Size2i(width, height)); } } } -void RendererSceneRenderRD::_process_taa(RID p_render_buffers, RID p_velocity_buffer, float p_z_near, float p_z_far) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - - bool just_allocated = false; - if (rb->taa.history.is_null()) { - RD::TextureFormat tf; - if (rb->view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } - tf.format = _render_buffers_get_color_format(); - tf.width = rb->internal_width; - tf.height = rb->internal_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); - - rb->taa.history = RD::get_singleton()->texture_create(tf, RD::TextureView()); - rb->taa.temp = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - tf.format = RD::DATA_FORMAT_R16G16_SFLOAT; - rb->taa.prev_velocity = RD::get_singleton()->texture_create(tf, RD::TextureView()); - just_allocated = true; - } - - RD::get_singleton()->draw_command_begin_label("TAA"); - if (!just_allocated) { - RendererCompositorRD::singleton->get_effects()->taa_resolve(rb->internal_texture, rb->taa.temp, rb->depth_texture, p_velocity_buffer, rb->taa.prev_velocity, rb->taa.history, Size2(rb->internal_width, rb->internal_height), p_z_near, p_z_far); - copy_effects->copy_to_rect(rb->taa.temp, rb->internal_texture, Rect2(0, 0, rb->internal_width, rb->internal_height)); - } - - copy_effects->copy_to_rect(rb->internal_texture, rb->taa.history, Rect2(0, 0, rb->internal_width, rb->internal_height)); - copy_effects->copy_to_rect(p_velocity_buffer, rb->taa.prev_velocity, Rect2(0, 0, rb->width, rb->height)); - RD::get_singleton()->draw_command_end_label(); -} - void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderDataRD *p_render_data) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); RD::get_singleton()->draw_command_begin_label("Copy screen texture"); - if (rb->blur[0].texture.is_null()) { - _allocate_blur_textures(rb); - } + rb->allocate_blur_textures(); bool can_use_storage = _render_buffers_can_be_storage(); + Size2i size = rb->get_internal_size(); + + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RID texture = rb->get_internal_texture(v); + int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0).mipmaps); + RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, 0); - for (uint32_t v = 0; v < rb->view_count; v++) { if (can_use_storage) { - copy_effects->copy_to_rect(rb->views[v].view_texture, rb->blur[0].layers[v].mipmaps[0].texture, Rect2i(0, 0, rb->width, rb->height)); - for (int i = 1; i < rb->blur[0].layers[v].mipmaps.size(); i++) { - copy_effects->make_mipmap(rb->blur[0].layers[v].mipmaps[i - 1].texture, rb->blur[0].layers[v].mipmaps[i].texture, Size2i(rb->blur[0].layers[v].mipmaps[i].width, rb->blur[0].layers[v].mipmaps[i].height)); - } + copy_effects->copy_to_rect(texture, dest, Rect2i(0, 0, size.x, size.y)); } else { - copy_effects->copy_to_fb_rect(rb->views[v].view_texture, rb->blur[0].layers[v].mipmaps[0].fb, Rect2i(0, 0, rb->width, rb->height)); - for (int i = 1; i < rb->blur[0].layers[v].mipmaps.size(); i++) { - copy_effects->make_mipmap_raster(rb->blur[0].layers[v].mipmaps[i - 1].texture, rb->blur[0].layers[v].mipmaps[i].fb, Size2i(rb->blur[0].layers[v].mipmaps[i].width, rb->blur[0].layers[v].mipmaps[i].height)); + RID fb = FramebufferCacheRD::get_singleton()->get_cache(dest); + copy_effects->copy_to_fb_rect(texture, fb, Rect2i(0, 0, size.x, size.y)); + } + + for (int i = 1; i < mipmaps; i++) { + RID source = dest; + dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, i); + Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, i); + + if (can_use_storage) { + copy_effects->make_mipmap(source, dest, msize); + } else { + copy_effects->make_mipmap_raster(source, dest, msize); } } } @@ -1833,23 +1478,30 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData } void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataRD *p_render_data) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); RD::get_singleton()->draw_command_begin_label("Copy depth texture"); - if (rb->depth_back_texture.is_null()) { - _allocate_depth_backbuffer_textures(rb); - } + // note, this only creates our back depth texture if we haven't already created it. + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; // set this as color attachment because we're copying data into it, it's not actually used as a depth buffer - // @TODO IMPLEMENT MULTIVIEW, all effects need to support stereo buffers or effects are only applied to the left eye + rb->create_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH, RD::DATA_FORMAT_R32_SFLOAT, usage_bits, RD::TEXTURE_SAMPLES_1); bool can_use_storage = _render_buffers_can_be_storage(); + Size2i size = rb->get_internal_size(); + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + RID depth_texture = rb->get_depth_texture(v); + RID depth_back_texture = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BACK_DEPTH, v, 0); - if (can_use_storage) { - copy_effects->copy_to_rect(rb->depth_texture, rb->depth_back_texture, Rect2i(0, 0, rb->width, rb->height)); - } else { - copy_effects->copy_to_fb_rect(rb->depth_texture, rb->depth_back_fb, Rect2i(0, 0, rb->width, rb->height)); + if (can_use_storage) { + copy_effects->copy_to_rect(depth_texture, depth_back_texture, Rect2i(0, 0, size.x, size.y)); + } else { + RID depth_back_fb = FramebufferCacheRD::get_singleton()->get_cache(depth_back_texture); + copy_effects->copy_to_fb_rect(depth_texture, depth_back_fb, Rect2i(0, 0, size.x, size.y)); + } } RD::get_singleton()->draw_command_end_label(); @@ -1857,40 +1509,44 @@ void RendererSceneRenderRD::_render_buffers_copy_depth_texture(const RenderDataR void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); - // Glow and override exposure (if enabled). - CameraEffects *camfx = camera_effects_owner.get_or_null(p_render_data->camera_effects); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + + // Glow, auto exposure and DoF (if enabled). - bool can_use_effects = rb->width >= 8 && rb->height >= 8; + Size2i internal_size = rb->get_internal_size(); + Size2i target_size = rb->get_target_size(); + + bool can_use_effects = target_size.x >= 8 && target_size.y >= 8; // FIXME I think this should check internal size, we do all our post processing at this size... bool can_use_storage = _render_buffers_can_be_storage(); - if (can_use_effects && camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0) { + RID render_target = rb->get_render_target(); + RID internal_texture = rb->get_internal_texture(); + + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes)) { RENDER_TIMESTAMP("Depth of Field"); RD::get_singleton()->draw_command_begin_label("DOF"); - if (rb->blur[0].texture.is_null()) { - _allocate_blur_textures(rb); - } + + rb->allocate_blur_textures(); RendererRD::BokehDOF::BokehBuffers buffers; // Textures we use - buffers.base_texture_size = Size2i(rb->internal_width, rb->internal_height); - buffers.secondary_texture = rb->blur[0].layers[0].mipmaps[0].texture; - buffers.half_texture[0] = rb->blur[1].layers[0].mipmaps[0].texture; - buffers.half_texture[1] = rb->blur[0].layers[0].mipmaps[1].texture; + buffers.base_texture_size = rb->get_internal_size(); + buffers.secondary_texture = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 0, 0); + buffers.half_texture[0] = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, 0, 0); + buffers.half_texture[1] = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 0, 1); - float bokeh_size = camfx->dof_blur_amount * 64.0; if (can_use_storage) { - for (uint32_t i = 0; i < rb->view_count; i++) { - buffers.base_texture = rb->views[i].view_texture; - buffers.depth_texture = rb->views[i].view_depth; + for (uint32_t i = 0; i < rb->get_view_count(); i++) { + buffers.base_texture = rb->get_internal_texture(i); + buffers.depth_texture = rb->get_depth_texture(i); // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum float z_near = p_render_data->view_projection[i].get_z_near(); float z_far = p_render_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_compute(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, z_near, z_far, p_render_data->cam_orthogonal); + bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->cam_orthogonal); }; } else { // Set framebuffers. @@ -1905,35 +1561,40 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // Set weight buffers. buffers.base_weight_fb = rb->weight_buffers[0].fb; - for (uint32_t i = 0; i < rb->view_count; i++) { - buffers.base_texture = rb->views[i].view_texture; - buffers.depth_texture = rb->views[i].view_depth; - buffers.base_fb = rb->views[i].view_fb; + for (uint32_t i = 0; i < rb->get_view_count(); i++) { + buffers.base_texture = rb->get_internal_texture(i); + buffers.depth_texture = rb->get_depth_texture(i); + buffers.base_fb = FramebufferCacheRD::get_singleton()->get_cache(buffers.base_texture); // TODO move this into bokeh_dof_raster, we can do this internally // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum float z_near = p_render_data->view_projection[i].get_z_near(); float z_far = p_render_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_raster(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, z_near, z_far, p_render_data->cam_orthogonal); + bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->cam_orthogonal); } } RD::get_singleton()->draw_command_end_label(); } - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment)) { + float auto_exposure_scale = 1.0; + + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { RENDER_TIMESTAMP("Auto exposure"); + RD::get_singleton()->draw_command_begin_label("Auto exposure"); if (rb->luminance.current.is_null()) { _allocate_luminance_textures(rb); } + uint64_t auto_exposure_version = RSG::camera_attributes->camera_attributes_get_auto_exposure_version(p_render_data->camera_attributes); + bool set_immediate = auto_exposure_version != rb->get_auto_exposure_version(); + rb->set_auto_exposure_version(auto_exposure_version); - bool set_immediate = environment_get_auto_exposure_version(p_render_data->environment) != rb->auto_exposure_version; - rb->auto_exposure_version = environment_get_auto_exposure_version(p_render_data->environment); - - double step = environment_get_auto_exp_speed(p_render_data->environment) * time_step; + double step = RSG::camera_attributes->camera_attributes_get_auto_exposure_adjust_speed(p_render_data->camera_attributes) * time_step; + float auto_exposure_min_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_min_sensitivity(p_render_data->camera_attributes); + float auto_exposure_max_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_max_sensitivity(p_render_data->camera_attributes); if (can_use_storage) { - RendererCompositorRD::singleton->get_effects()->luminance_reduction(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.current, environment_get_min_luminance(p_render_data->environment), environment_get_max_luminance(p_render_data->environment), step, set_immediate); + RendererCompositorRD::singleton->get_effects()->luminance_reduction(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); } else { - RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, environment_get_min_luminance(p_render_data->environment), environment_get_max_luminance(p_render_data->environment), step, set_immediate); + RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(internal_texture, internal_size, rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); } // Swap final reduce with prev luminance. SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]); @@ -1941,6 +1602,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]); } + auto_exposure_scale = RSG::camera_attributes->camera_attributes_get_auto_exposure_scale(p_render_data->camera_attributes); + RenderingServerDefault::redraw_request(); // Redraw all the time if auto exposure rendering is on. RD::get_singleton()->draw_command_end_label(); } @@ -1951,16 +1614,13 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RENDER_TIMESTAMP("Glow"); RD::get_singleton()->draw_command_begin_label("Gaussian Glow"); - /* see that blur textures are allocated */ - - if (rb->blur[1].texture.is_null()) { - _allocate_blur_textures(rb); - } + rb->allocate_blur_textures(); for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) { if (environment_get_glow_levels(p_render_data->environment)[i] > 0.0) { - if (i >= rb->blur[1].layers[0].mipmaps.size()) { - max_glow_level = rb->blur[1].layers[0].mipmaps.size() - 1; + int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1).mipmaps); + if (i >= mipmaps) { + max_glow_level = mipmaps - 1; } else { max_glow_level = i; } @@ -1968,26 +1628,32 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende } float luminance_multiplier = _render_buffers_get_luminance_multiplier(); - for (uint32_t l = 0; l < rb->view_count; l++) { + for (uint32_t l = 0; l < rb->get_view_count(); l++) { for (int i = 0; i < (max_glow_level + 1); i++) { - int vp_w = rb->blur[1].layers[l].mipmaps[i].width; - int vp_h = rb->blur[1].layers[l].mipmaps[i].height; + Size2i vp_size = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i); if (i == 0) { RID luminance_texture; - if (environment_get_auto_exposure(p_render_data->environment) && rb->luminance.current.is_valid()) { + if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { luminance_texture = rb->luminance.current; } + RID source = rb->get_internal_texture(l); + RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i); if (can_use_storage) { - copy_effects->gaussian_glow(rb->views[l].view_texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, environment_get_auto_exp_scale(p_render_data->environment)); + copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale); } else { - copy_effects->gaussian_glow_raster(rb->views[l].view_texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, environment_get_auto_exp_scale(p_render_data->environment)); + RID half = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, 0, i); // we can reuse this for each view + copy_effects->gaussian_glow_raster(source, half, dest, luminance_multiplier, vp_size, environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale); } } else { + RID source = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i - 1); + RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, l, i); + if (can_use_storage) { - copy_effects->gaussian_glow(rb->blur[1].layers[l].mipmaps[i - 1].texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality); + copy_effects->gaussian_glow(source, dest, vp_size, environment_get_glow_strength(p_render_data->environment), glow_high_quality); } else { - copy_effects->gaussian_glow_raster(rb->blur[1].layers[l].mipmaps[i - 1].texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality); + RID half = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, 0, i); // we can reuse this for each view + copy_effects->gaussian_glow_raster(source, half, dest, luminance_multiplier, vp_size, environment_get_glow_strength(p_render_data->environment), glow_high_quality); } } } @@ -2002,10 +1668,10 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RendererRD::ToneMapper::TonemapSettings tonemap; - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment) && rb->luminance.current.is_valid()) { + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { tonemap.use_auto_exposure = true; tonemap.exposure_texture = rb->luminance.current; - tonemap.auto_exposure_grey = environment_get_auto_exp_scale(p_render_data->environment); + tonemap.auto_exposure_scale = auto_exposure_scale; } else { tonemap.exposure_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); } @@ -2017,10 +1683,12 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende for (int i = 0; i < RS::MAX_GLOW_LEVELS; i++) { tonemap.glow_levels[i] = environment_get_glow_levels(p_render_data->environment)[i]; } - tonemap.glow_texture_size.x = rb->blur[1].layers[0].mipmaps[0].width; - tonemap.glow_texture_size.y = rb->blur[1].layers[0].mipmaps[0].height; + + Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, 0, 0); + tonemap.glow_texture_size.x = msize.width; + tonemap.glow_texture_size.y = msize.height; tonemap.glow_use_bicubic_upscale = glow_bicubic_upscale; - tonemap.glow_texture = rb->blur[1].texture; + tonemap.glow_texture = rb->get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1); if (environment_get_glow_map(p_render_data->environment).is_valid()) { tonemap.glow_map_strength = environment_get_glow_map_strength(p_render_data->environment); tonemap.glow_map = texture_storage->texture_get_rd_texture(environment_get_glow_map(p_render_data->environment)); @@ -2034,12 +1702,12 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.glow_map = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); } - if (rb->screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) { + if (rb->get_screen_space_aa() == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) { tonemap.use_fxaa = true; } - tonemap.use_debanding = rb->use_debanding; - tonemap.texture_size = Vector2i(rb->internal_width, rb->internal_height); + tonemap.use_debanding = rb->get_use_debanding(); + tonemap.texture_size = Vector2i(rb->get_internal_size().x, rb->get_internal_size().y); if (p_render_data->environment.is_valid()) { tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment); @@ -2047,10 +1715,6 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.exposure = environment_get_exposure(p_render_data->environment); } - if (camfx && camfx->override_exposure_enabled) { - tonemap.exposure = camfx->override_exposure; - } - tonemap.use_color_correction = false; tonemap.use_1d_color_correction = false; tonemap.color_correction_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); @@ -2068,35 +1732,56 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende } tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); - tonemap.view_count = p_render_data->view_count; + tonemap.view_count = rb->get_view_count(); + + RID dest_fb; + if (fsr && can_use_effects && (internal_size.x != target_size.x || internal_size.y != target_size.y)) { + // If we use FSR to upscale we need to write our result into an intermediate buffer. + // Note that this is cached so we only create the texture the first time. + RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT); + dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture); + } else { + // If we do a bilinear upscale we just render into our render target and our shader will upscale automatically. + // Target size in this case is lying as we never get our real target size communicated. + // Bit nasty but... + dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target); + } - tone_mapper->tonemapper(rb->internal_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); + tone_mapper->tonemapper(internal_texture, dest_fb, tonemap); RD::get_singleton()->draw_command_end_label(); } - if (can_use_effects && can_use_storage && (rb->internal_width != rb->width || rb->internal_height != rb->height)) { + if (fsr && can_use_effects && (internal_size.x != target_size.x || internal_size.y != target_size.y)) { + // TODO Investigate? Does this work? We never write into our render target and we've already done so up above in our tonemapper. + // I think FSR should either work before our tonemapper or as an alternative of our tonemapper. + RD::get_singleton()->draw_command_begin_label("FSR 1.0 Upscale"); - RendererCompositorRD::singleton->get_effects()->fsr_upscale(rb->internal_texture, rb->upscale_texture, rb->texture, Size2i(rb->internal_width, rb->internal_height), Size2i(rb->width, rb->height), rb->fsr_sharpness); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0); + RID dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v); + + fsr->fsr_upscale(rb, source_texture, dest_texture); + } RD::get_singleton()->draw_command_end_label(); } - texture_storage->render_target_disable_clear_request(rb->render_target); + texture_storage->render_target_disable_clear_request(render_target); } void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RD::get_singleton()->draw_command_begin_label("Post Process Subpass"); - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); - // Override exposure (if enabled). - CameraEffects *camfx = camera_effects_owner.get_or_null(p_render_data->camera_effects); - - bool can_use_effects = rb->width >= 8 && rb->height >= 8; + // FIXME: Our input it our internal_texture, shouldn't this be using internal_size ?? + // Seeing we don't support FSR in our mobile renderer right now target_size = internal_size... + Size2i target_size = rb->get_target_size(); + bool can_use_effects = target_size.x >= 8 && target_size.y >= 8; RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); @@ -2108,18 +1793,15 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr tonemap.white = environment_get_white(p_render_data->environment); } - if (camfx && camfx->override_exposure_enabled) { - tonemap.exposure = camfx->override_exposure; - } - // 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 && p_render_data->environment.is_valid() && environment_get_glow_enabled(p_render_data->environment)) { ERR_FAIL_MSG("Glow is not supported when using subpasses."); } - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment)) { - ERR_FAIL_MSG("Glow is not supported when using subpasses."); + + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { + ERR_FAIL_MSG("Auto Exposure is not supported when using subpasses."); } tonemap.use_glow = false; @@ -2144,11 +1826,11 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr } } - tonemap.use_debanding = rb->use_debanding; - tonemap.texture_size = Vector2i(rb->width, rb->height); + tonemap.use_debanding = rb->get_use_debanding(); + tonemap.texture_size = Vector2i(target_size.x, target_size.y); tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); - tonemap.view_count = p_render_data->view_count; + tonemap.view_count = rb->get_view_count(); tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap); @@ -2156,18 +1838,18 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr } void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_data) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); + ERR_FAIL_COND(p_render_data->render_buffers.is_null()); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - texture_storage->render_target_disable_clear_request(rb->render_target); + texture_storage->render_target_disable_clear_request(p_render_data->render_buffers->get_render_target()); } -void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { +void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); + ERR_FAIL_COND(p_render_buffers.is_null()); + + RID render_target = p_render_buffers->get_render_target(); if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS) { if (p_shadow_atlas.is_valid()) { @@ -2177,17 +1859,17 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID shadow_atlas_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK); } - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true); + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, true); } } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS) { if (directional_shadow_get_texture().is_valid()) { RID shadow_atlas_texture = directional_shadow_get_texture(); - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); + Size2 rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true); + copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, true); } } @@ -2195,247 +1877,59 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID RID decal_atlas = RendererRD::TextureStorage::get_singleton()->decal_atlas_get_texture(); if (decal_atlas.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); + Size2 rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(decal_atlas, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, false, true); + copy_effects->copy_to_fb_rect(decal_atlas, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, false, true); } } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) { - if (rb->luminance.current.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); + if (p_render_buffers->luminance.current.is_valid()) { + Size2 rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(rb->luminance.current, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8), false, true); + copy_effects->copy_to_fb_rect(p_render_buffers->luminance.current, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize / 8), false, true); } } - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSAO && rb->ss_effects.ssao.ao_final.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(rb->ss_effects.ssao.ao_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSAO && p_render_buffers->ss_effects.ssao.ao_final.is_valid()) { + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(p_render_buffers->ss_effects.ssao.ao_final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, true); } - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSIL && rb->ss_effects.ssil.ssil_final.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(rb->ss_effects.ssil.ssil_final, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false); + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SSIL && p_render_buffers->ss_effects.ssil.ssil_final.is_valid()) { + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(p_render_buffers->ss_effects.ssil.ssil_final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false); + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); } - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->rbgi.ambient_buffer.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - RID ambient_texture = rb->rbgi.ambient_buffer; - RID reflection_texture = rb->rbgi.reflection_buffer; - copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, rb->view_count > 1); + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && p_render_buffers->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) { + Size2 rtsize = texture_storage->render_target_get_size(render_target); + RID ambient_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT); + RID reflection_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION); + copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, p_render_buffers->get_view_count() > 1); } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { if (p_occlusion_buffer.is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize), true, false); + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize), true, false); } } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(p_render_buffers).is_valid()) { - Size2 rtsize = texture_storage->render_target_get_size(rb->render_target); - copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false); - } -} - -RID RendererSceneRenderRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - if (!rb->blur[0].texture.is_valid()) { - return RID(); //not valid at the moment - } - return rb->blur[0].texture; -} - -RID RendererSceneRenderRD::render_buffers_get_back_depth_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - if (!rb->depth_back_texture.is_valid()) { - return RID(); //not valid at the moment - } - return rb->depth_back_texture; -} - -RID RendererSceneRenderRD::render_buffers_get_depth_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - - return rb->depth_texture; -} - -RID RendererSceneRenderRD::render_buffers_get_ao_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - - return rb->ss_effects.ssao.ao_final; -} -RID RendererSceneRenderRD::render_buffers_get_ssil_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - - return rb->ss_effects.ssil.ssil_final; -} - -RID RendererSceneRenderRD::render_buffers_get_voxel_gi_buffer(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - if (rb->rbgi.voxel_gi_buffer.is_null()) { - rb->rbgi.voxel_gi_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(RendererRD::GI::VoxelGIData) * RendererRD::GI::MAX_VOXEL_GI_INSTANCES); + Size2 rtsize = texture_storage->render_target_get_size(render_target); + copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); } - return rb->rbgi.voxel_gi_buffer; } RID RendererSceneRenderRD::render_buffers_get_default_voxel_gi_buffer() { return gi.default_voxel_gi_buffer; } -RID RendererSceneRenderRD::render_buffers_get_gi_ambient_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - - return rb->rbgi.ambient_buffer; -} -RID RendererSceneRenderRD::render_buffers_get_gi_reflection_texture(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - return rb->rbgi.reflection_buffer; -} - -uint32_t RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0); - ERR_FAIL_COND_V(!rb->sdfgi, 0); - - return rb->sdfgi->cascades.size(); -} -bool RendererSceneRenderRD::render_buffers_is_sdfgi_enabled(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, false); - - return rb->sdfgi != nullptr; -} -RID RendererSceneRenderRD::render_buffers_get_sdfgi_irradiance_probes(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - ERR_FAIL_COND_V(!rb->sdfgi, RID()); - - return rb->sdfgi->lightprobe_texture; -} - -Vector3 RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_offset(RID p_render_buffers, uint32_t p_cascade) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, Vector3()); - ERR_FAIL_COND_V(!rb->sdfgi, Vector3()); - ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3()); - - return Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[p_cascade].position)) * rb->sdfgi->cascades[p_cascade].cell_size; -} - -Vector3i RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_probe_offset(RID p_render_buffers, uint32_t p_cascade) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, Vector3i()); - ERR_FAIL_COND_V(!rb->sdfgi, Vector3i()); - ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3i()); - int32_t probe_divisor = rb->sdfgi->cascade_size / RendererRD::GI::SDFGI::PROBE_DIVISOR; - - return rb->sdfgi->cascades[p_cascade].position / probe_divisor; -} - -float RendererSceneRenderRD::render_buffers_get_sdfgi_normal_bias(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0); - ERR_FAIL_COND_V(!rb->sdfgi, 0); - - return rb->sdfgi->normal_bias; -} -float RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_probe_size(RID p_render_buffers, uint32_t p_cascade) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0); - ERR_FAIL_COND_V(!rb->sdfgi, 0); - ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), 0); - - return float(rb->sdfgi->cascade_size) * rb->sdfgi->cascades[p_cascade].cell_size / float(rb->sdfgi->probe_axis_count - 1); -} -uint32_t RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_probe_count(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0); - ERR_FAIL_COND_V(!rb->sdfgi, 0); - - return rb->sdfgi->probe_axis_count; -} - -uint32_t RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_size(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0); - ERR_FAIL_COND_V(!rb->sdfgi, 0); - - return rb->sdfgi->cascade_size; -} - -bool RendererSceneRenderRD::render_buffers_is_sdfgi_using_occlusion(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, false); - ERR_FAIL_COND_V(!rb->sdfgi, false); - - return rb->sdfgi->uses_occlusion; -} - -float RendererSceneRenderRD::render_buffers_get_sdfgi_energy(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, 0.0); - ERR_FAIL_COND_V(!rb->sdfgi, 0.0); - - return rb->sdfgi->energy; -} -RID RendererSceneRenderRD::render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - ERR_FAIL_COND_V(!rb->sdfgi, RID()); - - return rb->sdfgi->occlusion_texture; -} - -bool RendererSceneRenderRD::render_buffers_has_volumetric_fog(RID p_render_buffers) const { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, false); - - return rb->volumetric_fog != nullptr; -} -RID RendererSceneRenderRD::render_buffers_get_volumetric_fog_texture(RID p_render_buffers) { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, RID()); - - return rb->volumetric_fog->fog_map; -} - -RID RendererSceneRenderRD::render_buffers_get_volumetric_fog_sky_uniform_set(RID p_render_buffers) { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, RID()); - - if (!rb->volumetric_fog) { - return RID(); - } - - return rb->volumetric_fog->sky_uniform_set; -} - -float RendererSceneRenderRD::render_buffers_get_volumetric_fog_end(RID p_render_buffers) { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); - return rb->volumetric_fog->length; -} -float RendererSceneRenderRD::render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers) { - const RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); - return rb->volumetric_fog->spread; -} - float RendererSceneRenderRD::_render_buffers_get_luminance_multiplier() { return 1.0; } @@ -2448,157 +1942,6 @@ bool RendererSceneRenderRD::_render_buffers_can_be_storage() { return true; } -void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { - RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); - RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); - - ERR_FAIL_COND_MSG(p_view_count == 0, "Must have at least 1 view"); - - if (!_render_buffers_can_be_storage()) { - p_internal_height = p_height; - p_internal_width = p_width; - } - - if (p_use_taa) { - // Use negative mipmap LOD bias when TAA is enabled to compensate for loss of sharpness. - // This restores sharpness in still images to be roughly at the same level as without TAA, - // but moving scenes will still be blurrier. - p_texture_mipmap_bias -= 0.5; - } - - if (p_screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) { - // Use negative mipmap LOD bias when FXAA is enabled to compensate for loss of sharpness. - // If both TAA and FXAA are enabled, combine their negative LOD biases together. - p_texture_mipmap_bias -= 0.25; - } - - material_storage->sampler_rd_configure_custom(p_texture_mipmap_bias); - update_uniform_sets(); - - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - - // Should we add an overrule per viewport? - rb->internal_width = p_internal_width; - rb->internal_height = p_internal_height; - rb->width = p_width; - rb->height = p_height; - rb->fsr_sharpness = p_fsr_sharpness; - rb->render_target = p_render_target; - rb->msaa_3d = p_msaa; - rb->screen_space_aa = p_screen_space_aa; - rb->use_taa = p_use_taa; - rb->use_debanding = p_use_debanding; - rb->view_count = p_view_count; - - if (is_clustered_enabled()) { - if (rb->cluster_builder == nullptr) { - rb->cluster_builder = memnew(ClusterBuilderRD); - } - rb->cluster_builder->set_shared(&cluster_builder_shared); - } - - _free_render_buffer_data(rb); - - { - RD::TextureFormat tf; - if (rb->view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } - tf.format = _render_buffers_get_color_format(); - tf.width = rb->internal_width; // If set to rb->width, msaa won't crash - tf.height = rb->internal_height; // If set to rb->width, msaa won't crash - 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) | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - if (rb->msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { - 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->internal_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - if ((p_internal_width != p_width || p_internal_height != p_height)) { - tf.width = rb->width; - tf.height = rb->height; - rb->texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - rb->upscale_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - } else { - rb->texture = rb->internal_texture; - rb->upscale_texture = rb->internal_texture; - } - } - - { - RD::TextureFormat tf; - if (rb->view_count > 1) { - tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } - if (rb->msaa_3d == RS::VIEWPORT_MSAA_DISABLED) { - tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, (RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT)) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; - } else { - tf.format = RD::DATA_FORMAT_R32_SFLOAT; - } - - tf.width = rb->internal_width; - tf.height = rb->internal_height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; - tf.array_layers = rb->view_count; // create a layer for every view - - if (rb->msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - - rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); - } - - { - if (!_render_buffers_can_be_storage()) { - // ONLY USED ON MOBILE RENDERER, ONLY USED FOR POST EFFECTS! - Vector<RID> fb; - fb.push_back(rb->internal_texture); - - rb->texture_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, rb->view_count); - } - - rb->views.clear(); // JIC - if (rb->view_count == 1) { - // copy as a convenience - RenderBuffers::View view; - view.view_texture = rb->texture; - view.view_depth = rb->depth_texture; - view.view_fb = rb->texture_fb; - rb->views.push_back(view); - } else { - for (uint32_t i = 0; i < rb->view_count; i++) { - RenderBuffers::View view; - view.view_texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->texture, i, 0); - view.view_depth = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->depth_texture, i, 0); - - if (!_render_buffers_can_be_storage()) { - Vector<RID> fb; - fb.push_back(view.view_texture); - view.view_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, 1); - } - - rb->views.push_back(view); - } - } - } - - RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(rb->render_target); - if (is_vrs_supported() && vrs_mode != RS::VIEWPORT_VRS_DISABLED) { - vrs->create_vrs_texture(p_internal_width, p_internal_height, p_view_count, rb->vrs_texture, rb->vrs_fb); - } - - RID target_texture = texture_storage->render_target_get_rd_texture(rb->render_target); - rb->data->configure(rb->internal_texture, rb->depth_texture, target_texture, p_internal_width, p_internal_height, p_msaa, p_use_taa, p_view_count, rb->vrs_texture); - - if (is_clustered_enabled()) { - rb->cluster_builder->setup(Size2i(p_internal_width, p_internal_height), max_cluster_elements, rb->depth_texture, RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->internal_texture); - } -} - void RendererSceneRenderRD::gi_set_use_half_resolution(bool p_enable) { gi.half_resolution = p_enable; } @@ -2733,13 +2076,7 @@ bool RendererSceneRenderRD::is_using_radiance_cubemap_array() const { return sky.sky_use_cubemap_array; } -RendererSceneRenderRD::RenderBufferData *RendererSceneRenderRD::render_buffers_get_data(RID p_render_buffers) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND_V(!rb, nullptr); - return rb->data; -} - -void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment) { +void RendererSceneRenderRD::_setup_reflections(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); cluster.reflection_count = 0; @@ -2796,6 +2133,12 @@ void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflecti reflection_ubo.exterior = !light_storage->reflection_probe_is_interior(base_probe); reflection_ubo.box_project = light_storage->reflection_probe_is_box_projection(base_probe); + reflection_ubo.exposure_normalization = 1.0; + + if (p_render_data->camera_attributes.is_valid()) { + float exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + reflection_ubo.exposure_normalization = exposure / light_storage->reflection_probe_get_baked_exposure(base_probe); + } Color ambient_linear = light_storage->reflection_probe_get_ambient_color(base_probe).srgb_to_linear(); float interior_ambient_energy = light_storage->reflection_probe_get_ambient_color_energy(base_probe); @@ -2819,7 +2162,7 @@ void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflecti } } -void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows) { +void RendererSceneRenderRD::_setup_lights(RenderDataRD *p_render_data, const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); @@ -2863,7 +2206,17 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const float sign = light_storage->light_is_negative(base) ? -1 : 1; - light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + if (is_using_physical_light_units()) { + light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } else { + light_data.energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); light_data.color[0] = linear_col.r; @@ -3076,7 +2429,26 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const } } - float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * fade; + + if (is_using_physical_light_units()) { + energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (type == RS::LIGHT_OMNI) { + energy *= 1.0 / (Math_PI * 4.0); + } else { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + energy *= 1.0 / Math_PI; + } + } else { + energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } light_data.color[0] = linear_col.r * energy; light_data.color[1] = linear_col.g * energy; @@ -3416,20 +2788,29 @@ void RendererSceneRenderRD::_setup_decals(const PagedArray<RID> &p_decals, const //////////////////////////////////////////////////////////////////////////////// // FOG SHADER -void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes) { +void RendererSceneRenderRD::_update_volumetric_fog(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes) { ERR_FAIL_COND(!is_clustered_enabled()); // can't use volumetric fog without clustered - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); + ERR_FAIL_COND(p_render_buffers.is_null()); + + // These should be available for our clustered renderer, at some point _update_volumetric_fog should be called by the renderer implemetentation itself + ERR_FAIL_COND(!p_render_buffers->has_custom_data(RB_SCOPE_GI)); + Ref<RendererRD::GI::RenderBuffersGI> rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI); - float ratio = float(rb->width) / float((rb->width + rb->height) / 2); + Ref<RendererRD::GI::SDFGI> sdfgi; + if (p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); + } + + Size2i size = p_render_buffers->get_internal_size(); + float ratio = float(size.x) / float((size.x + size.y) / 2); uint32_t target_width = uint32_t(float(volumetric_fog_size) * ratio); uint32_t target_height = uint32_t(float(volumetric_fog_size) / ratio); - if (rb->volumetric_fog) { + if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { + Ref<RendererRD::Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); //validate - if (p_environment.is_null() || !environment_get_volumetric_fog_enabled(p_environment) || rb->volumetric_fog->width != target_width || rb->volumetric_fog->height != target_height || rb->volumetric_fog->depth != volumetric_fog_depth) { - memdelete(rb->volumetric_fog); - rb->volumetric_fog = nullptr; + if (p_environment.is_null() || !environment_get_volumetric_fog_enabled(p_environment) || fog->width != target_width || fog->height != target_height || fog->depth != volumetric_fog_depth) { + p_render_buffers->set_custom_data(RB_SCOPE_FOG, Ref<RenderBufferCustomDataRD>()); } } @@ -3438,14 +2819,21 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e return; } - if (p_environment.is_valid() && environment_get_volumetric_fog_enabled(p_environment) && !rb->volumetric_fog) { + if (p_environment.is_valid() && environment_get_volumetric_fog_enabled(p_environment) && !p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { //required volumetric fog but not existing, create - rb->volumetric_fog = memnew(RendererRD::Fog::VolumetricFog(Vector3i(target_width, target_height, volumetric_fog_depth), sky.sky_shader.default_shader_rd)); + Ref<RendererRD::Fog::VolumetricFog> fog; + + fog.instantiate(); + fog->init(Vector3i(target_width, target_height, volumetric_fog_depth), sky.sky_shader.default_shader_rd); + + p_render_buffers->set_custom_data(RB_SCOPE_FOG, fog); } - if (rb->volumetric_fog) { + if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) { + Ref<RendererRD::Fog::VolumetricFog> fog = p_render_buffers->get_custom_data(RB_SCOPE_FOG); + RendererRD::Fog::VolumetricFogSettings settings; - settings.rb_size = Vector2i(rb->width, rb->height); + settings.rb_size = size; settings.time = time; settings.is_using_radiance_cubemap_array = is_using_radiance_cubemap_array(); settings.max_cluster_elements = max_cluster_elements; @@ -3454,16 +2842,16 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e settings.shadow_sampler = shadow_sampler; ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas); settings.shadow_atlas_depth = shadow_atlas ? shadow_atlas->depth : RID(); - settings.voxel_gl_buffer = render_buffers_get_voxel_gi_buffer(p_render_buffers); + settings.voxel_gi_buffer = rbgi->get_voxel_gi_buffer(); settings.omni_light_buffer = get_omni_light_buffer(); settings.spot_light_buffer = get_spot_light_buffer(); settings.directional_shadow_depth = directional_shadow.depth; settings.directional_light_buffer = get_directional_light_buffer(); - settings.vfog = rb->volumetric_fog; - settings.cluster_builder = rb->cluster_builder; - settings.rbgi = &rb->rbgi; - settings.sdfgi = rb->sdfgi; + settings.vfog = fog; + settings.cluster_builder = p_render_buffers->cluster_builder; + settings.rbgi = rbgi; + settings.sdfgi = sdfgi; settings.env = p_environment; settings.sky = &sky; settings.gi = &gi; @@ -3474,8 +2862,7 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e bool RendererSceneRenderRD::_needs_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi) { if (p_render_data->render_buffers.is_valid()) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - if (rb->sdfgi != nullptr) { + if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { return true; } } @@ -3483,16 +2870,13 @@ bool RendererSceneRenderRD::_needs_post_prepass_render(RenderDataRD *p_render_da } void RendererSceneRenderRD::_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi) { - if (p_render_data->render_buffers.is_valid()) { - if (p_use_gi) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(rb == nullptr); - if (rb->sdfgi == nullptr) { - return; - } - - rb->sdfgi->update_probes(p_render_data->environment, sky.sky_owner.get_or_null(environment_get_sky(p_render_data->environment))); + if (p_render_data->render_buffers.is_valid() && p_use_gi) { + if (!p_render_data->render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + return; } + + Ref<RendererRD::GI::SDFGI> sdfgi = p_render_data->render_buffers->get_custom_data(RB_SCOPE_SDFGI); + sdfgi->update_probes(p_render_data->environment, sky.sky_owner.get_or_null(environment_get_sky(p_render_data->environment))); } } @@ -3504,16 +2888,13 @@ void RendererSceneRenderRD::_pre_resolve_render(RenderDataRD *p_render_data, boo } } -void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, const RID *p_vrs_slices) { +void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer) { // Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); - if (p_render_data->render_buffers.is_valid() && p_use_gi) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(rb == nullptr); - if (rb->sdfgi != nullptr) { - rb->sdfgi->store_probes(); - } + if (p_render_data->render_buffers.is_valid() && p_use_gi && p_render_data->render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = p_render_data->render_buffers->get_custom_data(RB_SCOPE_SDFGI); + sdfgi->store_probes(); } render_state.cube_shadows.clear(); @@ -3579,7 +2960,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool //start GI if (render_gi) { - gi.process_gi(p_render_data->render_buffers, p_normal_roughness_slices, p_voxel_gi_buffer, p_vrs_slices, p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->view_eye_offset, p_render_data->cam_transform, *p_render_data->voxel_gi_instances, this); + gi.process_gi(p_render_data->render_buffers, p_normal_roughness_slices, p_voxel_gi_buffer, p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->view_eye_offset, p_render_data->cam_transform, *p_render_data->voxel_gi_instances); } //Do shadow rendering (in parallel with GI) @@ -3593,16 +2974,17 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool if (p_render_data->render_buffers.is_valid() && ss_effects) { if (p_use_ssao || p_use_ssil) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); - ERR_FAIL_COND(!rb); + Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers; + ERR_FAIL_COND(rb.is_null()); + Size2i size = rb->get_internal_size(); bool invalidate_uniform_set = false; if (rb->ss_effects.linear_depth.is_null()) { RD::TextureFormat tf; tf.format = RD::DATA_FORMAT_R16_SFLOAT; tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - tf.width = (rb->width + 1) / 2; - tf.height = (rb->height + 1) / 2; + tf.width = (size.x + 1) / 2; + tf.height = (size.y + 1) / 2; tf.mipmaps = 5; tf.array_layers = 4; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; @@ -3616,7 +2998,8 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool invalidate_uniform_set = true; } - ss_effects->downsample_depth(rb->depth_texture, rb->ss_effects.linear_depth_slices, ssao_quality, ssil_quality, invalidate_uniform_set, ssao_half_size, ssil_half_size, Size2i(rb->width, rb->height), p_render_data->cam_projection); + RID depth_texture = rb->get_depth_texture(); + ss_effects->downsample_depth(depth_texture, rb->ss_effects.linear_depth_slices, ssao_quality, ssil_quality, invalidate_uniform_set, ssao_half_size, ssil_half_size, size, p_render_data->cam_projection); } if (p_use_ssao) { @@ -3645,12 +3028,12 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool } } else { //do not render reflections when rendering a reflection probe - _setup_reflections(*p_render_data->reflection_probes, p_render_data->cam_transform.affine_inverse(), p_render_data->environment); + _setup_reflections(p_render_data, *p_render_data->reflection_probes, p_render_data->cam_transform.affine_inverse(), p_render_data->environment); } uint32_t directional_light_count = 0; uint32_t positional_light_count = 0; - _setup_lights(*p_render_data->lights, p_render_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); + _setup_lights(p_render_data, *p_render_data->lights, p_render_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); _setup_decals(*p_render_data->decals, p_render_data->cam_transform.affine_inverse()); p_render_data->directional_light_count = directional_light_count; @@ -3673,20 +3056,20 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool } } -void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); // getting this here now so we can direct call a bunch of things more easily - RenderBuffers *rb = nullptr; + Ref<RenderSceneBuffersRD> rb; if (p_render_buffers.is_valid()) { - rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); + rb = p_render_buffers; // cast it... + ERR_FAIL_COND(rb.is_null()); } //assign render data RenderDataRD render_data; { - render_data.render_buffers = p_render_buffers; + render_data.render_buffers = rb; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; @@ -3719,7 +3102,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData render_data.lightmaps = &p_lightmaps; render_data.fog_volumes = &p_fog_volumes; render_data.environment = p_environment; - render_data.camera_effects = p_camera_effects; + render_data.camera_attributes = p_camera_attributes; render_data.shadow_atlas = p_shadow_atlas; render_data.reflection_atlas = p_reflection_atlas; render_data.reflection_probe = p_reflection_probe; @@ -3752,18 +3135,24 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData } //sdfgi first - if (rb != nullptr && rb->sdfgi != nullptr) { + if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + float exposure_normalization = 1.0; + + if (p_camera_attributes.is_valid()) { + exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes); + } for (int i = 0; i < render_state.render_sdfgi_region_count; i++) { - rb->sdfgi->render_region(p_render_buffers, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances, this); + sdfgi->render_region(rb, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances, this, exposure_normalization); } if (render_state.sdfgi_update_data->update_static) { - rb->sdfgi->render_static_lights(p_render_buffers, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights, this); + sdfgi->render_static_lights(&render_data, rb, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights, this); } } Color clear_color; if (p_render_buffers.is_valid()) { - clear_color = texture_storage->render_target_get_clear_request_color(rb->render_target); + clear_color = texture_storage->render_target_get_clear_request_color(rb->get_render_target()); } else { clear_color = RSG::texture_storage->get_default_clear_color(); } @@ -3771,14 +3160,11 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData //assign render indices to voxel_gi_instances if (is_dynamic_gi_supported()) { for (uint32_t i = 0; i < (uint32_t)p_voxel_gi_instances.size(); i++) { - RendererRD::GI::VoxelGIInstance *voxel_gi_inst = gi.voxel_gi_instance_owner.get_or_null(p_voxel_gi_instances[i]); - if (voxel_gi_inst) { - voxel_gi_inst->render_index = i; - } + gi.voxel_gi_instance_set_render_index(p_voxel_gi_instances[i], i); } } - if (render_buffers_owner.owns(render_data.render_buffers)) { + if (rb.is_valid()) { // render_data.render_buffers == p_render_buffers so we can use our already retrieved rb current_cluster_builder = rb->cluster_builder; } else if (reflection_probe_instance_owner.owns(render_data.reflection_probe)) { @@ -3790,6 +3176,9 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData } else { current_cluster_builder = ra->cluster_builder; } + if (p_camera_attributes.is_valid()) { + RendererRD::LightStorage::get_singleton()->reflection_probe_set_baked_exposure(rpi->probe, RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes)); + } } else { ERR_PRINT("No render buffer nor reflection atlas, bug"); //should never happen, will crash current_cluster_builder = nullptr; @@ -3797,14 +3186,17 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData render_state.voxel_gi_count = 0; - if (rb != nullptr && is_dynamic_gi_supported()) { - if (rb->sdfgi) { - rb->sdfgi->update_cascades(); - rb->sdfgi->pre_process_gi(render_data.cam_transform, &render_data, this); - rb->sdfgi->update_light(); + if (rb.is_valid() && is_dynamic_gi_supported()) { + if (rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); + if (sdfgi.is_valid()) { + sdfgi->update_cascades(); + sdfgi->pre_process_gi(render_data.cam_transform, &render_data, this); + sdfgi->update_light(); + } } - gi.setup_voxel_gi_instances(render_data.render_buffers, render_data.cam_transform, *render_data.voxel_gi_instances, render_state.voxel_gi_count, this); + gi.setup_voxel_gi_instances(&render_data, render_data.render_buffers, render_data.cam_transform, *render_data.voxel_gi_instances, render_state.voxel_gi_count, this); } render_state.depth_prepass_used = false; @@ -3815,33 +3207,51 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData render_data.cluster_max_elements = current_cluster_builder->get_max_cluster_elements(); } - if (rb != nullptr && rb->vrs_fb.is_valid()) { - // vrs_fb will only be valid if vrs is enabled - vrs->update_vrs_texture(rb->vrs_fb, rb->render_target); + if (rb.is_valid() && vrs) { + RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(rb->get_render_target()); + if (vrs_mode != RS::VIEWPORT_VRS_DISABLED) { + RID vrs_texture = rb->get_texture(RB_SCOPE_VRS, RB_TEXTURE); + + // We use get_cache_multipass instead of get_cache_multiview because the default behavior is for + // our vrs_texture to be used as the VRS attachment. In this particular case we're writing to it + // so it needs to be set as our color attachment + + Vector<RID> textures; + textures.push_back(vrs_texture); + + Vector<RD::FramebufferPass> passes; + RD::FramebufferPass pass; + pass.color_attachments.push_back(0); + passes.push_back(pass); + + RID vrs_fb = FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, rb->get_view_count()); + + vrs->update_vrs_texture(vrs_fb, rb->get_render_target()); + } } _render_scene(&render_data, clear_color); - if (p_render_buffers.is_valid()) { - /* - _debug_draw_cluster(p_render_buffers); - _render_buffers_post_process_and_tonemap(&render_data); - */ + if (rb.is_valid()) { + _render_buffers_debug_draw(rb, p_shadow_atlas, p_occluder_debug_tex); - _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) { + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb->has_custom_data(RB_SCOPE_SDFGI)) { + Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI); Vector<RID> view_rids; - for (int v = 0; v < rb->views.size(); v++) { - view_rids.push_back(rb->views[v].view_texture); + // SDFGI renders at internal resolution, need to check if our debug correctly supports outputting upscaled. + Size2i size = rb->get_internal_size(); + RID source_texture = rb->get_internal_texture(); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + view_rids.push_back(rb->get_internal_texture(v)); } - rb->sdfgi->debug_draw(render_data.view_count, render_data.view_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture, view_rids); + sdfgi->debug_draw(render_data.view_count, render_data.view_projection, render_data.cam_transform, size.x, size.y, rb->get_render_target(), source_texture, view_rids); } } } -void RendererSceneRenderRD::_debug_draw_cluster(RID p_render_buffers) { +void RendererSceneRenderRD::_debug_draw_cluster(Ref<RenderSceneBuffersRD> p_render_buffers) { if (p_render_buffers.is_valid() && current_cluster_builder != nullptr) { RS::ViewportDebugDraw dd = get_debug_draw_mode(); @@ -4041,7 +3451,7 @@ void RendererSceneRenderRD::_render_shadow_pass(RID p_light, RID p_shadow_atlas, } void RendererSceneRenderRD::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { - _render_material(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_instances, p_framebuffer, p_region); + _render_material(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_instances, p_framebuffer, p_region, 1.0); } void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) { @@ -4064,28 +3474,10 @@ void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider, } bool RendererSceneRenderRD::free(RID p_rid) { - if (render_buffers_owner.owns(p_rid)) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_rid); - _free_render_buffer_data(rb); - memdelete(rb->data); - if (rb->sdfgi) { - rb->sdfgi->erase(); - memdelete(rb->sdfgi); - rb->sdfgi = nullptr; - } - if (rb->volumetric_fog) { - memdelete(rb->volumetric_fog); - rb->volumetric_fog = nullptr; - } - if (rb->cluster_builder) { - memdelete(rb->cluster_builder); - } - render_buffers_owner.free(p_rid); - } else if (is_environment(p_rid)) { + if (is_environment(p_rid)) { environment_free(p_rid); - } else if (camera_effects_owner.owns(p_rid)) { - //not much to delete, just free it - camera_effects_owner.free(p_rid); + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + RSG::camera_attributes->camera_attributes_free(p_rid); } else if (reflection_atlas_owner.owns(p_rid)) { reflection_atlas_set_size(p_rid, 0, 0); ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_rid); @@ -4335,8 +3727,8 @@ RendererSceneRenderRD::RendererSceneRenderRD() { void RendererSceneRenderRD::init() { max_cluster_elements = get_max_elements(); - directional_shadow.size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); - directional_shadow.use_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); + directional_shadow.size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size"); + directional_shadow.use_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits"); /* SKY SHADER */ @@ -4395,8 +3787,10 @@ void RendererSceneRenderRD::init() { shadow_sampler = RD::get_singleton()->sampler_create(sampler); } - camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")))); - camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter")); + RSG::camera_attributes->camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")))); + RSG::camera_attributes->camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter")); + use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"); + environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to")); screen_space_roughness_limiter = GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"); screen_space_roughness_limiter_amount = GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"); @@ -4414,8 +3808,8 @@ void RendererSceneRenderRD::init() { directional_soft_shadow_kernel = memnew_arr(float, 128); penumbra_shadow_kernel = memnew_arr(float, 128); soft_shadow_kernel = memnew_arr(float, 128); - positional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/positional_shadow/soft_shadow_filter_quality")))); - directional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_filter_quality")))); + positional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality")))); + directional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality")))); environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/environment/volumetric_fog/volume_size"), GLOBAL_GET("rendering/environment/volumetric_fog/volume_depth")); environment_set_volumetric_fog_filter_active(GLOBAL_GET("rendering/environment/volumetric_fog/use_filter")); @@ -4431,6 +3825,7 @@ void RendererSceneRenderRD::init() { tone_mapper = memnew(RendererRD::ToneMapper); vrs = memnew(RendererRD::VRS); if (can_use_storage) { + fsr = memnew(RendererRD::FSR); ss_effects = memnew(RendererRD::SSEffects); } } @@ -4448,6 +3843,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() { if (vrs) { memdelete(vrs); } + if (fsr) { + memdelete(fsr); + } if (ss_effects) { memdelete(ss_effects); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 3867fd8605..396faf8108 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -37,18 +37,21 @@ #include "servers/rendering/renderer_rd/cluster_builder_rd.h" #include "servers/rendering/renderer_rd/effects/bokeh_dof.h" #include "servers/rendering/renderer_rd/effects/copy_effects.h" +#include "servers/rendering/renderer_rd/effects/fsr.h" #include "servers/rendering/renderer_rd/effects/ss_effects.h" #include "servers/rendering/renderer_rd/effects/tone_mapper.h" #include "servers/rendering/renderer_rd/effects/vrs.h" #include "servers/rendering/renderer_rd/environment/fog.h" #include "servers/rendering/renderer_rd/environment/gi.h" #include "servers/rendering/renderer_rd/environment/sky.h" +#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_scene.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering/rendering_device.h" struct RenderDataRD { - RID render_buffers; + Ref<RenderSceneBuffersRD> render_buffers; Transform3D cam_transform; Projection cam_projection; @@ -76,7 +79,7 @@ struct RenderDataRD { const PagedArray<RID> *lightmaps = nullptr; const PagedArray<RID> *fog_volumes = nullptr; RID environment; - RID camera_effects; + RID camera_attributes; RID shadow_atlas; RID reflection_atlas; RID reflection_probe; @@ -104,19 +107,16 @@ protected: RendererRD::BokehDOF *bokeh_dof = nullptr; RendererRD::CopyEffects *copy_effects = nullptr; RendererRD::ToneMapper *tone_mapper = nullptr; + RendererRD::FSR *fsr = nullptr; RendererRD::VRS *vrs = nullptr; double time = 0.0; double time_step = 0.0; - struct RenderBufferData { - 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, bool p_use_taa, uint32_t p_view_count, RID p_vrs_texture) = 0; - virtual ~RenderBufferData() {} - }; - virtual RenderBufferData *_create_render_buffer_data() = 0; + virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) = 0; - void _setup_lights(const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows); + void _setup_lights(RenderDataRD *p_render_data, const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows); void _setup_decals(const PagedArray<RID> &p_decals, const Transform3D &p_camera_inverse_xform); - void _setup_reflections(const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment); + void _setup_reflections(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment); virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_color) = 0; @@ -125,33 +125,30 @@ protected: virtual void _render_shadow_process() = 0; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) = 0; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0; + virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) = 0; - void _debug_sdfgi_probes(RID p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); - void _debug_draw_cluster(RID p_render_buffers); - - RenderBufferData *render_buffers_get_data(RID p_render_buffers); + void _debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); + void _debug_draw_cluster(Ref<RenderSceneBuffersRD> p_render_buffers); virtual void _base_uniforms_changed() = 0; - virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) = 0; - virtual RID _render_buffers_get_velocity_texture(RID p_render_buffers) = 0; + virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0; + virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0; - void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection); - void _process_ssr(RID p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive); - void _process_sss(RID p_render_buffers, const Projection &p_camera); - void _process_ssil(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection, const Transform3D &p_transform); + void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection); + void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, const Color &p_metallic_mask, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive); + void _process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera); + void _process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, RID p_normal_buffer, const Projection &p_projection, const Transform3D &p_transform); - void _copy_framebuffer_to_ssil(RID p_render_buffers); - void _process_taa(RID p_render_buffers, RID p_velocity_buffer, float p_z_near, float p_z_far); + void _copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers); bool _needs_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi); void _post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi); void _pre_resolve_render(RenderDataRD *p_render_data, bool p_use_gi); - void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, const RID *p_vrs_slices); + void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer); void _render_buffers_copy_screen_texture(const RenderDataRD *p_render_data); void _render_buffers_copy_depth_texture(const RenderDataRD *p_render_data); @@ -418,157 +415,28 @@ private: bool glow_high_quality = false; RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; - /* CAMERA EFFECTS */ - - struct CameraEffects { - bool dof_blur_far_enabled = false; - float dof_blur_far_distance = 10; - float dof_blur_far_transition = 5; - - bool dof_blur_near_enabled = false; - float dof_blur_near_distance = 2; - float dof_blur_near_transition = 1; - - float dof_blur_amount = 0.1; - - bool override_exposure_enabled = false; - float override_exposure = 1; - }; - - RS::DOFBlurQuality dof_blur_quality = RS::DOF_BLUR_QUALITY_MEDIUM; - RS::DOFBokehShape dof_blur_bokeh_shape = RS::DOF_BOKEH_HEXAGON; - bool dof_blur_use_jitter = false; RS::SubSurfaceScatteringQuality sss_quality = RS::SUB_SURFACE_SCATTERING_QUALITY_MEDIUM; float sss_scale = 0.05; float sss_depth_scale = 0.01; - mutable RID_Owner<CameraEffects, true> camera_effects_owner; + bool use_physical_light_units = false; - /* RENDER BUFFERS */ + /* Cluster builder */ ClusterBuilderSharedDataRD cluster_builder_shared; ClusterBuilderRD *current_cluster_builder = nullptr; - struct RenderBuffers { - RenderBufferData *data = nullptr; - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; - float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa_3d = RS::VIEWPORT_MSAA_DISABLED; - RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; - bool use_taa = false; - bool use_debanding = false; - uint32_t view_count = 1; - - RID render_target; - - uint64_t auto_exposure_version = 1; - - RID sss_texture; //texture for sss. This needs to be a different resolution than blur[0] - RID internal_texture; //main texture for rendering to, must be filled after done rendering - RID texture; //upscaled version of main texture (This uses the same resource as internal_texture if there is no upscaling) - RID depth_texture; //main depth texture - RID texture_fb; // framebuffer for the main texture, ONLY USED FOR MOBILE RENDERER POST EFFECTS, DO NOT USE FOR RENDERING 3D!!! - RID upscale_texture; //used when upscaling internal_texture (This uses the same resource as internal_texture if there is no upscaling) - RID vrs_texture; // texture for vrs. - RID vrs_fb; // framebuffer to write to our vrs texture - - // Access to the layers for each of our views (specifically needed for applying post effects on stereoscopic images) - struct View { - RID view_texture; // texture slice for this view/layer - RID view_depth; // depth slice for this view/layer - RID view_fb; // framebuffer for this view/layer, ONLY USED FOR MOBILE RENDERER POST EFFECTS, DO NOT USE FOR RENDERING 3D!!! - }; - Vector<View> views; - - RendererRD::GI::SDFGI *sdfgi = nullptr; - RendererRD::GI::RenderBuffersGI rbgi; - RendererRD::Fog::VolumetricFog *volumetric_fog = nullptr; - - ClusterBuilderRD *cluster_builder = nullptr; - - //built-in textures used for ping pong image processing and blurring - struct Blur { - RID texture; - - struct Mipmap { - RID texture; - int width; - int height; - - // only used on mobile renderer - RID fb; - RID half_texture; - RID half_fb; - }; - - struct Layer { - Vector<Mipmap> mipmaps; - }; - - Vector<Layer> layers; - }; - - Blur blur[2]; //the second one starts from the first mipmap - - struct WeightBuffers { - RID weight; - RID fb; // FB with both texture and weight writing into one level lower - }; - - // 2 full size, 2 half size - WeightBuffers weight_buffers[4]; // Only used in raster - - RID depth_back_texture; - RID depth_back_fb; // only used on mobile - - struct Luminance { - Vector<RID> reduce; - RID current; - - // used only on mobile renderer - Vector<RID> fb; - RID current_fb; - } luminance; - - struct SSEffects { - RID linear_depth; - Vector<RID> linear_depth_slices; - - RID downsample_uniform_set; - - Projection last_frame_projection; - Transform3D last_frame_transform; + /* RENDER BUFFERS */ - RendererRD::SSEffects::SSAORenderBuffers ssao; - RendererRD::SSEffects::SSILRenderBuffers ssil; - } ss_effects; + void _allocate_luminance_textures(Ref<RenderSceneBuffersRD> rb); - RendererRD::SSEffects::SSRRenderBuffers ssr; - - struct TAA { - RID history; - RID temp; - RID prev_velocity; // Last frame velocity buffer - } taa; - }; + void _render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); /* GI */ bool screen_space_roughness_limiter = false; float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - mutable RID_Owner<RenderBuffers> render_buffers_owner; - - void _free_render_buffer_data(RenderBuffers *rb); - void _allocate_blur_textures(RenderBuffers *rb); - void _allocate_depth_backbuffer_textures(RenderBuffers *rb); - void _allocate_luminance_textures(RenderBuffers *rb); - - void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); - /* Cluster */ struct Cluster { @@ -592,7 +460,7 @@ private: uint32_t exterior; uint32_t box_project; uint32_t ambient_mode; - uint32_t pad; + float exposure_normalization; float local_matrix[16]; // up to here for spot and omni, rest is for directional }; @@ -747,11 +615,14 @@ private: uint32_t volumetric_fog_depth = 128; bool volumetric_fog_filter_active = true; - void _update_volumetric_fog(RID p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes); + void _update_volumetric_fog(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes); public: static RendererSceneRenderRD *get_singleton() { return singleton; } + /* Cluster builder */ + ClusterBuilderSharedDataRD *get_cluster_builder_shared() { return &cluster_builder_shared; } + /* GI */ RendererRD::GI *get_gi() { return &gi; } @@ -794,10 +665,10 @@ public: /* SDFGI UPDATE */ - virtual void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override; - virtual int sdfgi_get_pending_region_count(RID p_render_buffers) const override; - virtual AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override; - virtual uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override; + virtual void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override; + virtual int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override; + virtual AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override; + virtual uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override; RID sdfgi_get_ubo() const { return gi.sdfgi_ubo; } /* SKY API */ @@ -831,21 +702,8 @@ public: virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; - /* CAMERA EFFECTS */ - - virtual RID camera_effects_allocate() override; - virtual void camera_effects_initialize(RID p_rid) override; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; - - 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.get_or_null(p_camera_effects); - - return camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0; + _FORCE_INLINE_ bool is_using_physical_light_units() { + return use_physical_light_units; } /* LIGHT INSTANCE API */ @@ -1104,42 +962,14 @@ public: virtual float _render_buffers_get_luminance_multiplier(); virtual RD::DataFormat _render_buffers_get_color_format(); virtual bool _render_buffers_can_be_storage(); - virtual RID render_buffers_create() override; - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + virtual Ref<RenderSceneBuffers> render_buffers_create() override; virtual void gi_set_use_half_resolution(bool p_enable) override; - RID render_buffers_get_depth_texture(RID p_render_buffers); - RID render_buffers_get_ao_texture(RID p_render_buffers); - RID render_buffers_get_ssil_texture(RID p_render_buffers); - RID render_buffers_get_back_buffer_texture(RID p_render_buffers); - RID render_buffers_get_back_depth_texture(RID p_render_buffers); - RID render_buffers_get_voxel_gi_buffer(RID p_render_buffers); RID render_buffers_get_default_voxel_gi_buffer(); - RID render_buffers_get_gi_ambient_texture(RID p_render_buffers); - RID render_buffers_get_gi_reflection_texture(RID p_render_buffers); - - uint32_t render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const; - bool render_buffers_is_sdfgi_enabled(RID p_render_buffers) const; - RID render_buffers_get_sdfgi_irradiance_probes(RID p_render_buffers) const; - Vector3 render_buffers_get_sdfgi_cascade_offset(RID p_render_buffers, uint32_t p_cascade) const; - Vector3i render_buffers_get_sdfgi_cascade_probe_offset(RID p_render_buffers, uint32_t p_cascade) const; - float render_buffers_get_sdfgi_cascade_probe_size(RID p_render_buffers, uint32_t p_cascade) const; - float render_buffers_get_sdfgi_normal_bias(RID p_render_buffers) const; - uint32_t render_buffers_get_sdfgi_cascade_probe_count(RID p_render_buffers) const; - uint32_t render_buffers_get_sdfgi_cascade_size(RID p_render_buffers) const; - bool render_buffers_is_sdfgi_using_occlusion(RID p_render_buffers) const; - float render_buffers_get_sdfgi_energy(RID p_render_buffers) const; - RID render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const; - - bool render_buffers_has_volumetric_fog(RID p_render_buffers) const; - RID render_buffers_get_volumetric_fog_texture(RID p_render_buffers); - RID render_buffers_get_volumetric_fog_sky_uniform_set(RID p_render_buffers); - float render_buffers_get_volumetric_fog_end(RID p_render_buffers); - float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); virtual void update_uniform_sets(){}; - virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + virtual void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl index 96f5c3e9f2..cb06250cf2 100644 --- a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl @@ -129,7 +129,7 @@ void main() { #ifdef GLOW_USE_AUTO_EXPOSURE - frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey; + frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_scale; #endif frag_color *= blur.glow_exposure; diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl index 730504571a..06ca198f37 100644 --- a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl @@ -16,7 +16,7 @@ layout(push_constant, std430) uniform Blur { float glow_exposure; // 04 - 36 float glow_white; // 04 - 40 float glow_luminance_cap; // 04 - 44 - float glow_auto_exposure_grey; // 04 - 48 + float glow_auto_exposure_scale; // 04 - 48 float luminance_multiplier; // 04 - 52 float res1; // 04 - 56 diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl index 0b43af7738..bdf84bb03a 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl @@ -41,11 +41,25 @@ float get_depth_at_pos(vec2 uv) { float get_blur_size(float depth) { if (params.blur_near_active && depth < params.blur_near_begin) { - return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative + if (params.use_physical_near) { + // Physically-based. + float d = abs(params.blur_near_begin - depth); + return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative. + } else { + // Non-physically-based. + return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative. + } } if (params.blur_far_active && depth > params.blur_far_begin) { - return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + if (params.use_physical_far) { + // Physically-based. + float d = abs(params.blur_far_begin - depth); + return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP; + } else { + // Non-physically-based. + return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + } } return 0.0; diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl index b90a527554..4a2b0edc18 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl @@ -20,6 +20,11 @@ layout(push_constant, std430) uniform Params { bool use_jitter; float jitter_seed; + bool use_physical_near; + bool use_physical_far; + + float blur_size_near; + float blur_size_far; uint pad[2]; } params; diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl index a06cacfabe..a2bdc2e90e 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl @@ -63,11 +63,25 @@ float get_depth_at_pos(vec2 uv) { float get_blur_size(float depth) { if (params.blur_near_active && depth < params.blur_near_begin) { - return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative + if (params.use_physical_near) { + // Physically-based. + float d = abs(params.blur_near_begin - depth); + return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative. + } else { + // Non-physically-based. + return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative. + } } if (params.blur_far_active && depth > params.blur_far_begin) { - return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + if (params.use_physical_far) { + // Physically-based. + float d = abs(params.blur_far_begin - depth); + return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP; + } else { + // Non-physically-based. + return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + } } return 0.0; diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl index 3a4ef86ef0..bfe329b8ec 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl @@ -31,7 +31,7 @@ layout(push_constant, std430) uniform Params { float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; // DOF. float camera_z_far; float camera_z_near; @@ -185,7 +185,7 @@ void main() { if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) { #ifdef GLOW_USE_AUTO_EXPOSURE - color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_grey; + color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_scale; #endif color *= params.glow_exposure; diff --git a/servers/rendering/renderer_rd/shaders/fsr_upscale.glsl b/servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl index c8eb78a2f0..c8eb78a2f0 100644 --- a/servers/rendering/renderer_rd/shaders/fsr_upscale.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl diff --git a/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl b/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl index fb35d3cde6..fb35d3cde6 100644 --- a/servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl diff --git a/servers/rendering/renderer_rd/shaders/taa_resolve.glsl b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl index b0a0839836..b0a0839836 100644 --- a/servers/rendering/renderer_rd/shaders/taa_resolve.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 62a7b0e7d7..e459756c6a 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -75,7 +75,7 @@ layout(push_constant, std430) uniform Params { float exposure; float white; - float auto_exposure_grey; + float auto_exposure_scale; float luminance_multiplier; vec2 pixel_size; @@ -440,7 +440,7 @@ void main() { #ifndef SUBPASS if (params.use_auto_exposure) { - exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_grey); + exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale); } #endif diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index 6ea8cb1377..ab927df678 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -32,6 +32,8 @@ struct ProbeCascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer; @@ -83,6 +85,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 0, binding = 16, std140) uniform VoxelGIs { @@ -241,7 +246,7 @@ void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_ pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb; - diffuse_accum += vec4(diffuse * weight, weight); + diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); { vec3 specular = vec3(0.0); @@ -255,7 +260,7 @@ void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_ specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25); } - specular_accum += specular * weight; + specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; } } @@ -574,7 +579,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 } } - light.rgb *= voxel_gi_instances.data[index].dynamic_range; + light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; if (!voxel_gi_instances.data[index].blend_ambient) { light.a = 1.0; } @@ -583,7 +588,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 //radiance vec4 irr_light = voxel_cone_trace(voxel_gi_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, voxel_gi_instances.data[index].bias); - irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range; + irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; if (!voxel_gi_instances.data[index].blend_ambient) { irr_light.a = 1.0; } diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl index 9640d30e78..177dab16c7 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl @@ -21,6 +21,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 9, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl index 75b1ad2130..a0ef169f03 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl @@ -73,6 +73,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 1, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl index b95fad650e..9f7449b8aa 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl @@ -45,6 +45,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 8, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl index 9c03297f5c..4bdb0dcc72 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl @@ -20,6 +20,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 7, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index e825020a4e..0eb0f5f8fd 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -15,10 +15,10 @@ layout(location = 0) out vec2 uv_interp; layout(push_constant, std430) uniform Params { mat3 orientation; vec4 projections[MAX_VIEWS]; - vec4 position_multiplier; + vec3 position; float time; + vec3 pad; float luminance_multiplier; - float pad[2]; } params; @@ -55,10 +55,10 @@ layout(location = 0) in vec2 uv_interp; layout(push_constant, std430) uniform Params { mat3 orientation; vec4 projections[MAX_VIEWS]; - vec4 position_multiplier; + vec3 position; float time; + vec3 pad; float luminance_multiplier; - float pad[2]; } params; @@ -83,20 +83,23 @@ layout(set = 0, binding = 1, std430) restrict readonly buffer GlobalShaderUnifor global_shader_uniforms; layout(set = 0, binding = 2, std140) uniform SceneData { - bool volumetric_fog_enabled; - float volumetric_fog_inv_length; - float volumetric_fog_detail_spread; - - float fog_aerial_perspective; - - vec3 fog_light_color; - float fog_sun_scatter; - - bool fog_enabled; - float fog_density; - - float z_far; - uint directional_light_count; + bool volumetric_fog_enabled; // 4 - 4 + float volumetric_fog_inv_length; // 4 - 8 + float volumetric_fog_detail_spread; // 4 - 12 + float volumetric_fog_sky_affect; // 4 - 16 + + bool fog_enabled; // 4 - 20 + float fog_sky_affect; // 4 - 24 + float fog_density; // 4 - 28 + float fog_sun_scatter; // 4 - 32 + + vec3 fog_light_color; // 12 - 44 + float fog_aerial_perspective; // 4 - 48 + + float z_far; // 4 - 52 + uint directional_light_count; // 4 - 56 + uint pad1; // 4 - 60 + uint pad2; // 4 - 64 } scene_data; @@ -169,9 +172,7 @@ vec4 fog_process(vec3 view, vec3 sky_color) { } } - float fog_amount = clamp(1.0 - exp(-scene_data.z_far * scene_data.fog_density), 0.0, 1.0); - - return vec4(fog_color, fog_amount); + return vec4(fog_color, 1.0); } void main() { @@ -200,17 +201,17 @@ void main() { #ifdef USE_CUBEMAP_PASS #ifdef USES_HALF_RES_COLOR - half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) * params.luminance_multiplier; + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) * params.luminance_multiplier; + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier; #endif #else #ifdef USES_HALF_RES_COLOR - half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; + half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; + quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier; #endif #endif @@ -220,7 +221,7 @@ void main() { } - frag_color.rgb = color * params.position_multiplier.w; + frag_color.rgb = color; frag_color.a = alpha; #if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS) @@ -228,12 +229,12 @@ void main() { // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky. if (scene_data.fog_enabled) { vec4 fog = fog_process(cube_normal, frag_color.rgb); - frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a * scene_data.fog_sky_affect); } if (scene_data.volumetric_fog_enabled) { vec4 fog = volumetric_fog_process(uv); - frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a * scene_data.volumetric_fog_sky_affect); } if (custom_fog.a > 0.0) { @@ -242,12 +243,13 @@ void main() { #endif // DISABLE_FOG - // Blending is disabled for Sky, so alpha doesn't blend - // alpha is used for subsurface scattering so make sure it doesn't get applied to Sky + // Blending is disabled for Sky, so alpha doesn't blend. + // Alpha is used for subsurface scattering so make sure it doesn't get applied to Sky. if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) { frag_color.a = 0.0; } - // For mobile renderer we're dividing by 2.0 as we're using a UNORM buffer - frag_color.rgb = frag_color.rgb / params.luminance_multiplier; + // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. + // For both mobile and clustered, we also bake in the exposure value for the environment and camera. + frag_color.rgb = frag_color.rgb * params.luminance_multiplier; } diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl index 07d5223472..eed9038502 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl @@ -84,6 +84,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 0, binding = 11, std140) uniform VoxelGIs { @@ -105,6 +108,8 @@ struct SDFVoxelGICascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(set = 1, binding = 0, std140) uniform SDFGI { @@ -624,7 +629,7 @@ void main() { light += a * slight; } - light.rgb *= voxel_gi_instances.data[i].dynamic_range * params.gi_inject; + light.rgb *= voxel_gi_instances.data[i].dynamic_range * params.gi_inject * voxel_gi_instances.data[i].exposure_normalization; total_light += light.rgb; } @@ -691,7 +696,7 @@ void main() { vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb; - ambient_accum.rgb += ambient * weight; + ambient_accum.rgb += ambient * weight * sdfgi.cascades[i].exposure_normalization; ambient_accum.a += weight; } diff --git a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl index a7fca25a6b..7488a3f2c7 100644 --- a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl @@ -44,7 +44,7 @@ struct ReflectionData { bool exterior; bool box_project; uint ambient_mode; - uint pad; + float exposure_normalization; //0-8 is intensity,8-9 is ambient, mode highp mat4 local_matrix; // up to here for spot and omni, rest is for directional // notes: for ambientblend, use distance to edge to blend between already existing global environment @@ -52,7 +52,7 @@ struct ReflectionData { struct DirectionalLightData { mediump vec3 direction; - mediump float energy; + highp float energy; // needs to be highp to avoid NaNs being created with high energy values (i.e. when using physical light units and over-exposing the image) mediump vec3 color; mediump float size; mediump float specular; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index 6b4e4a5a16..26b96b358f 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -986,6 +986,11 @@ void fragment_shader(in SceneData scene_data) { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); +#ifndef MODE_UNSHADED + // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + emission *= scene_data.emissive_exposure_normalization; +#endif + #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) if (scene_data.use_reflection_cubemap) { @@ -1015,6 +1020,7 @@ void fragment_shader(in SceneData scene_data) { specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY + specular_light *= scene_data.IBL_exposure_normalization; specular_light *= horizon * horizon; specular_light *= scene_data.ambient_light_color_energy.a; } @@ -1035,7 +1041,7 @@ void fragment_shader(in SceneData scene_data) { #else vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY - + cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); } } @@ -1094,15 +1100,16 @@ void fragment_shader(in SceneData scene_data) { const float c4 = 0.886227; const float c5 = 0.247708; ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z); + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; } else if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); @@ -1120,20 +1127,22 @@ void fragment_shader(in SceneData scene_data) { uint idx = instances.data[instance_index].gi_offset >> 20; vec3 n = normalize(lightmaps.data[idx].normal_xform * normal); + float en = lightmaps.data[idx].exposure_normalization; - ambient_light += lm_light_l0 * 0.282095f; - ambient_light += lm_light_l1n1 * 0.32573 * n.y; - ambient_light += lm_light_l1_0 * 0.32573 * n.z; - ambient_light += lm_light_l1p1 * 0.32573 * n.x; + ambient_light += lm_light_l0 * 0.282095f * en; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * en; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * en; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * en; if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y; - specular_light += lm_light_l1_0 * 0.32573 * r.z; - specular_light += lm_light_l1p1 * 0.32573 * r.x; + specular_light += lm_light_l1n1 * 0.32573 * r.y * en; + specular_light += lm_light_l1_0 * 0.32573 * r.z * en; + specular_light += lm_light_l1p1 * 0.32573 * r.x * en; } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb; + uint idx = instances.data[instance_index].gi_offset >> 20; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization; } } #else diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl index f0717294ef..45484b8c47 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl @@ -105,6 +105,8 @@ directional_lights; struct Lightmap { mat3 normal_xform; + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { @@ -139,6 +141,8 @@ struct SDFVoxelGICascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 15, std140) uniform SDFGI { @@ -251,7 +255,8 @@ struct SceneData { bool pancake_shadows; vec2 taa_jitter; - uvec2 pad2; + float emissive_exposure_normalization; + float IBL_exposure_normalization; }; layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -340,6 +345,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 1, binding = 17, std140) uniform VoxelGIs { diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl index c88bd0a14b..ae5e1b7251 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl @@ -94,7 +94,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 light += cone_weights[i] * cone_light.rgb; } - light *= voxel_gi_instances.data[index].dynamic_range; + light *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; out_diff += vec4(light * blend, blend); //irradiance @@ -102,7 +102,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 if (voxel_gi_instances.data[index].blend_ambient) { irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95)); } - irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range; + irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; //irr_light=vec3(0.0); out_spec += vec4(irr_light.rgb * blend, blend); @@ -189,7 +189,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb; - diffuse_accum += vec4(diffuse * weight, weight); + diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); if (use_specular) { vec3 specular = vec3(0.0); @@ -203,7 +203,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0); } - specular_accum += specular * weight; + specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; } } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 7299bb0576..4e6e29b315 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -909,7 +909,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 reflection; reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; - + reflection.rgb *= reflections.data[ref_index].exposure_normalization; if (reflections.data[ref_index].exterior) { reflection.rgb = mix(specular_light, reflection.rgb, blend); } @@ -932,6 +932,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 ambient_out; ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; + ambient_out.rgb *= reflections.data[ref_index].exposure_normalization; ambient_out.a = blend; if (reflections.data[ref_index].exterior) { ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl index 0ffc72f78f..5a5ada7231 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl @@ -471,8 +471,10 @@ layout(location = 8) highp in float dp_clip; #define model_matrix draw_call.transform #ifdef USE_MULTIVIEW #define projection_matrix scene_data.projection_matrix_view[ViewIndex] +#define inv_projection_matrix scene_data.inv_projection_matrix_view[ViewIndex] #else #define projection_matrix scene_data.projection_matrix +#define inv_projection_matrix scene_data.inv_projection_matrix #endif #if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE) @@ -887,6 +889,11 @@ void main() { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); +#ifndef MODE_UNSHADED + // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + emission *= scene_data.emissive_exposure_normalization; +#endif + #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) if (scene_data.use_reflection_cubemap) { @@ -915,6 +922,8 @@ void main() { specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY + specular_light *= sc_luminance_multiplier; + specular_light *= scene_data.IBL_exposure_normalization; specular_light *= horizon * horizon; specular_light *= scene_data.ambient_light_color_energy.a; } @@ -935,7 +944,8 @@ void main() { #else vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY - + cubemap_ambient *= sc_luminance_multiplier; + cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); } } @@ -993,15 +1003,16 @@ void main() { const float c4 = 0.886227; const float c5 = 0.247708; ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z); + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; } else if (bool(draw_call.flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(draw_call.flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); @@ -1010,6 +1021,8 @@ void main() { uvw.xy = uv2 * draw_call.lightmap_uv_scale.zw + draw_call.lightmap_uv_scale.xy; uvw.z = float((draw_call.gi_offset >> 16) & 0xFFFF); + uint idx = draw_call.gi_offset >> 20; + if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; @@ -1017,22 +1030,22 @@ void main() { vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; - uint idx = draw_call.gi_offset >> 20; vec3 n = normalize(lightmaps.data[idx].normal_xform * normal); + float exposure_normalization = lightmaps.data[idx].exposure_normalization; ambient_light += lm_light_l0 * 0.282095f; - ambient_light += lm_light_l1n1 * 0.32573 * n.y; - ambient_light += lm_light_l1_0 * 0.32573 * n.z; - ambient_light += lm_light_l1p1 * 0.32573 * n.x; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * exposure_normalization; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * exposure_normalization; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * exposure_normalization; if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y; - specular_light += lm_light_l1_0 * 0.32573 * r.z; - specular_light += lm_light_l1p1 * 0.32573 * r.x; + specular_light += lm_light_l1n1 * 0.32573 * r.y * exposure_normalization; + specular_light += lm_light_l1_0 * 0.32573 * r.z * exposure_normalization; + specular_light += lm_light_l1p1 * 0.32573 * r.x * exposure_normalization; } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization; } } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl index 98ad674ce0..3a9c52f5bc 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl @@ -94,6 +94,8 @@ directional_lights; struct Lightmap { mediump mat3 normal_xform; + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { @@ -187,8 +189,8 @@ struct SceneData { mediump float reflection_multiplier; // one normally, zero when rendering reflections bool pancake_shadows; - uint pad1; - uint pad2; + float emissive_exposure_normalization; + float IBL_exposure_normalization; uint pad3; }; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index b845c1155e..81b0661481 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -93,6 +93,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; + light.param[RS::LIGHT_PARAM_INTENSITY] = p_type == RS::LIGHT_DIRECTIONAL ? 100000.0 : 1000.0; light_owner.initialize_rid(p_light, light); } @@ -502,6 +503,13 @@ void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE); } +void LightStorage::reflection_probe_set_baked_exposure(RID p_probe, float p_exposure) { + ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->baked_exposure = p_exposure; +} + AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, AABB()); @@ -569,6 +577,13 @@ int LightStorage::reflection_probe_get_resolution(RID p_probe) const { return reflection_probe->resolution; } +float LightStorage::reflection_probe_get_baked_exposure(RID p_probe) const { + const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 1.0); + + return reflection_probe->baked_exposure; +} + float LightStorage::reflection_probe_get_intensity(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); @@ -711,6 +726,13 @@ void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedV lm->tetrahedra = p_tetrahedra; } +void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_COND(!lm); + + lm->baked_exposure = p_exposure; +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedVector3Array()); diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 7e98a3cb8e..82d609291c 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -87,6 +87,7 @@ private: bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; + float baked_exposure = 1.0; Dependency dependency; }; @@ -99,6 +100,7 @@ private: bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + float baked_exposure = 1.0; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -279,6 +281,8 @@ public: virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; + void reflection_probe_set_baked_exposure(RID p_probe, float p_exposure); + virtual AABB reflection_probe_get_aabb(RID p_probe) const override; virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override; virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override; @@ -288,6 +292,7 @@ public: virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override; int reflection_probe_get_resolution(RID p_probe) const; + float reflection_probe_get_baked_exposure(RID p_probe) const; virtual bool reflection_probe_renders_shadows(RID p_probe) const override; float reflection_probe_get_intensity(RID p_probe) const; @@ -311,6 +316,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; @@ -330,6 +336,11 @@ public: ERR_FAIL_COND_V(!lm, RID()); return lm->light_texture; } + _FORCE_INLINE_ float lightmap_get_baked_exposure_normalization(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_COND_V(!lm, 1.0); + return lm->baked_exposure; + } _FORCE_INLINE_ int32_t lightmap_get_array_index(RID p_lightmap) const { ERR_FAIL_COND_V(!using_lightmap_array, -1); //only for arrays const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); diff --git a/servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h new file mode 100644 index 0000000000..d904012914 --- /dev/null +++ b/servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* render_buffer_custom_data_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_BUFFER_CUSTOM_DATA_RD_H +#define RENDER_BUFFER_CUSTOM_DATA_RD_H + +#include "core/object/ref_counted.h" + +class RenderSceneBuffersRD; + +class RenderBufferCustomDataRD : public RefCounted { + GDCLASS(RenderBufferCustomDataRD, RefCounted); + +public: + virtual void configure(RenderSceneBuffersRD *p_render_buffers) = 0; + virtual void free_data() = 0; // called on cleanup + +private: +}; + +#endif // RENDER_BUFFER_CUSTOM_DATA_RD_H diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp new file mode 100644 index 0000000000..576ec81124 --- /dev/null +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -0,0 +1,559 @@ +/*************************************************************************/ +/* render_scene_buffers_rd.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "render_scene_buffers_rd.h" +#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" +#include "servers/rendering/renderer_rd/storage_rd/material_storage.h" +#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" + +RenderSceneBuffersRD::RenderSceneBuffersRD() { +} + +RenderSceneBuffersRD::~RenderSceneBuffersRD() { + cleanup(); + + data_buffers.clear(); + + // need to investigate if we can remove these things. + if (cluster_builder) { + memdelete(cluster_builder); + } +} + +void RenderSceneBuffersRD::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_texture", "context", "name"), &RenderSceneBuffersRD::has_texture); + // FIXME we can't pass RD::DataFormat, RD::TextureSamples and RD::TextureView in ClassDB, need to solve views differently... + // ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::create_texture); + // ClassDB::bind_method(D_METHOD("create_texture_from_format", "context", "name", "format", "view", "unique"), &RenderSceneBuffersRD::create_texture_from_format); + // ClassDB::bind_method(D_METHOD("create_texture_view", "context", "name", "view_name", "view"), &RenderSceneBuffersRD::has_texture); + ClassDB::bind_method(D_METHOD("get_texture", "context", "name"), &RenderSceneBuffersRD::get_texture); + // ClassDB::bind_method(D_METHOD("get_texture_format", "context", "name"), &RenderSceneBuffersRD::get_texture_format); + ClassDB::bind_method(D_METHOD("get_texture_slice", "context", "name", "layer", "mipmap"), &RenderSceneBuffersRD::get_texture_slice); + ClassDB::bind_method(D_METHOD("get_texture_slice_size", "context", "name", "layer", "mipmap"), &RenderSceneBuffersRD::get_texture_slice_size); + ClassDB::bind_method(D_METHOD("clear_context", "context"), &RenderSceneBuffersRD::clear_context); +} + +void RenderSceneBuffersRD::update_sizes(NamedTexture &p_named_texture) { + ERR_FAIL_COND(p_named_texture.texture.is_null()); + + uint32_t size = p_named_texture.format.array_layers * p_named_texture.format.mipmaps; + p_named_texture.sizes.resize(size); + + Size2i mipmap_size = Size2i(p_named_texture.format.width, p_named_texture.format.height); + + for (uint32_t mipmap = 0; mipmap < p_named_texture.format.mipmaps; mipmap++) { + for (uint32_t layer = 0; layer < p_named_texture.format.array_layers; layer++) { + uint32_t index = layer * p_named_texture.format.mipmaps + mipmap; + + p_named_texture.sizes.ptrw()[index] = mipmap_size; + } + + mipmap_size.width = MAX(1, mipmap_size.width >> 1); + mipmap_size.height = MAX(1, mipmap_size.height >> 1); + } +} + +void RenderSceneBuffersRD::free_named_texture(NamedTexture &p_named_texture) { + if (p_named_texture.texture.is_valid()) { + RD::get_singleton()->free(p_named_texture.texture); + } + p_named_texture.texture = RID(); + p_named_texture.slices.clear(); // slices should be freed automatically as dependents... +} + +void RenderSceneBuffersRD::cleanup() { + // Free our data buffers (but don't destroy them) + for (KeyValue<StringName, Ref<RenderBufferCustomDataRD>> &E : data_buffers) { + E.value->free_data(); + } + + // Clear our named textures + for (KeyValue<NTKey, NamedTexture> &E : named_textures) { + free_named_texture(E.value); + } + named_textures.clear(); + + // old stuff, to be re-evaluated... + + for (int i = 0; i < luminance.fb.size(); i++) { + RD::get_singleton()->free(luminance.fb[i]); + } + luminance.fb.clear(); + + for (int i = 0; i < luminance.reduce.size(); i++) { + RD::get_singleton()->free(luminance.reduce[i]); + } + luminance.reduce.clear(); + + if (luminance.current_fb.is_valid()) { + RD::get_singleton()->free(luminance.current_fb); + luminance.current_fb = RID(); + } + + if (luminance.current.is_valid()) { + RD::get_singleton()->free(luminance.current); + luminance.current = RID(); + } + + if (ss_effects.linear_depth.is_valid()) { + RD::get_singleton()->free(ss_effects.linear_depth); + ss_effects.linear_depth = RID(); + ss_effects.linear_depth_slices.clear(); + } + + sse->ssao_free(ss_effects.ssao); + sse->ssil_free(ss_effects.ssil); + sse->ssr_free(ssr); +} + +void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { + RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + + ERR_FAIL_COND_MSG(p_view_count == 0, "Must have at least 1 view"); + + target_size = p_target_size; + internal_size = p_internal_size; + + // FIXME, right now we do this because only our clustered renderer supports FSR upscale + // this does mean that with linear upscale if we use subpasses, we could get into trouble. + if (!can_be_storage) { + internal_size = target_size; + } + + if (p_use_taa) { + // Use negative mipmap LOD bias when TAA is enabled to compensate for loss of sharpness. + // This restores sharpness in still images to be roughly at the same level as without TAA, + // but moving scenes will still be blurrier. + p_texture_mipmap_bias -= 0.5; + } + + if (p_screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) { + // Use negative mipmap LOD bias when FXAA is enabled to compensate for loss of sharpness. + // If both TAA and FXAA are enabled, combine their negative LOD biases together. + p_texture_mipmap_bias -= 0.25; + } + + material_storage->sampler_rd_configure_custom(p_texture_mipmap_bias); + + // need to check if we really need to do this here.. + RendererSceneRenderRD::get_singleton()->update_uniform_sets(); + + render_target = p_render_target; + fsr_sharpness = p_fsr_sharpness; + msaa_3d = p_msaa_3d; + screen_space_aa = p_screen_space_aa; + use_taa = p_use_taa; + use_debanding = p_use_debanding; + view_count = p_view_count; + + /* may move this into our clustered renderer data object */ + if (can_be_storage) { + if (cluster_builder == nullptr) { + cluster_builder = memnew(ClusterBuilderRD); + } + cluster_builder->set_shared(RendererSceneRenderRD::get_singleton()->get_cluster_builder_shared()); + } + + // cleanout any old buffers we had. + cleanup(); + + // create our 3D render buffers + { + // Create our color buffer(s) + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (can_be_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0) | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + usage_bits |= RD::TEXTURE_USAGE_INPUT_ATTACHMENT_BIT; // only needed when using subpasses in the mobile renderer + + // our internal texture should have MSAA support if applicable + if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + } + + create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR, base_data_format, usage_bits); + } + + // Create our depth buffer + { + // TODO If we have depth buffer supplied externally, pick this up + + RD::DataFormat format; + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + + if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) { + format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, (RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT)) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + format = RD::DATA_FORMAT_R32_SFLOAT; + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + } + + create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, format, usage_bits); + } + + // VRS (note, our vrs object will only be set if VRS is supported) + RID vrs_texture; + RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(p_render_target); + if (vrs && vrs_mode != RS::VIEWPORT_VRS_DISABLED) { + uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + vrs_texture = create_texture(RB_SCOPE_VRS, RB_TEXTURE, RD::DATA_FORMAT_R8_UINT, usage_bits, RD::TEXTURE_SAMPLES_1, vrs->get_vrs_texture_size(internal_size)); + } + + for (KeyValue<StringName, Ref<RenderBufferCustomDataRD>> &E : data_buffers) { + E.value->configure(this); + } + + if (cluster_builder) { + RID sampler = RendererRD::MaterialStorage::get_singleton()->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + cluster_builder->setup(internal_size, max_cluster_elements, get_depth_texture(), sampler, get_internal_texture()); + } +} + +void RenderSceneBuffersRD::set_fsr_sharpness(float p_fsr_sharpness) { + fsr_sharpness = p_fsr_sharpness; +} + +void RenderSceneBuffersRD::set_texture_mipmap_bias(float p_texture_mipmap_bias) { + RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); + material_storage->sampler_rd_configure_custom(p_texture_mipmap_bias); +} + +void RenderSceneBuffersRD::set_use_debanding(bool p_use_debanding) { + use_debanding = p_use_debanding; +} + +// Named textures + +bool RenderSceneBuffersRD::has_texture(const StringName &p_context, const StringName &p_texture_name) const { + NTKey key(p_context, p_texture_name); + + return named_textures.has(key); +} + +RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique) { + // Keep some useful data, we use default values when these are 0. + Size2i size = p_size == Size2i(0, 0) ? internal_size : p_size; + uint32_t layers = p_layers == 0 ? view_count : p_layers; + uint32_t mipmaps = p_mipmaps == 0 ? 1 : p_mipmaps; + + // Create our texture + RD::TextureFormat tf; + tf.format = p_data_format; + if (layers > 1) { + tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } + + tf.width = size.x; + tf.height = size.y; + tf.depth = 1; + tf.array_layers = layers; + tf.mipmaps = mipmaps; + tf.usage_bits = p_usage_bits; + tf.samples = p_texture_samples; + + return create_texture_from_format(p_context, p_texture_name, tf, RD::TextureView(), p_unique); +} + +RID RenderSceneBuffersRD::create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view, bool p_unique) { + // TODO p_unique, if p_unique is true, this is a texture that can be shared. This will be implemented later as an optimisation. + + NTKey key(p_context, p_texture_name); + + // check if this is a known texture + if (named_textures.has(key)) { + return named_textures[key].texture; + } + + // Add a new entry.. + NamedTexture &named_texture = named_textures[key]; + named_texture.format = p_texture_format; + named_texture.is_unique = p_unique; + named_texture.texture = RD::get_singleton()->texture_create(p_texture_format, p_view); + + Array arr; + arr.push_back(p_context); + arr.push_back(p_texture_name); + RD::get_singleton()->set_resource_name(named_texture.texture, String("RenderBuffer {0}/{1}").format(arr)); + + update_sizes(named_texture); + + // The rest is lazy created.. + + return named_texture.texture; +} + +RID RenderSceneBuffersRD::create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, RD::TextureView p_view) { + NTKey view_key(p_context, p_view_name); + + // check if this is a known texture + if (named_textures.has(view_key)) { + return named_textures[view_key].texture; + } + + NTKey key(p_context, p_texture_name); + + ERR_FAIL_COND_V(!named_textures.has(key), RID()); + + NamedTexture &named_texture = named_textures[key]; + NamedTexture &view_texture = named_textures[view_key]; + + view_texture.format = named_texture.format; + view_texture.is_unique = named_texture.is_unique; + + view_texture.texture = RD::get_singleton()->texture_create_shared(p_view, named_texture.texture); + + Array arr; + arr.push_back(p_context); + arr.push_back(p_view_name); + RD::get_singleton()->set_resource_name(view_texture.texture, String("RenderBuffer View {0}/{1}").format(arr)); + + update_sizes(named_texture); + + return view_texture.texture; +} + +RID RenderSceneBuffersRD::get_texture(const StringName &p_context, const StringName &p_texture_name) const { + NTKey key(p_context, p_texture_name); + + ERR_FAIL_COND_V(!named_textures.has(key), RID()); + + return named_textures[key].texture; +} + +const RD::TextureFormat RenderSceneBuffersRD::get_texture_format(const StringName &p_context, const StringName &p_texture_name) const { + NTKey key(p_context, p_texture_name); + + ERR_FAIL_COND_V(!named_textures.has(key), RD::TextureFormat()); + + return named_textures[key].format; +} + +RID RenderSceneBuffersRD::get_texture_slice(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap) { + NTKey key(p_context, p_texture_name); + + // check if this is a known texture + ERR_FAIL_COND_V(!named_textures.has(key), RID()); + NamedTexture &named_texture = named_textures[key]; + ERR_FAIL_COND_V(named_texture.texture.is_null(), RID()); + + // check if we're in bounds + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, named_texture.format.array_layers, RID()); + ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, named_texture.format.mipmaps, RID()); + + // if we don't have multiple layers or mipmaps, we can just return our texture as is + if (named_texture.format.array_layers == 1 && named_texture.format.mipmaps == 1) { + return named_texture.texture; + } + + // get our index and make sure we have enough entries in our slices vector + uint32_t index = p_layer * named_texture.format.mipmaps + p_mipmap; + while (named_texture.slices.size() <= int(index)) { + named_texture.slices.push_back(RID()); + } + + // create our slice if we don't have it already + if (named_texture.slices[index].is_null()) { + named_texture.slices.ptrw()[index] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), named_texture.texture, p_layer, p_mipmap); + + Array arr; + arr.push_back(p_context); + arr.push_back(p_texture_name); + arr.push_back(itos(p_layer)); + arr.push_back(itos(p_mipmap)); + RD::get_singleton()->set_resource_name(named_texture.slices[index], String("RenderBuffer {0}/{1} slice {2}/{3}").format(arr)); + } + + // and return our slice + return named_texture.slices[index]; +} + +Size2i RenderSceneBuffersRD::get_texture_slice_size(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap) { + NTKey key(p_context, p_texture_name); + + // check if this is a known texture + ERR_FAIL_COND_V(!named_textures.has(key), Size2i()); + NamedTexture &named_texture = named_textures[key]; + ERR_FAIL_COND_V(named_texture.texture.is_null(), Size2i()); + + // check if we're in bounds + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, named_texture.format.array_layers, Size2i()); + ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, named_texture.format.mipmaps, Size2i()); + + // get our index + uint32_t index = p_layer * named_texture.format.mipmaps + p_mipmap; + + // and return our size + return named_texture.sizes[index]; +} + +void RenderSceneBuffersRD::clear_context(const StringName &p_context) { + Vector<NTKey> to_free; // free these + + // Find all entries for our context, we don't want to free them yet or our loop fails. + for (KeyValue<NTKey, NamedTexture> &E : named_textures) { + if (E.key.context == p_context) { + to_free.push_back(E.key); + } + } + + // Now free these and remove them from our textures + for (NTKey &key : to_free) { + free_named_texture(named_textures[key]); + named_textures.erase(key); + } +} + +// Allocate shared buffers +void RenderSceneBuffersRD::allocate_blur_textures() { + if (has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0)) { + // already allocated... + return; + } + + uint32_t mipmaps_required = Image::get_image_required_mipmaps(internal_size.x, internal_size.y, Image::FORMAT_RGBAH); + + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + if (can_be_storage) { + usage_bits += RD::TEXTURE_USAGE_STORAGE_BIT; + } else { + usage_bits += RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + } + + create_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, base_data_format, usage_bits, RD::TEXTURE_SAMPLES_1, internal_size, view_count, mipmaps_required); + create_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, base_data_format, usage_bits, RD::TEXTURE_SAMPLES_1, Size2i(internal_size.x >> 1, internal_size.y >> 1), view_count, mipmaps_required - 1); + + // if !can_be_storage we need a half width version + if (!can_be_storage) { + create_texture(RB_SCOPE_BUFFERS, RB_TEX_HALF_BLUR, base_data_format, usage_bits, RD::TEXTURE_SAMPLES_1, Size2i(internal_size.x >> 1, internal_size.y), 1, mipmaps_required); + } + + // TODO redo this: + if (!can_be_storage) { + // create 4 weight textures, 2 full size, 2 half size + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16_SFLOAT; // We could probably use DATA_FORMAT_R8_SNORM if we don't pre-multiply by blur_size but that depends on whether we can remove DEPTH_GAP + tf.width = internal_size.x; + tf.height = internal_size.y; + tf.texture_type = RD::TEXTURE_TYPE_2D; + tf.array_layers = 1; // Our DOF effect handles one eye per turn + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tf.mipmaps = 1; + for (uint32_t i = 0; i < 4; i++) { + // associated blur texture + RID texture; + if (i == 1) { + texture = get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 0, 0); + } else if (i == 2) { + texture = get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_1, 0, 0); + } else if (i == 3) { + texture = get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, 0, 1); + } + + // create weight texture + weight_buffers[i].weight = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + // create frame buffer + Vector<RID> fb; + if (i != 0) { + fb.push_back(texture); + } + fb.push_back(weight_buffers[i].weight); + weight_buffers[i].fb = RD::get_singleton()->framebuffer_create(fb); + + if (i == 1) { + // next 2 are half size + tf.width = MAX(1u, tf.width >> 1); + tf.height = MAX(1u, tf.height >> 1); + } + } + } +} + +// Data buffers + +bool RenderSceneBuffersRD::has_custom_data(const StringName &p_name) { + return data_buffers.has(p_name); +} + +void RenderSceneBuffersRD::set_custom_data(const StringName &p_name, Ref<RenderBufferCustomDataRD> p_data) { + if (p_data.is_valid()) { + data_buffers[p_name] = p_data; + } else if (has_custom_data(p_name)) { + data_buffers.erase(p_name); + } +} + +Ref<RenderBufferCustomDataRD> RenderSceneBuffersRD::get_custom_data(const StringName &p_name) const { + ERR_FAIL_COND_V(!data_buffers.has(p_name), Ref<RenderBufferCustomDataRD>()); + + Ref<RenderBufferCustomDataRD> ret = data_buffers[p_name]; + + return ret; +} + +// Velocity texture. + +void RenderSceneBuffersRD::ensure_velocity() { + if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY)) { + uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { + uint32_t msaa_usage_bits = usage_bits | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + + const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { + RD::TEXTURE_SAMPLES_1, + RD::TEXTURE_SAMPLES_2, + RD::TEXTURE_SAMPLES_4, + RD::TEXTURE_SAMPLES_8, + }; + + create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, ts[msaa_3d]); + } + + create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits); + } +} + +RID RenderSceneBuffersRD::get_velocity_buffer(bool p_get_msaa) { + if (p_get_msaa) { + if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA)) { + return RID(); + } else { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA); + } + } else { + if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY)) { + return RID(); + } else { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY); + } + } +} diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h new file mode 100644 index 0000000000..adaf075f80 --- /dev/null +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -0,0 +1,256 @@ +/*************************************************************************/ +/* render_scene_buffers_rd.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_SCENE_BUFFERS_RD_H +#define RENDER_SCENE_BUFFERS_RD_H + +#include "core/templates/hash_map.h" +#include "servers/rendering/renderer_rd/effects/vrs.h" +#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h" +#include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h" +#include "servers/rendering/renderer_scene.h" +#include "servers/rendering/rendering_device.h" +#include "servers/rendering/storage/render_scene_buffers.h" + +// These can be retired in due time +#include "servers/rendering/renderer_rd/cluster_builder_rd.h" +#include "servers/rendering/renderer_rd/effects/ss_effects.h" +#include "servers/rendering/renderer_rd/environment/fog.h" + +#define RB_SCOPE_BUFFERS SNAME("render_buffers") +#define RB_SCOPE_VRS SNAME("VRS") + +#define RB_TEXTURE SNAME("texture") +#define RB_TEX_COLOR SNAME("color") +#define RB_TEX_COLOR_MSAA SNAME("color_msaa") +#define RB_TEX_DEPTH SNAME("depth") +#define RB_TEX_DEPTH_MSAA SNAME("depth_msaa") +#define RB_TEX_VELOCITY SNAME("velocity") +#define RB_TEX_VELOCITY_MSAA SNAME("velocity_msaa") + +#define RB_TEX_BLUR_0 SNAME("blur_0") +#define RB_TEX_BLUR_1 SNAME("blur_1") +#define RB_TEX_HALF_BLUR SNAME("half_blur") // only for raster! + +#define RB_TEX_BACK_DEPTH SNAME("back_depth") + +class RenderSceneBuffersRD : public RenderSceneBuffers { + GDCLASS(RenderSceneBuffersRD, RenderSceneBuffers); + +private: + bool can_be_storage = true; + uint32_t max_cluster_elements = 512; + RD::DataFormat base_data_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + RendererRD::SSEffects *sse = nullptr; + RendererRD::VRS *vrs = nullptr; + uint64_t auto_exposure_version = 1; + + // Our render target represents our final destination that we display on screen. + RID render_target; + Size2i target_size = Size2i(0, 0); + uint32_t view_count = 1; + + // The internal size of the textures we render 3D to in case we render at a lower resolution and upscale + Size2i internal_size = Size2i(0, 0); + float fsr_sharpness = 0.2f; + + // Aliassing settings + RS::ViewportMSAA msaa_3d = RS::VIEWPORT_MSAA_DISABLED; + RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + bool use_taa = false; + bool use_debanding = false; + + // Named Textures + + struct NTKey { + StringName context; + StringName buffer_name; + + bool operator==(const NTKey &p_val) const { + return (context == p_val.context) && (buffer_name == p_val.buffer_name); + } + + static uint32_t hash(const NTKey &p_val) { + // FIXME, properly hash two stringnames together + uint32_t h = p_val.context.hash(); + h = hash_murmur3_one_32(p_val.buffer_name.hash(), h); + return hash_fmix32(h); + } + + NTKey() {} + NTKey(const StringName p_context, const StringName p_texture_name) { + context = p_context; + buffer_name = p_texture_name; + } + }; + + struct NamedTexture { + // Cache the data used to create our texture + RD::TextureFormat format; + bool is_unique; // If marked as unique, we return it into our pool + + // Our texture objects, slices are lazy (i.e. only created when requested). + RID texture; + Vector<RID> slices; + Vector<Size2i> sizes; + }; + + mutable HashMap<NTKey, NamedTexture, NTKey> named_textures; + void update_sizes(NamedTexture &p_named_texture); + void free_named_texture(NamedTexture &p_named_texture); + + // Data buffers + mutable HashMap<StringName, Ref<RenderBufferCustomDataRD>> data_buffers; + +protected: + static void _bind_methods(); + +public: + RenderSceneBuffersRD(); + virtual ~RenderSceneBuffersRD(); + + // info from our renderer + void set_can_be_storage(const bool p_can_be_storage) { can_be_storage = p_can_be_storage; } + void set_max_cluster_elements(const uint32_t p_max_elements) { max_cluster_elements = p_max_elements; } + void set_base_data_format(const RD::DataFormat p_base_data_format) { base_data_format = p_base_data_format; } + RD::DataFormat get_base_data_format() const { return base_data_format; } + void set_sseffects(RendererRD::SSEffects *p_ss_effects) { sse = p_ss_effects; } + void set_vrs(RendererRD::VRS *p_vrs) { vrs = p_vrs; } + + void cleanup(); + virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + virtual void set_fsr_sharpness(float p_fsr_sharpness) override; + virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override; + virtual void set_use_debanding(bool p_use_debanding) override; + + // Named Textures + + bool has_texture(const StringName &p_context, const StringName &p_texture_name) const; + RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true); + RID create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view = RD::TextureView(), bool p_unique = true); + RID create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, RD::TextureView p_view = RD::TextureView()); + RID get_texture(const StringName &p_context, const StringName &p_texture_name) const; + const RD::TextureFormat get_texture_format(const StringName &p_context, const StringName &p_texture_name) const; + RID get_texture_slice(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap); + Size2i get_texture_slice_size(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap); + + void clear_context(const StringName &p_context); + + // Allocate shared buffers + void allocate_blur_textures(); + + // Custom data + bool has_custom_data(const StringName &p_name); + void set_custom_data(const StringName &p_name, Ref<RenderBufferCustomDataRD> p_data); + Ref<RenderBufferCustomDataRD> get_custom_data(const StringName &p_name) const; + + // Getters + + _FORCE_INLINE_ RID get_render_target() const { return render_target; } + _FORCE_INLINE_ uint32_t get_view_count() const { return view_count; } + _FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; } + _FORCE_INLINE_ Size2i get_target_size() const { return target_size; } + _FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; } + _FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa_3d; } + _FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; } + _FORCE_INLINE_ bool get_use_taa() const { return use_taa; } + _FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; } + + uint64_t get_auto_exposure_version() const { return auto_exposure_version; } + void set_auto_exposure_version(const uint64_t p_auto_exposure_version) { auto_exposure_version = p_auto_exposure_version; } + + // For our internal textures we provide some easy access methods. + + _FORCE_INLINE_ RID get_internal_texture() const { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR); + } + _FORCE_INLINE_ RID get_internal_texture(const uint32_t p_layer) { + return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR, p_layer, 0); + } + + _FORCE_INLINE_ RID get_depth_texture() const { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH); + } + _FORCE_INLINE_ RID get_depth_texture(const uint32_t p_layer) { + return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, p_layer, 0); + } + + // back buffer (color) + RID get_back_buffer_texture() const { return has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) ? get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) : RID(); } // We (re)use our blur texture here. + + // Velocity, currently only used by TAA (Clustered) but we'll be using this in other places soon too. + + void ensure_velocity(); + bool has_velocity_buffer(bool p_has_msaa) { return has_texture(RB_SCOPE_BUFFERS, p_has_msaa ? RB_TEX_VELOCITY_MSAA : RB_TEX_VELOCITY); } + RID get_velocity_buffer(bool p_get_msaa); + RID get_velocity_buffer(bool p_get_msaa, uint32_t p_layer) { return get_texture_slice(RB_SCOPE_BUFFERS, p_get_msaa ? RB_TEX_VELOCITY_MSAA : RB_TEX_VELOCITY, p_layer, 0); } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Everything after this needs to be re-evaluated, this is all old implementation + + ClusterBuilderRD *cluster_builder = nullptr; + + struct WeightBuffers { + RID weight; + RID fb; // FB with both texture and weight writing into one level lower + }; + + // 2 full size, 2 half size + WeightBuffers weight_buffers[4]; // Only used in raster + + struct Luminance { + Vector<RID> reduce; + RID current; + + // used only on mobile renderer + Vector<RID> fb; + RID current_fb; + } luminance; + + struct SSEffects { + RID linear_depth; + Vector<RID> linear_depth_slices; + + RID downsample_uniform_set; + + Projection last_frame_projection; + Transform3D last_frame_transform; + + RendererRD::SSEffects::SSAORenderBuffers ssao; + RendererRD::SSEffects::SSILRenderBuffers ssil; + } ss_effects; + + RendererRD::SSEffects::SSRRenderBuffers ssr; + + RID get_ao_texture() const { return ss_effects.ssao.ao_final; } + RID get_ssil_texture() const { return ss_effects.ssil.ssil_final; } +}; + +#endif // RENDER_SCENE_BUFFERS_RD_H diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index ce19187071..b87b4d4a0f 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -2120,6 +2120,7 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { if (rt->color.is_valid()) { RD::get_singleton()->free(rt->color); } + rt->color_slices.clear(); // these are automatically freed. if (rt->color_multisample.is_valid()) { RD::get_singleton()->free(rt->color_multisample); @@ -2174,6 +2175,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { } rd_color_attachment_format.samples = RD::TEXTURE_SAMPLES_1; rd_color_attachment_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + rd_color_attachment_format.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; // FIXME we need this only when FSR is enabled rd_color_attachment_format.shareable_formats.push_back(rt->color_format); rd_color_attachment_format.shareable_formats.push_back(rt->color_format_srgb); if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) { @@ -2392,6 +2394,24 @@ RID TextureStorage::render_target_get_rd_texture(RID p_render_target) { return rt->color; } +RID TextureStorage::render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + if (rt->view_count == 1) { + return rt->color; + } else { + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, rt->view_count, RID()); + if (rt->color_slices.size() == 0) { + for (uint32_t v = 0; v < rt->view_count; v++) { + RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->color, v, 0); + rt->color_slices.push_back(slice); + } + } + return rt->color_slices[p_layer]; + } +} + RID TextureStorage::render_target_get_rd_backbuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 25d355d878..a3acad30f3 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -254,6 +254,7 @@ private: uint32_t view_count; RID framebuffer; RID color; + Vector<RID> color_slices; RID color_multisample; // Needed when MSAA is enabled. RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; @@ -589,6 +590,7 @@ public: Size2 render_target_get_size(RID p_render_target); RID render_target_get_rd_framebuffer(RID p_render_target); RID render_target_get_rd_texture(RID p_render_target); + RID render_target_get_rd_texture_slice(RID p_render_target, uint32_t p_layer); RID render_target_get_rd_backbuffer(RID p_render_target); RID render_target_get_rd_backbuffer_framebuffer(RID p_render_target); diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp index fcef2f24bf..b80bcd514f 100644 --- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp +++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp @@ -155,8 +155,8 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance Dependency *dependency = TextureStorage::get_singleton()->decal_get_dependency(p_base); p_instance->update_dependency(dependency); } else if (GI::get_singleton()->owns_voxel_gi(p_base)) { - GI::VoxelGI *gip = GI::get_singleton()->get_voxel_gi(p_base); - p_instance->update_dependency(&gip->dependency); + Dependency *dependency = GI::get_singleton()->voxel_gi_get_dependency(p_base); + p_instance->update_dependency(dependency); } else if (LightStorage::get_singleton()->owns_lightmap(p_base)) { Dependency *dependency = LightStorage::get_singleton()->lightmap_get_dependency(p_base); p_instance->update_dependency(dependency); diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 852fe89cd0..895b9f6e5d 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -31,6 +31,7 @@ #ifndef RENDERER_SCENE_H #define RENDERER_SCENE_H +#include "servers/rendering/storage/render_scene_buffers.h" #include "servers/rendering_server.h" #include "servers/xr/xr_interface.h" @@ -45,7 +46,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; - virtual void camera_set_camera_effects(RID p_camera, RID p_fx) = 0; + virtual void camera_set_camera_attributes(RID p_camera, RID p_attributes) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; virtual bool is_camera(RID p_camera) const = 0; @@ -57,7 +58,7 @@ public: virtual void scenario_initialize(RID p_rid) = 0; virtual void scenario_set_environment(RID p_scenario, RID p_environment) = 0; - virtual void scenario_set_camera_effects(RID p_scenario, RID p_fx) = 0; + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_attributes) = 0; virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment) = 0; virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count) = 0; virtual bool is_scenario(RID p_scenario) const = 0; @@ -128,7 +129,7 @@ public: virtual void environment_set_sky_custom_fov(RID p_env, float p_scale) = 0; virtual void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) = 0; virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0; - virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0; + virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0; virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0; virtual void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) = 0; @@ -137,7 +138,8 @@ public: virtual float environment_get_sky_custom_fov(RID p_env) const = 0; virtual Basis environment_get_sky_orientation(RID p_env) const = 0; virtual Color environment_get_bg_color(RID p_env) const = 0; - virtual float environment_get_bg_energy(RID p_env) const = 0; + virtual float environment_get_bg_energy_multiplier(RID p_env) const = 0; + virtual float environment_get_bg_intensity(RID p_env) const = 0; virtual int environment_get_canvas_max_layer(RID p_env) const = 0; virtual RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const = 0; virtual Color environment_get_ambient_light(RID p_env) const = 0; @@ -146,20 +148,14 @@ public: virtual RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const = 0; // Tonemap - virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0; + virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0; virtual RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const = 0; virtual float environment_get_exposure(RID p_env) const = 0; virtual float environment_get_white(RID p_env) const = 0; - virtual bool environment_get_auto_exposure(RID p_env) const = 0; - virtual float environment_get_min_luminance(RID p_env) const = 0; - virtual float environment_get_max_luminance(RID p_env) const = 0; - virtual float environment_get_auto_exp_speed(RID p_env) const = 0; - virtual float environment_get_auto_exp_scale(RID p_env) const = 0; - virtual uint64_t environment_get_auto_exposure_version(RID p_env) const = 0; // Fog - virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) = 0; + virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) = 0; virtual bool environment_get_fog_enabled(RID p_env) const = 0; virtual Color environment_get_fog_light_color(RID p_env) const = 0; @@ -169,9 +165,10 @@ public: virtual float environment_get_fog_height(RID p_env) const = 0; virtual float environment_get_fog_height_density(RID p_env) const = 0; virtual float environment_get_fog_aerial_perspective(RID p_env) const = 0; + virtual float environment_get_fog_sky_affect(RID p_env) const = 0; // Volumetric Fog - virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) = 0; + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) = 0; virtual bool environment_get_volumetric_fog_enabled(RID p_env) const = 0; virtual float environment_get_volumetric_fog_density(RID p_env) const = 0; @@ -182,6 +179,7 @@ public: virtual float environment_get_volumetric_fog_length(RID p_env) const = 0; virtual float environment_get_volumetric_fog_detail_spread(RID p_env) const = 0; virtual float environment_get_volumetric_fog_gi_inject(RID p_env) const = 0; + virtual float environment_get_volumetric_fog_sky_affect(RID p_env) const = 0; virtual bool environment_get_volumetric_fog_temporal_reprojection(RID p_env) const = 0; virtual float environment_get_volumetric_fog_temporal_reprojection_amount(RID p_env) const = 0; virtual float environment_get_volumetric_fog_ambient_inject(RID p_env) const = 0; @@ -266,7 +264,6 @@ public: virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0; - // Adjustment virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual bool environment_get_adjustments_enabled(RID p_env) const = 0; @@ -284,17 +281,6 @@ public: virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) = 0; virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) = 0; - /* Camera Effects */ - - virtual RID camera_effects_allocate() = 0; - virtual void camera_effects_initialize(RID p_rid) = 0; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) = 0; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) = 0; - - 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) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; - virtual void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; virtual void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; @@ -304,9 +290,7 @@ public: /* Render Buffers */ - virtual RID render_buffers_create() = 0; - - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) = 0; + virtual Ref<RenderSceneBuffers> render_buffers_create() = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0; @@ -317,13 +301,13 @@ public: virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; - virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; + virtual void render_empty_scene(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; struct RenderInfo { int info[RS::VIEWPORT_RENDER_INFO_TYPE_MAX][RS::VIEWPORT_RENDER_INFO_MAX] = {}; }; - virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RenderInfo *r_render_info = nullptr) = 0; + virtual void render_camera(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RenderInfo *r_render_info = nullptr) = 0; virtual void update() = 0; virtual void render_probes() = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 4a60f124cd..0e03058c11 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -93,10 +93,10 @@ void RendererSceneCull::camera_set_environment(RID p_camera, RID p_env) { camera->env = p_env; } -void RendererSceneCull::camera_set_camera_effects(RID p_camera, RID p_fx) { +void RendererSceneCull::camera_set_camera_attributes(RID p_camera, RID p_attributes) { Camera *camera = camera_owner.get_or_null(p_camera); ERR_FAIL_COND(!camera); - camera->effects = p_fx; + camera->attributes = p_attributes; } void RendererSceneCull::camera_set_use_vertical_aspect(RID p_camera, bool p_enable) { @@ -378,10 +378,10 @@ void RendererSceneCull::scenario_set_environment(RID p_scenario, RID p_environme scenario->environment = p_environment; } -void RendererSceneCull::scenario_set_camera_effects(RID p_scenario, RID p_camera_effects) { +void RendererSceneCull::scenario_set_camera_attributes(RID p_scenario, RID p_camera_attributes) { Scenario *scenario = scenario_owner.get_or_null(p_scenario); ERR_FAIL_COND(!scenario); - scenario->camera_effects = p_camera_effects; + scenario->camera_attributes = p_camera_attributes; } void RendererSceneCull::scenario_set_fallback_environment(RID p_scenario, RID p_environment) { @@ -2450,7 +2450,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons return animated_material_found; } -void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RenderInfo *r_render_info) { +void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RenderInfo *r_render_info) { #ifndef _3D_DISABLED Camera *camera = camera_owner.get_or_null(p_camera); @@ -2537,7 +2537,7 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_ // For now just cull on the first camera RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_orthogonal); - _render_scene(&camera_data, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info); + _render_scene(&camera_data, p_render_buffers, environment, camera->attributes, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info); #endif } @@ -2910,7 +2910,7 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul } } -void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows, RendererScene::RenderInfo *r_render_info) { +void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, RID p_force_camera_attributes, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows, RendererScene::RenderInfo *r_render_info) { Instance *render_reflection_probe = instance_owner.get_or_null(p_reflection_probe); //if null, not rendering to it Scenario *scenario = scenario_owner.get_or_null(p_scenario); @@ -3240,11 +3240,11 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c scene_cull_result.light_instances.push_back(directional_lights[i]); } - RID camera_effects; - if (p_force_camera_effects.is_valid()) { - camera_effects = p_force_camera_effects; + RID camera_attributes; + if (p_force_camera_attributes.is_valid()) { + camera_attributes = p_force_camera_attributes; } else { - camera_effects = scenario->camera_effects; + camera_attributes = scenario->camera_attributes; } /* PROCESS GEOMETRY AND DRAW SCENE */ @@ -3256,7 +3256,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } RENDER_TIMESTAMP("Render 3D Scene"); - scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data, r_render_info); + scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_attributes, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data, r_render_info); if (p_viewport.is_valid()) { RSG::viewport->viewport_set_prev_camera_data(p_viewport, p_camera_data); @@ -3270,8 +3270,6 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) { render_sdfgi_data[i].instances.clear(); } - - // virtual void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold,const RenderShadowData *p_render_shadows,int p_render_shadow_count,const RenderSDFGIData *p_render_sdfgi_regions,int p_render_sdfgi_region_count,const RenderSDFGIStaticLightData *p_render_sdfgi_static_lights=nullptr) = 0; } RID RendererSceneCull::_render_get_environment(RID p_camera, RID p_scenario) { @@ -3295,7 +3293,7 @@ RID RendererSceneCull::_render_get_environment(RID p_camera, RID p_scenario) { return RID(); } -void RendererSceneCull::render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) { +void RendererSceneCull::render_empty_scene(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_scenario, RID p_shadow_atlas) { #ifndef _3D_DISABLED Scenario *scenario = scenario_owner.get_or_null(p_scenario); @@ -3383,7 +3381,8 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int RendererSceneRender::CameraData camera_data; camera_data.set_camera(xform, cm, false, false); - _render_scene(&camera_data, RID(), environment, RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, mesh_lod_threshold, use_shadows); + Ref<RenderSceneBuffers> render_buffers; + _render_scene(&camera_data, render_buffers, environment, RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, mesh_lod_threshold, use_shadows); } else { //do roughness postprocess step until it believes it's done @@ -3479,6 +3478,7 @@ void RendererSceneCull::render_probes() { cache->transform != instance->transform || cache->color != RSG::light_storage->light_get_color(instance->base) || cache->energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->intensity != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY) || cache->bake_energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || cache->radius != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || cache->attenuation != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || @@ -3510,6 +3510,7 @@ void RendererSceneCull::render_probes() { cache->transform != instance->transform || cache->color != RSG::light_storage->light_get_color(instance->base) || cache->energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->intensity != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY) || cache->bake_energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || cache->radius != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || cache->attenuation != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || @@ -3556,6 +3557,7 @@ void RendererSceneCull::render_probes() { cache->transform = instance->transform; cache->color = RSG::light_storage->light_get_color(instance->base); cache->energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->intensity = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY); cache->bake_energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); cache->radius = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); cache->attenuation = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); @@ -3578,6 +3580,7 @@ void RendererSceneCull::render_probes() { cache->transform = instance->transform; cache->color = RSG::light_storage->light_get_color(instance->base); cache->energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->intensity = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY); cache->bake_energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); cache->radius = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); cache->attenuation = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 985749a45d..0e8fdf789a 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -76,7 +76,7 @@ public: uint32_t visible_layers; bool vaspect; RID env; - RID effects; + RID attributes; Transform3D transform; @@ -103,7 +103,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform); virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); virtual void camera_set_environment(RID p_camera, RID p_env); - virtual void camera_set_camera_effects(RID p_camera, RID p_fx); + virtual void camera_set_camera_attributes(RID p_camera, RID p_attributes); virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); virtual bool is_camera(RID p_camera) const; @@ -320,7 +320,7 @@ public: List<Instance *> directional_lights; RID environment; RID fallback_environment; - RID camera_effects; + RID camera_attributes; RID reflection_probe_shadow_atlas; RID reflection_atlas; uint64_t used_viewport_visibility_bits; @@ -354,7 +354,7 @@ public: virtual void scenario_initialize(RID p_rid); virtual void scenario_set_environment(RID p_scenario, RID p_environment); - virtual void scenario_set_camera_effects(RID p_scenario, RID p_fx); + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_attributes); virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment); virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count); virtual bool is_scenario(RID p_scenario) const; @@ -683,6 +683,7 @@ public: Transform3D transform; Color color; float energy; + float intensity; float bake_energy; float radius; float attenuation; @@ -1054,10 +1055,10 @@ public: _FORCE_INLINE_ bool _visibility_parent_check(const CullData &p_cull_data, const InstanceData &p_instance_data); bool _render_reflection_probe_step(Instance *p_instance, int p_step); - void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows = true, RenderInfo *r_render_info = nullptr); - void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); + void _render_scene(const RendererSceneRender::CameraData *p_camera_data, const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, RID p_force_camera_attributes, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows = true, RenderInfo *r_render_info = nullptr); + void render_empty_scene(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_scenario, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RendererScene::RenderInfo *r_render_info = nullptr); + void render_camera(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RendererScene::RenderInfo *r_render_info = nullptr); void update_dirty_instances(); void render_particle_colliders(); @@ -1099,7 +1100,7 @@ public: PASS2(environment_set_sky_custom_fov, RID, float) PASS2(environment_set_sky_orientation, RID, const Basis &) PASS2(environment_set_bg_color, RID, const Color &) - PASS2(environment_set_bg_energy, RID, float) + PASS3(environment_set_bg_energy, RID, float, float) PASS2(environment_set_canvas_max_layer, RID, int) PASS6(environment_set_ambient_light, RID, const Color &, RS::EnvironmentAmbientSource, float, float, RS::EnvironmentReflectionSource) @@ -1108,7 +1109,8 @@ public: PASS1RC(float, environment_get_sky_custom_fov, RID) PASS1RC(Basis, environment_get_sky_orientation, RID) PASS1RC(Color, environment_get_bg_color, RID) - PASS1RC(float, environment_get_bg_energy, RID) + PASS1RC(float, environment_get_bg_energy_multiplier, RID) + PASS1RC(float, environment_get_bg_intensity, RID) PASS1RC(int, environment_get_canvas_max_layer, RID) PASS1RC(RS::EnvironmentAmbientSource, environment_get_ambient_source, RID) PASS1RC(Color, environment_get_ambient_light, RID) @@ -1117,25 +1119,20 @@ public: PASS1RC(RS::EnvironmentReflectionSource, environment_get_reflection_source, RID) // Tonemap - PASS9(environment_set_tonemap, RID, RS::EnvironmentToneMapper, float, float, bool, float, float, float, float) + PASS4(environment_set_tonemap, RID, RS::EnvironmentToneMapper, float, float) PASS1RC(RS::EnvironmentToneMapper, environment_get_tone_mapper, RID) PASS1RC(float, environment_get_exposure, RID) PASS1RC(float, environment_get_white, RID) - PASS1RC(bool, environment_get_auto_exposure, RID) - PASS1RC(float, environment_get_min_luminance, RID) - PASS1RC(float, environment_get_max_luminance, RID) - PASS1RC(float, environment_get_auto_exp_speed, RID) - PASS1RC(float, environment_get_auto_exp_scale, RID) - PASS1RC(uint64_t, environment_get_auto_exposure_version, RID) // Fog - PASS9(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float) + PASS10(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float) PASS1RC(bool, environment_get_fog_enabled, RID) PASS1RC(Color, environment_get_fog_light_color, RID) PASS1RC(float, environment_get_fog_light_energy, RID) PASS1RC(float, environment_get_fog_sun_scatter, RID) PASS1RC(float, environment_get_fog_density, RID) + PASS1RC(float, environment_get_fog_sky_affect, RID) PASS1RC(float, environment_get_fog_height, RID) PASS1RC(float, environment_get_fog_height_density, RID) PASS1RC(float, environment_get_fog_aerial_perspective, RID) @@ -1144,7 +1141,7 @@ public: PASS1(environment_set_volumetric_fog_filter_active, bool) // Volumentric Fog - PASS13(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float) + PASS14(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float, float) PASS1RC(bool, environment_get_volumetric_fog_enabled, RID) PASS1RC(float, environment_get_volumetric_fog_density, RID) @@ -1155,6 +1152,7 @@ public: PASS1RC(float, environment_get_volumetric_fog_length, RID) PASS1RC(float, environment_get_volumetric_fog_detail_spread, RID) PASS1RC(float, environment_get_volumetric_fog_gi_inject, RID) + PASS1RC(float, environment_get_volumetric_fog_sky_affect, RID) PASS1RC(bool, environment_get_volumetric_fog_temporal_reprojection, RID) PASS1RC(float, environment_get_volumetric_fog_temporal_reprojection_amount, RID) PASS1RC(float, environment_get_volumetric_fog_ambient_inject, RID) @@ -1250,17 +1248,6 @@ public: PASS1(sub_surface_scattering_set_quality, RS::SubSurfaceScatteringQuality) PASS2(sub_surface_scattering_set_scale, float, float) - /* CAMERA EFFECTS */ - - PASS0R(RID, camera_effects_allocate) - PASS1(camera_effects_initialize, RID) - - PASS2(camera_effects_set_dof_blur_quality, RS::DOFBlurQuality, bool) - PASS1(camera_effects_set_dof_blur_bokeh_shape, RS::DOFBokehShape) - - PASS8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) - PASS3(camera_effects_set_custom_exposure, RID, bool, float) - PASS1(positional_soft_shadow_filter_set_quality, RS::ShadowQuality) PASS1(directional_soft_shadow_filter_set_quality, RS::ShadowQuality) @@ -1268,8 +1255,7 @@ public: /* Render Buffers */ - PASS0R(RID, render_buffers_create) - PASS13(render_buffers_configure, RID, RID, int, int, int, int, float, float, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool, bool, uint32_t) + PASS0R(Ref<RenderSceneBuffers>, render_buffers_create) PASS1(gi_set_use_half_resolution, bool) /* Shadow Atlas */ diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index e024e59ec3..f085168df3 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -224,8 +224,8 @@ void RendererSceneRender::environment_set_bg_color(RID p_env, const Color &p_col environment_storage.environment_set_bg_color(p_env, p_color); } -void RendererSceneRender::environment_set_bg_energy(RID p_env, float p_energy) { - environment_storage.environment_set_bg_energy(p_env, p_energy); +void RendererSceneRender::environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) { + environment_storage.environment_set_bg_energy(p_env, p_multiplier, p_exposure_value); } void RendererSceneRender::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { @@ -256,8 +256,12 @@ Color RendererSceneRender::environment_get_bg_color(RID p_env) const { return environment_storage.environment_get_bg_color(p_env); } -float RendererSceneRender::environment_get_bg_energy(RID p_env) const { - return environment_storage.environment_get_bg_energy(p_env); +float RendererSceneRender::environment_get_bg_energy_multiplier(RID p_env) const { + return environment_storage.environment_get_bg_energy_multiplier(p_env); +} + +float RendererSceneRender::environment_get_bg_intensity(RID p_env) const { + return environment_storage.environment_get_bg_intensity(p_env); } int RendererSceneRender::environment_get_canvas_max_layer(RID p_env) const { @@ -286,8 +290,8 @@ RS::EnvironmentReflectionSource RendererSceneRender::environment_get_reflection_ // Tonemap -void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { - environment_storage.environment_set_tonemap(p_env, p_tone_mapper, p_exposure, p_white, p_auto_exposure, p_min_luminance, p_max_luminance, p_auto_exp_speed, p_auto_exp_scale); +void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) { + environment_storage.environment_set_tonemap(p_env, p_tone_mapper, p_exposure, p_white); } RS::EnvironmentToneMapper RendererSceneRender::environment_get_tone_mapper(RID p_env) const { @@ -302,34 +306,10 @@ float RendererSceneRender::environment_get_white(RID p_env) const { return environment_storage.environment_get_white(p_env); } -bool RendererSceneRender::environment_get_auto_exposure(RID p_env) const { - return environment_storage.environment_get_auto_exposure(p_env); -} - -float RendererSceneRender::environment_get_min_luminance(RID p_env) const { - return environment_storage.environment_get_min_luminance(p_env); -} - -float RendererSceneRender::environment_get_max_luminance(RID p_env) const { - return environment_storage.environment_get_max_luminance(p_env); -} - -float RendererSceneRender::environment_get_auto_exp_speed(RID p_env) const { - return environment_storage.environment_get_auto_exp_speed(p_env); -} - -float RendererSceneRender::environment_get_auto_exp_scale(RID p_env) const { - return environment_storage.environment_get_auto_exp_scale(p_env); -} - -uint64_t RendererSceneRender::environment_get_auto_exposure_version(RID p_env) const { - return environment_storage.environment_get_auto_exposure_version(p_env); -} - // Fog -void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) { - environment_storage.environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective); +void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) { + environment_storage.environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective, p_sky_affect); } bool RendererSceneRender::environment_get_fog_enabled(RID p_env) const { @@ -352,6 +332,10 @@ float RendererSceneRender::environment_get_fog_density(RID p_env) const { return environment_storage.environment_get_fog_density(p_env); } +float RendererSceneRender::environment_get_fog_sky_affect(RID p_env) const { + return environment_storage.environment_get_fog_sky_affect(p_env); +} + float RendererSceneRender::environment_get_fog_height(RID p_env) const { return environment_storage.environment_get_fog_height(p_env); } @@ -366,8 +350,8 @@ float RendererSceneRender::environment_get_fog_aerial_perspective(RID p_env) con // Volumetric Fog -void RendererSceneRender::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) { - environment_storage.environment_set_volumetric_fog(p_env, p_enable, p_density, p_albedo, p_emission, p_emission_energy, p_anisotropy, p_length, p_detail_spread, p_gi_inject, p_temporal_reprojection, p_temporal_reprojection_amount, p_ambient_inject); +void RendererSceneRender::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) { + environment_storage.environment_set_volumetric_fog(p_env, p_enable, p_density, p_albedo, p_emission, p_emission_energy, p_anisotropy, p_length, p_detail_spread, p_gi_inject, p_temporal_reprojection, p_temporal_reprojection_amount, p_ambient_inject, p_sky_affect); } bool RendererSceneRender::environment_get_volumetric_fog_enabled(RID p_env) const { @@ -406,6 +390,10 @@ float RendererSceneRender::environment_get_volumetric_fog_gi_inject(RID p_env) c return environment_storage.environment_get_volumetric_fog_gi_inject(p_env); } +float RendererSceneRender::environment_get_volumetric_fog_sky_affect(RID p_env) const { + return environment_storage.environment_get_volumetric_fog_sky_affect(p_env); +} + bool RendererSceneRender::environment_get_volumetric_fog_temporal_reprojection(RID p_env) const { return environment_storage.environment_get_volumetric_fog_temporal_reprojection(p_env); } diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 7f70f4b939..2b18621fff 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -36,6 +36,7 @@ #include "servers/rendering/renderer_geometry_instance.h" #include "servers/rendering/renderer_scene.h" #include "servers/rendering/storage/environment_storage.h" +#include "storage/render_scene_buffers.h" #include "storage/utilities.h" class RendererSceneRender { @@ -68,10 +69,10 @@ public: /* SDFGI UPDATE */ - virtual void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0; - virtual int sdfgi_get_pending_region_count(RID p_render_buffers) const = 0; - virtual AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const = 0; - virtual uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const = 0; + virtual void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0; + virtual int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const = 0; + virtual AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const = 0; + virtual uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const = 0; /* SKY API */ @@ -97,7 +98,7 @@ public: void environment_set_sky_custom_fov(RID p_env, float p_scale); void environment_set_sky_orientation(RID p_env, const Basis &p_orientation); void environment_set_bg_color(RID p_env, const Color &p_color); - void environment_set_bg_energy(RID p_env, float p_energy); + void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value); void environment_set_canvas_max_layer(RID p_env, int p_max_layer); void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG); // FIXME: Disabled during Vulkan refactoring, should be ported. @@ -110,7 +111,8 @@ public: float environment_get_sky_custom_fov(RID p_env) const; Basis environment_get_sky_orientation(RID p_env) const; Color environment_get_bg_color(RID p_env) const; - float environment_get_bg_energy(RID p_env) const; + float environment_get_bg_energy_multiplier(RID p_env) const; + float environment_get_bg_intensity(RID p_env) const; int environment_get_canvas_max_layer(RID p_env) const; RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const; Color environment_get_ambient_light(RID p_env) const; @@ -119,30 +121,25 @@ public: RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const; // Tonemap - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white); RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; - bool environment_get_auto_exposure(RID p_env) const; - float environment_get_min_luminance(RID p_env) const; - float environment_get_max_luminance(RID p_env) const; - float environment_get_auto_exp_speed(RID p_env) const; - float environment_get_auto_exp_scale(RID p_env) const; - uint64_t environment_get_auto_exposure_version(RID p_env) const; // Fog - void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective); + void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect); bool environment_get_fog_enabled(RID p_env) const; Color environment_get_fog_light_color(RID p_env) const; float environment_get_fog_light_energy(RID p_env) const; float environment_get_fog_sun_scatter(RID p_env) const; float environment_get_fog_density(RID p_env) const; + float environment_get_fog_sky_affect(RID p_env) const; float environment_get_fog_height(RID p_env) const; float environment_get_fog_height_density(RID p_env) const; float environment_get_fog_aerial_perspective(RID p_env) const; // Volumetric Fog - void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject); + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect); bool environment_get_volumetric_fog_enabled(RID p_env) const; float environment_get_volumetric_fog_density(RID p_env) const; Color environment_get_volumetric_fog_scattering(RID p_env) const; @@ -152,6 +149,7 @@ public: float environment_get_volumetric_fog_length(RID p_env) const; float environment_get_volumetric_fog_detail_spread(RID p_env) const; float environment_get_volumetric_fog_gi_inject(RID p_env) const; + float environment_get_volumetric_fog_sky_affect(RID p_env) const; bool environment_get_volumetric_fog_temporal_reprojection(RID p_env) const; float environment_get_volumetric_fog_temporal_reprojection_amount(RID p_env) const; float environment_get_volumetric_fog_ambient_inject(RID p_env) const; @@ -239,15 +237,6 @@ public: virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0; - virtual RID camera_effects_allocate() = 0; - virtual void camera_effects_initialize(RID p_rid) = 0; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) = 0; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) = 0; - - 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) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; - virtual void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; virtual void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; @@ -331,7 +320,7 @@ public: void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect); }; - virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) = 0; + virtual void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) = 0; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) = 0; @@ -340,8 +329,7 @@ public: virtual void set_time(double p_time, double p_step) = 0; virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0; - virtual RID render_buffers_create() = 0; - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) = 0; + virtual Ref<RenderSceneBuffers> render_buffers_create() = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0; virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index b6f515b223..d466f90e79 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -110,8 +110,7 @@ Vector<RendererViewport::Viewport *> RendererViewport::_sort_active_viewports() void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { if (p_viewport->render_buffers.is_valid()) { if (p_viewport->size.width == 0 || p_viewport->size.height == 0) { - RSG::scene->free(p_viewport->render_buffers); - p_viewport->render_buffers = RID(); + p_viewport->render_buffers.unref(); } else { const float scaling_3d_scale = p_viewport->scaling_3d_scale; RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode; @@ -177,7 +176,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { // to compensate for the loss of sharpness. const float texture_mipmap_bias = log2f(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias; - RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, render_width, render_height, width, height, p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->get_view_count()); + p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->get_view_count()); } } } @@ -784,7 +783,9 @@ void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpn ERR_FAIL_COND(!viewport); viewport->fsr_sharpness = p_sharpness; - _configure_3d_render_buffers(viewport); + if (viewport->render_buffers.is_valid()) { + viewport->render_buffers->set_fsr_sharpness(p_sharpness); + } } void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) { @@ -792,7 +793,9 @@ void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_ ERR_FAIL_COND(!viewport); viewport->texture_mipmap_bias = p_mipmap_bias; - _configure_3d_render_buffers(viewport); + if (viewport->render_buffers.is_valid()) { + viewport->render_buffers->set_texture_mipmap_bias(p_mipmap_bias); + } } void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) { @@ -1128,7 +1131,9 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb return; } viewport->use_debanding = p_use_debanding; - _configure_3d_render_buffers(viewport); + if (viewport->render_buffers.is_valid()) { + viewport->render_buffers->set_use_debanding(p_use_debanding); + } } void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { @@ -1284,7 +1289,7 @@ bool RendererViewport::free(RID p_rid) { RSG::texture_storage->render_target_free(viewport->render_target); RSG::scene->free(viewport->shadow_atlas); if (viewport->render_buffers.is_valid()) { - RSG::scene->free(viewport->render_buffers); + viewport->render_buffers.unref(); } while (viewport->canvas_map.begin()) { diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index f64b2a986f..a123c70372 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -38,6 +38,7 @@ #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" #include "servers/xr/xr_interface.h" +#include "storage/render_scene_buffers.h" class RendererViewport { public: @@ -64,7 +65,7 @@ public: RS::ViewportUpdateMode update_mode = RenderingServer::VIEWPORT_UPDATE_WHEN_VISIBLE; RID render_target; RID render_target_texture; - RID render_buffers; + Ref<RenderSceneBuffers> render_buffers; RS::ViewportMSAA msaa_2d = RenderingServer::VIEWPORT_MSAA_DISABLED; RS::ViewportMSAA msaa_3d = RenderingServer::VIEWPORT_MSAA_DISABLED; diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 276ba8f247..ed88f57a94 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -464,6 +464,32 @@ public: Vector<DataFormat> shareable_formats; bool is_resolve_buffer = false; + bool operator==(const TextureFormat &b) const { + if (format != b.format) { + return false; + } else if (width != b.width) { + return false; + } else if (height != b.height) { + return false; + } else if (depth != b.depth) { + return false; + } else if (array_layers != b.array_layers) { + return false; + } else if (mipmaps != b.mipmaps) { + return false; + } else if (texture_type != b.texture_type) { + return false; + } else if (samples != b.samples) { + return false; + } else if (usage_bits != b.usage_bits) { + return false; + } else if (shareable_formats != b.shareable_formats) { + return false; + } else { + return true; + } + } + TextureFormat() { format = DATA_FORMAT_R8_UNORM; width = 1; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 8071b7e416..1f686069bd 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -397,6 +397,7 @@ RenderingServerDefault::RenderingServerDefault(bool p_create_thread) : RSG::canvas = memnew(RendererCanvasCull); RSG::viewport = memnew(RendererViewport); RendererSceneCull *sr = memnew(RendererSceneCull); + RSG::camera_attributes = memnew(RendererCameraAttributes); RSG::scene = sr; RSG::rasterizer = RendererCompositor::create(); RSG::utilities = RSG::rasterizer->get_utilities(); @@ -418,4 +419,5 @@ RenderingServerDefault::~RenderingServerDefault() { memdelete(RSG::viewport); memdelete(RSG::rasterizer); memdelete(RSG::scene); + memdelete(RSG::camera_attributes); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 3ce0fb8b2c..4de84b0f3b 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -403,6 +403,7 @@ public: FUNC2(lightmap_set_probe_bounds, RID, const AABB &) FUNC2(lightmap_set_probe_interior, RID, bool) FUNC5(lightmap_set_probe_capture_data, RID, const PackedVector3Array &, const PackedColorArray &, const PackedInt32Array &, const PackedInt32Array &) + FUNC2(lightmap_set_baked_exposure_normalization, RID, float) FUNC1RC(PackedVector3Array, lightmap_get_probe_capture_points, RID) FUNC1RC(PackedColorArray, lightmap_get_probe_capture_sh, RID) FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_tetrahedra, RID) @@ -453,6 +454,7 @@ public: FUNC2(voxel_gi_set_dynamic_range, RID, float) FUNC2(voxel_gi_set_propagation, RID, float) FUNC2(voxel_gi_set_energy, RID, float) + FUNC2(voxel_gi_set_baked_exposure_normalization, RID, float) FUNC2(voxel_gi_set_bias, RID, float) FUNC2(voxel_gi_set_normal_bias, RID, float) FUNC2(voxel_gi_set_interior, RID, bool) @@ -560,7 +562,7 @@ public: FUNC2(camera_set_transform, RID, const Transform3D &) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID) - FUNC2(camera_set_camera_effects, RID, RID) + FUNC2(camera_set_camera_attributes, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) /* OCCLUDER */ @@ -668,7 +670,7 @@ public: FUNC2(environment_set_sky_custom_fov, RID, float) FUNC2(environment_set_sky_orientation, RID, const Basis &) FUNC2(environment_set_bg_color, RID, const Color &) - FUNC2(environment_set_bg_energy, RID, float) + FUNC3(environment_set_bg_energy, RID, float, float) FUNC2(environment_set_canvas_max_layer, RID, int) FUNC6(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource) @@ -689,12 +691,12 @@ public: FUNC1(environment_glow_set_use_bicubic_upscale, bool) FUNC1(environment_glow_set_use_high_quality, bool) - FUNC9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) + FUNC4(environment_set_tonemap, RID, EnvironmentToneMapper, float, float) FUNC7(environment_set_adjustment, RID, bool, float, float, float, bool, RID) - FUNC9(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float) - FUNC13(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float) + FUNC10(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float) + FUNC14(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float, float) FUNC2(environment_set_volumetric_fog_volume_size, int, int) FUNC1(environment_set_volumetric_fog_filter_active, bool) @@ -710,21 +712,28 @@ public: FUNC1(sub_surface_scattering_set_quality, SubSurfaceScatteringQuality) FUNC2(sub_surface_scattering_set_scale, float, float) - /* CAMERA EFFECTS */ - - FUNCRIDSPLIT(camera_effects) - - FUNC2(camera_effects_set_dof_blur_quality, DOFBlurQuality, bool) - FUNC1(camera_effects_set_dof_blur_bokeh_shape, DOFBokehShape) - - FUNC8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) - FUNC3(camera_effects_set_custom_exposure, RID, bool, float) - FUNC1(positional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(decals_set_filter, RS::DecalFilter); FUNC1(light_projectors_set_filter, RS::LightProjectorFilter); + /* CAMERA ATTRIBUTES */ + +#undef server_name +#undef ServerName +//from now on, calls forwarded to this singleton +#define ServerName RendererCameraAttributes +#define server_name RSG::camera_attributes + + FUNCRIDSPLIT(camera_attributes) + + FUNC2(camera_attributes_set_dof_blur_quality, DOFBlurQuality, bool) + FUNC1(camera_attributes_set_dof_blur_bokeh_shape, DOFBokehShape) + + FUNC8(camera_attributes_set_dof_blur, RID, bool, float, float, bool, float, float, float) + FUNC3(camera_attributes_set_exposure, RID, float, float) + FUNC6(camera_attributes_set_auto_exposure, RID, bool, float, float, float, float) + /* SCENARIO API */ #undef server_name @@ -736,7 +745,7 @@ public: FUNCRIDSPLIT(scenario) FUNC2(scenario_set_environment, RID, RID) - FUNC2(scenario_set_camera_effects, RID, RID) + FUNC2(scenario_set_camera_attributes, RID, RID) FUNC2(scenario_set_fallback_environment, RID, RID) /* INSTANCING API */ diff --git a/servers/rendering/rendering_server_globals.cpp b/servers/rendering/rendering_server_globals.cpp index 46fa49e683..ce7383a03f 100644 --- a/servers/rendering/rendering_server_globals.cpp +++ b/servers/rendering/rendering_server_globals.cpp @@ -40,6 +40,7 @@ RendererParticlesStorage *RenderingServerGlobals::particles_storage = nullptr; RendererTextureStorage *RenderingServerGlobals::texture_storage = nullptr; RendererGI *RenderingServerGlobals::gi = nullptr; RendererFog *RenderingServerGlobals::fog = nullptr; +RendererCameraAttributes *RenderingServerGlobals::camera_attributes = nullptr; RendererCanvasRender *RenderingServerGlobals::canvas_render = nullptr; RendererCompositor *RenderingServerGlobals::rasterizer = nullptr; diff --git a/servers/rendering/rendering_server_globals.h b/servers/rendering/rendering_server_globals.h index 87d69984f8..6c4ab5a26e 100644 --- a/servers/rendering/rendering_server_globals.h +++ b/servers/rendering/rendering_server_globals.h @@ -36,6 +36,7 @@ #include "servers/rendering/renderer_canvas_cull.h" #include "servers/rendering/renderer_canvas_render.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/storage/camera_attributes_storage.h" #include "servers/rendering/storage/light_storage.h" #include "servers/rendering/storage/material_storage.h" #include "servers/rendering/storage/mesh_storage.h" @@ -59,6 +60,7 @@ public: static RendererTextureStorage *texture_storage; static RendererGI *gi; static RendererFog *fog; + static RendererCameraAttributes *camera_attributes; static RendererCanvasRender *canvas_render; static RendererCompositor *rasterizer; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 12a3a6530e..ab5b8af794 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1239,7 +1239,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } code += ")"; if (is_screen_texture && actions.apply_luminance_multiplier) { - code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))"; + code = "(" + code + " / vec4(vec3(sc_luminance_multiplier), 1.0))"; } } break; case SL::OP_INDEX: { diff --git a/servers/rendering/storage/camera_attributes_storage.cpp b/servers/rendering/storage/camera_attributes_storage.cpp new file mode 100644 index 0000000000..570fefb9de --- /dev/null +++ b/servers/rendering/storage/camera_attributes_storage.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* camera_attributes_storage.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_attributes_storage.h" + +RendererCameraAttributes *RendererCameraAttributes::singleton = nullptr; +uint64_t RendererCameraAttributes::auto_exposure_counter = 2; + +RendererCameraAttributes::RendererCameraAttributes() { + singleton = this; +} + +RendererCameraAttributes::~RendererCameraAttributes() { + singleton = nullptr; +} + +RID RendererCameraAttributes::camera_attributes_allocate() { + return camera_attributes_owner.allocate_rid(); +} + +void RendererCameraAttributes::camera_attributes_initialize(RID p_rid) { + camera_attributes_owner.initialize_rid(p_rid, CameraAttributes()); +} + +void RendererCameraAttributes::camera_attributes_free(RID p_rid) { + camera_attributes_owner.free(p_rid); +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { + dof_blur_quality = p_quality; + dof_blur_use_jitter = p_use_jitter; +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { + dof_blur_bokeh_shape = p_shape; +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur(RID p_camera_attributes, 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) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + + cam_attributes->dof_blur_far_enabled = p_far_enable; + cam_attributes->dof_blur_far_distance = p_far_distance; + cam_attributes->dof_blur_far_transition = p_far_transition; + + cam_attributes->dof_blur_near_enabled = p_near_enable; + cam_attributes->dof_blur_near_distance = p_near_distance; + cam_attributes->dof_blur_near_transition = p_near_transition; + + cam_attributes->dof_blur_amount = p_amount; +} + +bool RendererCameraAttributes::camera_attributes_get_dof_far_enabled(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, false); + return cam_attributes->dof_blur_far_enabled; +} + +float RendererCameraAttributes::camera_attributes_get_dof_far_distance(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_far_distance; +} + +float RendererCameraAttributes::camera_attributes_get_dof_far_transition(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_far_transition; +} + +bool RendererCameraAttributes::camera_attributes_get_dof_near_enabled(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, false); + return cam_attributes->dof_blur_near_enabled; +} + +float RendererCameraAttributes::camera_attributes_get_dof_near_distance(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_near_distance; +} + +float RendererCameraAttributes::camera_attributes_get_dof_near_transition(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_near_transition; +} + +float RendererCameraAttributes::camera_attributes_get_dof_blur_amount(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_amount; +} + +void RendererCameraAttributes::camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + cam_attributes->exposure_multiplier = p_multiplier; + cam_attributes->exposure_normalization = p_exposure_normalization; +} + +float RendererCameraAttributes::camera_attributes_get_exposure_normalization_factor(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 1.0); + + return cam_attributes->exposure_multiplier * cam_attributes->exposure_normalization; +} + +void RendererCameraAttributes::camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + if (!cam_attributes->use_auto_exposure && p_enable) { + cam_attributes->auto_exposure_version = ++auto_exposure_counter; + } + cam_attributes->use_auto_exposure = p_enable; + cam_attributes->auto_exposure_min_sensitivity = p_min_sensitivity; + cam_attributes->auto_exposure_max_sensitivity = p_max_sensitivity; + cam_attributes->auto_exposure_adjust_speed = p_speed; + cam_attributes->auto_exposure_scale = p_scale; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_min_sensitivity(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_min_sensitivity; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_max_sensitivity(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_max_sensitivity; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_adjust_speed(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_adjust_speed; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_scale(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_scale; +} + +uint64_t RendererCameraAttributes::camera_attributes_get_auto_exposure_version(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0); + return cam_attributes->auto_exposure_version; +} diff --git a/servers/rendering/storage/camera_attributes_storage.h b/servers/rendering/storage/camera_attributes_storage.h new file mode 100644 index 0000000000..6c7b364b10 --- /dev/null +++ b/servers/rendering/storage/camera_attributes_storage.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* camera_attributes_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CAMERA_ATTRIBUTES_STORAGE_H +#define CAMERA_ATTRIBUTES_STORAGE_H + +#include "core/templates/rid_owner.h" +#include "servers/rendering_server.h" + +class RendererCameraAttributes { +private: + static RendererCameraAttributes *singleton; + + struct CameraAttributes { + float exposure_multiplier = 1.0; + float exposure_normalization = 1.0; + float exposure_sensitivity = 100.0; // In ISO. + + bool use_auto_exposure = false; + float auto_exposure_min_sensitivity = 50.0; + float auto_exposure_max_sensitivity = 800.0; + float auto_exposure_adjust_speed = 1.0; + float auto_exposure_scale = 1.0; + uint64_t auto_exposure_version = 0; + + bool dof_blur_far_enabled = false; + float dof_blur_far_distance = 10; + float dof_blur_far_transition = 5; + bool dof_blur_near_enabled = false; + float dof_blur_near_distance = 2; + float dof_blur_near_transition = 1; + float dof_blur_amount = 0.1; + }; + + RS::DOFBlurQuality dof_blur_quality = RS::DOF_BLUR_QUALITY_MEDIUM; + RS::DOFBokehShape dof_blur_bokeh_shape = RS::DOF_BOKEH_HEXAGON; + bool dof_blur_use_jitter = false; + static uint64_t auto_exposure_counter; + + mutable RID_Owner<CameraAttributes, true> camera_attributes_owner; + +public: + static RendererCameraAttributes *get_singleton() { return singleton; } + + RendererCameraAttributes(); + ~RendererCameraAttributes(); + + CameraAttributes *get_camera_attributes(RID p_rid) { return camera_attributes_owner.get_or_null(p_rid); }; + bool owns_camera_attributes(RID p_rid) { return camera_attributes_owner.owns(p_rid); }; + + RID camera_attributes_allocate(); + void camera_attributes_initialize(RID p_rid); + void camera_attributes_free(RID p_rid); + + void camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter); + void camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape); + + void camera_attributes_set_dof_blur(RID p_camera_attributes, 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); + bool camera_attributes_get_dof_far_enabled(RID p_camera_attributes); + float camera_attributes_get_dof_far_distance(RID p_camera_attributes); + float camera_attributes_get_dof_far_transition(RID p_camera_attributes); + bool camera_attributes_get_dof_near_enabled(RID p_camera_attributes); + float camera_attributes_get_dof_near_distance(RID p_camera_attributes); + float camera_attributes_get_dof_near_transition(RID p_camera_attributes); + float camera_attributes_get_dof_blur_amount(RID p_camera_attributes); + + _FORCE_INLINE_ bool camera_attributes_uses_dof(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + + return cam_attributes && (cam_attributes->dof_blur_near_enabled || cam_attributes->dof_blur_far_enabled) && cam_attributes->dof_blur_amount > 0.0; + } + + void camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization); + float camera_attributes_get_exposure_normalization_factor(RID p_camera_attributes); + + void camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale); + float camera_attributes_get_auto_exposure_min_sensitivity(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_max_sensitivity(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_adjust_speed(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_scale(RID p_camera_attributes); + uint64_t camera_attributes_get_auto_exposure_version(RID p_camera_attributes); + + _FORCE_INLINE_ bool camera_attributes_uses_auto_exposure(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + + return cam_attributes && cam_attributes->use_auto_exposure; + } + + _FORCE_INLINE_ RS::DOFBlurQuality camera_attributes_get_dof_blur_quality() { + return dof_blur_quality; + } + + _FORCE_INLINE_ RS::DOFBokehShape camera_attributes_get_dof_blur_bokeh_shape() { + return dof_blur_bokeh_shape; + } + + _FORCE_INLINE_ bool camera_attributes_get_dof_blur_use_jitter() { + return dof_blur_use_jitter; + } +}; + +#endif // CAMERA_ATTRIBUTES_STORAGE_H diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 1d4dc55e98..9b1842f1d9 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -30,8 +30,6 @@ #include "environment_storage.h" -uint64_t RendererEnvironmentStorage::auto_exposure_counter = 2; - RID RendererEnvironmentStorage::environment_allocate() { return environment_owner.allocate_rid(); } @@ -76,10 +74,11 @@ void RendererEnvironmentStorage::environment_set_bg_color(RID p_env, const Color env->bg_color = p_color; } -void RendererEnvironmentStorage::environment_set_bg_energy(RID p_env, float p_energy) { +void RendererEnvironmentStorage::environment_set_bg_energy(RID p_env, float p_multiplier, float p_intensity) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); - env->bg_energy = p_energy; + env->bg_energy_multiplier = p_multiplier; + env->bg_intensity = p_intensity; } void RendererEnvironmentStorage::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { @@ -128,10 +127,16 @@ Color RendererEnvironmentStorage::environment_get_bg_color(RID p_env) const { return env->bg_color; } -float RendererEnvironmentStorage::environment_get_bg_energy(RID p_env) const { +float RendererEnvironmentStorage::environment_get_bg_energy_multiplier(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_COND_V(!env, 1.0); + return env->bg_energy_multiplier; +} + +float RendererEnvironmentStorage::environment_get_bg_intensity(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND_V(!env, 1.0); - return env->bg_energy; + return env->bg_intensity; } int RendererEnvironmentStorage::environment_get_canvas_max_layer(RID p_env) const { @@ -172,20 +177,12 @@ RS::EnvironmentReflectionSource RendererEnvironmentStorage::environment_get_refl // Tonemap -void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { +void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); env->exposure = p_exposure; env->tone_mapper = p_tone_mapper; - if (!env->auto_exposure && p_auto_exposure) { - env->auto_exposure_version = ++auto_exposure_counter; - } - env->auto_exposure = p_auto_exposure; env->white = p_white; - env->min_luminance = p_min_luminance; - env->max_luminance = p_max_luminance; - env->auto_exp_speed = p_auto_exp_speed; - env->auto_exp_scale = p_auto_exp_scale; } RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const { @@ -206,45 +203,9 @@ float RendererEnvironmentStorage::environment_get_white(RID p_env) const { return env->white; } -bool RendererEnvironmentStorage::environment_get_auto_exposure(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, false); - return env->auto_exposure; -} - -float RendererEnvironmentStorage::environment_get_min_luminance(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.2); - return env->min_luminance; -} - -float RendererEnvironmentStorage::environment_get_max_luminance(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 8.0); - return env->max_luminance; -} - -float RendererEnvironmentStorage::environment_get_auto_exp_speed(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.2); - return env->auto_exp_speed; -} - -float RendererEnvironmentStorage::environment_get_auto_exp_scale(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.5); - return env->auto_exp_scale; -} - -uint64_t RendererEnvironmentStorage::environment_get_auto_exposure_version(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0); - return env->auto_exposure_version; -} - // Fog -void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective) { +void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective, float p_sky_affect) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); env->fog_enabled = p_enable; @@ -255,6 +216,7 @@ void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, c env->fog_height = p_height; env->fog_height_density = p_height_density; env->fog_aerial_perspective = p_fog_aerial_perspective; + env->fog_sky_affect = p_sky_affect; } bool RendererEnvironmentStorage::environment_get_fog_enabled(RID p_env) const { @@ -305,9 +267,15 @@ float RendererEnvironmentStorage::environment_get_fog_aerial_perspective(RID p_e return env->fog_aerial_perspective; } +float RendererEnvironmentStorage::environment_get_fog_sky_affect(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_COND_V(!env, 0.0); + return env->fog_sky_affect; +} + // Volumetric Fog -void RendererEnvironmentStorage::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) { +void RendererEnvironmentStorage::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); env->volumetric_fog_enabled = p_enable; @@ -322,6 +290,7 @@ void RendererEnvironmentStorage::environment_set_volumetric_fog(RID p_env, bool env->volumetric_fog_temporal_reprojection = p_temporal_reprojection; env->volumetric_fog_temporal_reprojection_amount = p_temporal_reprojection_amount; env->volumetric_fog_ambient_inject = p_ambient_inject; + env->volumetric_fog_sky_affect = p_sky_affect; } bool RendererEnvironmentStorage::environment_get_volumetric_fog_enabled(RID p_env) const { @@ -378,6 +347,12 @@ float RendererEnvironmentStorage::environment_get_volumetric_fog_gi_inject(RID p return env->volumetric_fog_gi_inject; } +float RendererEnvironmentStorage::environment_get_volumetric_fog_sky_affect(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_COND_V(!env, 0.0); + return env->volumetric_fog_sky_affect; +} + bool RendererEnvironmentStorage::environment_get_volumetric_fog_temporal_reprojection(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND_V(!env, true); diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h index 29bba86930..17bde94902 100644 --- a/servers/rendering/storage/environment_storage.h +++ b/servers/rendering/storage/environment_storage.h @@ -46,7 +46,8 @@ private: float sky_custom_fov = 0.0; Basis sky_orientation; Color bg_color; - float bg_energy = 1.0; + float bg_energy_multiplier = 1.0; + float bg_intensity = 1.0; // Measured in nits or candela/m^2. Default to 1.0 so this doesn't impact rendering when Physical Light Units disabled. int canvas_max_layer = 0; RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG; Color ambient_light; @@ -58,12 +59,6 @@ private: RS::EnvironmentToneMapper tone_mapper; float exposure = 1.0; float white = 1.0; - bool auto_exposure = false; - float min_luminance = 0.2; - float max_luminance = 8.0; - float auto_exp_speed = 0.2; - float auto_exp_scale = 0.5; - uint64_t auto_exposure_version = 0; // Fog bool fog_enabled = false; @@ -71,6 +66,7 @@ private: float fog_light_energy = 1.0; float fog_sun_scatter = 0.0; float fog_density = 0.01; + float fog_sky_affect = 1.0; float fog_height = 0.0; float fog_height_density = 0.0; //can be negative to invert effect float fog_aerial_perspective = 0.0; @@ -86,6 +82,7 @@ private: float volumetric_fog_detail_spread = 2.0; float volumetric_fog_gi_inject = 1.0; float volumetric_fog_ambient_inject = 0.0; + float volumetric_fog_sky_affect = 1.0; bool volumetric_fog_temporal_reprojection = true; float volumetric_fog_temporal_reprojection_amount = 0.9; @@ -149,8 +146,6 @@ private: RID color_correction = RID(); }; - static uint64_t auto_exposure_counter; - mutable RID_Owner<Environment, true> environment_owner; public: @@ -168,7 +163,7 @@ public: void environment_set_sky_custom_fov(RID p_env, float p_scale); void environment_set_sky_orientation(RID p_env, const Basis &p_orientation); void environment_set_bg_color(RID p_env, const Color &p_color); - void environment_set_bg_energy(RID p_env, float p_energy); + void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value); void environment_set_canvas_max_layer(RID p_env, int p_max_layer); void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG); // FIXME: Disabled during Vulkan refactoring, should be ported. @@ -181,7 +176,8 @@ public: float environment_get_sky_custom_fov(RID p_env) const; Basis environment_get_sky_orientation(RID p_env) const; Color environment_get_bg_color(RID p_env) const; - float environment_get_bg_energy(RID p_env) const; + float environment_get_bg_energy_multiplier(RID p_env) const; + float environment_get_bg_intensity(RID p_env) const; int environment_get_canvas_max_layer(RID p_env) const; RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const; Color environment_get_ambient_light(RID p_env) const; @@ -190,30 +186,25 @@ public: RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const; // Tonemap - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white); RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; - bool environment_get_auto_exposure(RID p_env) const; - float environment_get_min_luminance(RID p_env) const; - float environment_get_max_luminance(RID p_env) const; - float environment_get_auto_exp_speed(RID p_env) const; - float environment_get_auto_exp_scale(RID p_env) const; - uint64_t environment_get_auto_exposure_version(RID p_env) const; // Fog - void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective); + void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect); bool environment_get_fog_enabled(RID p_env) const; Color environment_get_fog_light_color(RID p_env) const; float environment_get_fog_light_energy(RID p_env) const; float environment_get_fog_sun_scatter(RID p_env) const; float environment_get_fog_density(RID p_env) const; + float environment_get_fog_sky_affect(RID p_env) const; float environment_get_fog_height(RID p_env) const; float environment_get_fog_height_density(RID p_env) const; float environment_get_fog_aerial_perspective(RID p_env) const; // Volumetric Fog - void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject); + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect); bool environment_get_volumetric_fog_enabled(RID p_env) const; float environment_get_volumetric_fog_density(RID p_env) const; Color environment_get_volumetric_fog_scattering(RID p_env) const; @@ -223,6 +214,7 @@ public: float environment_get_volumetric_fog_length(RID p_env) const; float environment_get_volumetric_fog_detail_spread(RID p_env) const; float environment_get_volumetric_fog_gi_inject(RID p_env) const; + float environment_get_volumetric_fog_sky_affect(RID p_env) const; bool environment_get_volumetric_fog_temporal_reprojection(RID p_env) const; float environment_get_volumetric_fog_temporal_reprojection_amount(RID p_env) const; float environment_get_volumetric_fog_ambient_inject(RID p_env) const; diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index 087ea1a025..b04bc671ee 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -124,6 +124,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) = 0; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; diff --git a/servers/rendering/storage/render_scene_buffers.cpp b/servers/rendering/storage/render_scene_buffers.cpp new file mode 100644 index 0000000000..104700090f --- /dev/null +++ b/servers/rendering/storage/render_scene_buffers.cpp @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* render_scene_buffers.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "render_scene_buffers.h" + +void RenderSceneBuffers::_bind_methods() { + ClassDB::bind_method(D_METHOD("configure", "render_target", "internal_size", "target_size", "fsr_sharpness", "texture_mipmap_bias", "msaa", "screen_space_aa", "use_taa", "use_debanding", "view_count"), &RenderSceneBuffers::configure); +} + +void RenderSceneBuffers::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { + GDVIRTUAL_CALL(_configure, p_render_target, p_internal_size, p_target_size, p_fsr_sharpness, p_texture_mipmap_bias, p_msaa, p_screen_space_aa, p_use_taa, p_use_debanding, p_view_count); +}; + +void RenderSceneBuffers::set_fsr_sharpness(float p_fsr_sharpness) { + GDVIRTUAL_CALL(_set_fsr_sharpness, p_fsr_sharpness); +} + +void RenderSceneBuffers::set_texture_mipmap_bias(float p_texture_mipmap_bias) { + GDVIRTUAL_CALL(_set_texture_mipmap_bias, p_texture_mipmap_bias); +} + +void RenderSceneBuffers::set_use_debanding(bool p_use_debanding) { + GDVIRTUAL_CALL(_set_use_debanding, p_use_debanding); +} diff --git a/servers/rendering/storage/render_scene_buffers.h b/servers/rendering/storage/render_scene_buffers.h new file mode 100644 index 0000000000..e28e3201ae --- /dev/null +++ b/servers/rendering/storage/render_scene_buffers.h @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* render_scene_buffers.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_SCENE_BUFFERS_H +#define RENDER_SCENE_BUFFERS_H + +#include "core/object/ref_counted.h" +#include "servers/rendering_server.h" + +class RenderSceneBuffers : public RefCounted { + GDCLASS(RenderSceneBuffers, RefCounted); + +protected: + static void _bind_methods(); + + GDVIRTUAL10(_configure, RID, Size2i, Size2i, float, float, RS::ViewportMSAA, RenderingServer::ViewportScreenSpaceAA, bool, bool, uint32_t) + GDVIRTUAL1(_set_fsr_sharpness, float) + GDVIRTUAL1(_set_texture_mipmap_bias, float) + GDVIRTUAL1(_set_use_debanding, bool) + +public: + RenderSceneBuffers(){}; + virtual ~RenderSceneBuffers(){}; + + virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count); + + // for those settings that are unlikely to require buffers to be recreated, we'll add setters + virtual void set_fsr_sharpness(float p_fsr_sharpness); + virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias); + virtual void set_use_debanding(bool p_use_debanding); +}; + +#endif // RENDER_SCENE_BUFFERS_H diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 22de47a764..1328d01b85 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2016,6 +2016,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("voxel_gi_set_dynamic_range", "voxel_gi", "range"), &RenderingServer::voxel_gi_set_dynamic_range); ClassDB::bind_method(D_METHOD("voxel_gi_set_propagation", "voxel_gi", "amount"), &RenderingServer::voxel_gi_set_propagation); ClassDB::bind_method(D_METHOD("voxel_gi_set_energy", "voxel_gi", "energy"), &RenderingServer::voxel_gi_set_energy); + ClassDB::bind_method(D_METHOD("voxel_gi_set_baked_exposure_normalization", "voxel_gi", "baked_exposure"), &RenderingServer::voxel_gi_set_baked_exposure_normalization); ClassDB::bind_method(D_METHOD("voxel_gi_set_bias", "voxel_gi", "bias"), &RenderingServer::voxel_gi_set_bias); ClassDB::bind_method(D_METHOD("voxel_gi_set_normal_bias", "voxel_gi", "bias"), &RenderingServer::voxel_gi_set_normal_bias); ClassDB::bind_method(D_METHOD("voxel_gi_set_interior", "voxel_gi", "enable"), &RenderingServer::voxel_gi_set_interior); @@ -2037,6 +2038,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_sh", "lightmap"), &RenderingServer::lightmap_get_probe_capture_sh); ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_tetrahedra", "lightmap"), &RenderingServer::lightmap_get_probe_capture_tetrahedra); ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_bsp_tree", "lightmap"), &RenderingServer::lightmap_get_probe_capture_bsp_tree); + ClassDB::bind_method(D_METHOD("lightmap_set_baked_exposure_normalization", "lightmap", "baked_exposure"), &RenderingServer::lightmap_set_baked_exposure_normalization); ClassDB::bind_method(D_METHOD("lightmap_set_probe_capture_update_speed", "speed"), &RenderingServer::lightmap_set_probe_capture_update_speed); @@ -2161,7 +2163,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &RenderingServer::camera_set_transform); ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &RenderingServer::camera_set_cull_mask); ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &RenderingServer::camera_set_environment); - ClassDB::bind_method(D_METHOD("camera_set_camera_effects", "camera", "effects"), &RenderingServer::camera_set_camera_effects); + ClassDB::bind_method(D_METHOD("camera_set_camera_attributes", "camera", "effects"), &RenderingServer::camera_set_camera_attributes); ClassDB::bind_method(D_METHOD("camera_set_use_vertical_aspect", "camera", "enable"), &RenderingServer::camera_set_use_vertical_aspect); /* VIEWPORT */ @@ -2325,17 +2327,17 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_sky_custom_fov", "env", "scale"), &RenderingServer::environment_set_sky_custom_fov); ClassDB::bind_method(D_METHOD("environment_set_sky_orientation", "env", "orientation"), &RenderingServer::environment_set_sky_orientation); ClassDB::bind_method(D_METHOD("environment_set_bg_color", "env", "color"), &RenderingServer::environment_set_bg_color); - ClassDB::bind_method(D_METHOD("environment_set_bg_energy", "env", "energy"), &RenderingServer::environment_set_bg_energy); + ClassDB::bind_method(D_METHOD("environment_set_bg_energy", "env", "multiplier", "exposure_value"), &RenderingServer::environment_set_bg_energy); ClassDB::bind_method(D_METHOD("environment_set_canvas_max_layer", "env", "max_layer"), &RenderingServer::environment_set_canvas_max_layer); ClassDB::bind_method(D_METHOD("environment_set_ambient_light", "env", "color", "ambient", "energy", "sky_contibution", "reflection_source"), &RenderingServer::environment_set_ambient_light, DEFVAL(RS::ENV_AMBIENT_SOURCE_BG), DEFVAL(1.0), DEFVAL(0.0), DEFVAL(RS::ENV_REFLECTION_SOURCE_BG)); ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap", "glow_map_strength", "glow_map"), &RenderingServer::environment_set_glow); - ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &RenderingServer::environment_set_tonemap); + ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white"), &RenderingServer::environment_set_tonemap); ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr); ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "power", "detail", "horizon", "sharpness", "light_affect", "ao_channel_affect"), &RenderingServer::environment_set_ssao); - ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective"), &RenderingServer::environment_set_fog); + ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect"), &RenderingServer::environment_set_fog); ClassDB::bind_method(D_METHOD("environment_set_sdfgi", "env", "enable", "cascades", "min_cell_size", "y_scale", "use_occlusion", "bounce_feedback", "read_sky", "energy", "normal_bias", "probe_bias"), &RenderingServer::environment_set_sdfgi); - ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject"), &RenderingServer::environment_set_volumetric_fog); + ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject", "sky_affect"), &RenderingServer::environment_set_volumetric_fog); ClassDB::bind_method(D_METHOD("environment_glow_set_use_bicubic_upscale", "enable"), &RenderingServer::environment_glow_set_use_bicubic_upscale); ClassDB::bind_method(D_METHOD("environment_glow_set_use_high_quality", "enable"), &RenderingServer::environment_glow_set_use_high_quality); @@ -2434,13 +2436,14 @@ void RenderingServer::_bind_methods() { /* CAMERA EFFECTS */ - ClassDB::bind_method(D_METHOD("camera_effects_create"), &RenderingServer::camera_effects_create); + ClassDB::bind_method(D_METHOD("camera_attributes_create"), &RenderingServer::camera_attributes_create); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur_quality", "quality", "use_jitter"), &RenderingServer::camera_effects_set_dof_blur_quality); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur_bokeh_shape", "shape"), &RenderingServer::camera_effects_set_dof_blur_bokeh_shape); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur_quality", "quality", "use_jitter"), &RenderingServer::camera_attributes_set_dof_blur_quality); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur_bokeh_shape", "shape"), &RenderingServer::camera_attributes_set_dof_blur_bokeh_shape); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur", "camera_effects", "far_enable", "far_distance", "far_transition", "near_enable", "near_distance", "near_transition", "amount"), &RenderingServer::camera_effects_set_dof_blur); - ClassDB::bind_method(D_METHOD("camera_effects_set_custom_exposure", "camera_effects", "enable", "exposure"), &RenderingServer::camera_effects_set_custom_exposure); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur", "camera_attributes", "far_enable", "far_distance", "far_transition", "near_enable", "near_distance", "near_transition", "amount"), &RenderingServer::camera_attributes_set_dof_blur); + ClassDB::bind_method(D_METHOD("camera_attributes_set_exposure", "camera_attributes", "multiplier", "normalization"), &RenderingServer::camera_attributes_set_exposure); + ClassDB::bind_method(D_METHOD("camera_attributes_set_auto_exposure", "camera_attributes", "enable", "min_sensitivity", "max_sensitivity", "speed", "scale"), &RenderingServer::camera_attributes_set_auto_exposure); BIND_ENUM_CONSTANT(DOF_BOKEH_BOX); BIND_ENUM_CONSTANT(DOF_BOKEH_HEXAGON); @@ -2456,7 +2459,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("scenario_create"), &RenderingServer::scenario_create); ClassDB::bind_method(D_METHOD("scenario_set_environment", "scenario", "environment"), &RenderingServer::scenario_set_environment); ClassDB::bind_method(D_METHOD("scenario_set_fallback_environment", "scenario", "environment"), &RenderingServer::scenario_set_fallback_environment); - ClassDB::bind_method(D_METHOD("scenario_set_camera_effects", "scenario", "effects"), &RenderingServer::scenario_set_camera_effects); + ClassDB::bind_method(D_METHOD("scenario_set_camera_attributes", "scenario", "effects"), &RenderingServer::scenario_set_camera_attributes); /* INSTANCE */ @@ -2842,17 +2845,19 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/limits/time/time_rollover_secs", 3600); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/time/time_rollover_secs", PropertyInfo(Variant::FLOAT, "rendering/limits/time/time_rollover_secs", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); - GLOBAL_DEF("rendering/shadows/directional_shadow/size", 4096); - GLOBAL_DEF("rendering/shadows/directional_shadow/size.mobile", 2048); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_filter_quality", 2); - GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_filter_quality.mobile", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); - GLOBAL_DEF("rendering/shadows/directional_shadow/16_bits", true); - - GLOBAL_DEF("rendering/shadows/positional_shadow/soft_shadow_filter_quality", 2); - GLOBAL_DEF("rendering/shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); + GLOBAL_DEF_RST("rendering/lights_and_shadows/use_physical_light_units", false); + + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/size", 4096); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/size.mobile", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", 2); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/16_bits", true); + + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", 2); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index bd06503219..b69001fd57 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -434,6 +434,7 @@ public: LIGHT_PARAM_SHADOW_OPACITY, LIGHT_PARAM_SHADOW_BLUR, LIGHT_PARAM_TRANSMITTANCE_BIAS, + LIGHT_PARAM_INTENSITY, LIGHT_PARAM_MAX }; @@ -590,6 +591,7 @@ public: virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) = 0; virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0; virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) = 0; @@ -610,6 +612,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) = 0; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; @@ -762,7 +765,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; - virtual void camera_set_camera_effects(RID p_camera, RID p_camera_effects) = 0; + virtual void camera_set_camera_attributes(RID p_camera, RID p_camera_attributes) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; /* VIEWPORT API */ @@ -1011,7 +1014,7 @@ public: virtual void environment_set_sky_custom_fov(RID p_env, float p_scale) = 0; virtual void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) = 0; virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0; - virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0; + virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0; virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0; virtual void environment_set_ambient_light(RID p_env, const Color &p_color, EnvironmentAmbientSource p_ambient = ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, EnvironmentReflectionSource p_reflection_source = ENV_REFLECTION_SOURCE_BG) = 0; @@ -1035,7 +1038,7 @@ public: ENV_TONE_MAPPER_ACES }; - virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_grey) = 0; + virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0; virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0; @@ -1117,9 +1120,9 @@ public: virtual void environment_set_sdfgi_frames_to_update_light(EnvironmentSDFGIFramesToUpdateLight p_update) = 0; - virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) = 0; + virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) = 0; - virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) = 0; + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) = 0; virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; @@ -1139,7 +1142,7 @@ public: /* CAMERA EFFECTS */ - virtual RID camera_effects_create() = 0; + virtual RID camera_attributes_create() = 0; enum DOFBlurQuality { DOF_BLUR_QUALITY_VERY_LOW, @@ -1148,7 +1151,7 @@ public: DOF_BLUR_QUALITY_HIGH, }; - virtual void camera_effects_set_dof_blur_quality(DOFBlurQuality p_quality, bool p_use_jitter) = 0; + virtual void camera_attributes_set_dof_blur_quality(DOFBlurQuality p_quality, bool p_use_jitter) = 0; enum DOFBokehShape { DOF_BOKEH_BOX, @@ -1156,10 +1159,11 @@ public: DOF_BOKEH_CIRCLE }; - virtual void camera_effects_set_dof_blur_bokeh_shape(DOFBokehShape p_shape) = 0; + virtual void camera_attributes_set_dof_blur_bokeh_shape(DOFBokehShape p_shape) = 0; - 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) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; + virtual void camera_attributes_set_dof_blur(RID p_camera_attributes, 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) = 0; + virtual void camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization) = 0; + virtual void camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale) = 0; /* SCENARIO API */ @@ -1167,7 +1171,7 @@ public: virtual void scenario_set_environment(RID p_scenario, RID p_environment) = 0; virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment) = 0; - virtual void scenario_set_camera_effects(RID p_scenario, RID p_camera_effects) = 0; + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_camera_attributes) = 0; /* INSTANCING API */ diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 393160fe9e..660247839c 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -498,6 +498,7 @@ void TextServer::_bind_methods() { BIND_BITFIELD_FLAG(BREAK_WORD_BOUND); BIND_BITFIELD_FLAG(BREAK_GRAPHEME_BOUND); BIND_BITFIELD_FLAG(BREAK_ADAPTIVE); + BIND_BITFIELD_FLAG(BREAK_TRIM_EDGE_SPACES); /* VisibleCharactersBehavior */ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING); @@ -680,24 +681,44 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped real_t width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; + int word_count = 0; int chunk = 0; + bool trim_next = false; int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width[chunk] > 0) && (width + l_gl[i].advance > p_width[chunk]) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = true; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; + word_count = 0; chunk++; if (chunk >= p_width.size()) { chunk = 0; @@ -709,9 +730,24 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = false; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; chunk = 0; @@ -724,9 +760,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped if (p_break_flags.has_flag(BREAK_WORD_BOUND)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_safe_break = i; + word_count++; } } - if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND)) { + if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND) && word_count == 0) { last_safe_break = i; } } @@ -734,8 +771,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + lines.push_back(l_gl[start_pos].start); + } else { + lines.push_back(line_start); + } lines.push_back(range.y); } } else { @@ -754,21 +800,39 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do double width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; int word_count = 0; + bool trim_next = false; int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width > 0) && (width + l_gl[i].advance * l_gl[i].repeat > p_width) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = true; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; @@ -777,9 +841,24 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do } if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + trim_next = false; + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; continue; @@ -802,8 +881,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + lines.push_back(l_gl[start_pos].start); + } else { + lines.push_back(line_start); + } lines.push_back(range.y); } } else { diff --git a/servers/text_server.h b/servers/text_server.h index 9304771d1b..e8945dd807 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -103,6 +103,7 @@ public: BREAK_WORD_BOUND = 1 << 1, BREAK_GRAPHEME_BOUND = 1 << 2, BREAK_ADAPTIVE = 1 << 3, + BREAK_TRIM_EDGE_SPACES = 1 << 4, }; enum OverrunBehavior { diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index 7395cd5ad4..7024a25528 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -358,9 +358,5 @@ RID XRInterfaceExtension::get_render_target_texture(RID p_render_target) { RID XRInterfaceExtension::get_render_target_depth(RID p_render_target) { // TODO implement this, the problem is that our depth texture isn't part of our render target as it is used for 3D rendering only // but we don't have access to our render buffers from here.... - RendererSceneRenderRD * rd_scene = ?????; - ERR_FAIL_NULL_V_MSG(rd_scene, RID(), "Renderer scene render not setup"); - - return rd_scene->render_buffers_get_depth_texture(????????????); } */ diff --git a/tests/core/math/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h index 99a4ef2d46..23bbf1e183 100644 --- a/tests/core/math/test_geometry_3d.h +++ b/tests/core/math/test_geometry_3d.h @@ -63,7 +63,7 @@ TEST_CASE("[Geometry3D] Closest Distance Between Segments") { p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want(p_want){}; }; Vector<Case> tt; - tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 0.0f)); + tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 2.0f)); for (int i = 0; i < tt.size(); ++i) { Case current_case = tt[i]; float out = Geometry3D::get_closest_distance_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4); diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h index 729035919d..c98434d42c 100644 --- a/tests/core/variant/test_dictionary.h +++ b/tests/core/variant/test_dictionary.h @@ -500,6 +500,24 @@ TEST_CASE("[Dictionary] Recursive self comparison") { d2.clear(); } +TEST_CASE("[Dictionary] Order and find") { + Dictionary d; + d[4] = "four"; + d[8] = "eight"; + d[12] = "twelve"; + d["4"] = "four"; + + Array keys; + keys.append(4); + keys.append(8); + keys.append(12); + keys.append("4"); + + CHECK_EQ(d.keys(), keys); + CHECK_EQ(d.find_key("four"), Variant(4)); + CHECK_EQ(d.find_key("does not exist"), Variant()); +} + } // namespace TestDictionary #endif // TEST_DICTIONARY_H diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h new file mode 100644 index 0000000000..53afdc38f7 --- /dev/null +++ b/tests/scene/test_bit_map.h @@ -0,0 +1,445 @@ +/*************************************************************************/ +/* test_bit_map.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_BIT_MAP_H +#define TEST_BIT_MAP_H + +#include "core/os/memory.h" +#include "scene/resources/bit_map.h" +#include "tests/test_macros.h" + +namespace TestBitmap { + +void reset_bit_map(BitMap &p_bm) { + Size2i size = p_bm.get_size(); + p_bm.set_bit_rect(Rect2i(0, 0, size.width, size.height), false); +} + +TEST_CASE("[BitMap] Create bit map") { + Size2i dim{ 256, 512 }; + BitMap bit_map{}; + bit_map.create(dim); + CHECK(bit_map.get_size() == Size2i(256, 512)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "This will go through the entire bitmask inside of bitmap, thus hopefully checking if the bitmask was correctly set up."); + + dim = Size2i(0, 256); + bit_map.create(dim); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 512), "We should still have the same dimensions as before, because the new dimension is invalid."); + + dim = Size2i(512, 0); + bit_map.create(dim); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 512), "We should still have the same dimensions as before, because the new dimension is invalid."); + + dim = Size2i(46341, 46341); + bit_map.create(dim); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 512), "We should still have the same dimensions as before, because the new dimension is too large (46341*46341=2147488281)."); +} + +TEST_CASE("[BitMap] Create bit map from image alpha") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + bit_map.create(dim); + + const Ref<Image> null_img = nullptr; + bit_map.create_from_image_alpha(null_img); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from a nullptr should fail."); + + Ref<Image> empty_img; + empty_img.instantiate(); + bit_map.create_from_image_alpha(empty_img); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from an empty image should fail."); + + Ref<Image> wrong_format_img; + wrong_format_img.instantiate(); + wrong_format_img->create(3, 3, false, Image::Format::FORMAT_DXT1); + bit_map.create_from_image_alpha(wrong_format_img); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because converting from a compressed image should fail."); + + Ref<Image> img; + img.instantiate(); + img->create(3, 3, false, Image::Format::FORMAT_RGBA8); + img->set_pixel(0, 0, Color(0, 0, 0, 0)); + img->set_pixel(0, 1, Color(0, 0, 0, 0.09f)); + img->set_pixel(0, 2, Color(0, 0, 0, 0.25f)); + img->set_pixel(1, 0, Color(0, 0, 0, 0.5f)); + img->set_pixel(1, 1, Color(0, 0, 0, 0.75f)); + img->set_pixel(1, 2, Color(0, 0, 0, 0.99f)); + img->set_pixel(2, 0, Color(0, 0, 0, 1.f)); + + // Check different threshold values. + bit_map.create_from_image_alpha(img); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 5, "There are 5 values in the image that are smaller than the default threshold of 0.1."); + + bit_map.create_from_image_alpha(img, 0.08f); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 6, "There are 6 values in the image that are smaller than the threshold of 0.08."); + + bit_map.create_from_image_alpha(img, 1); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "There are no values in the image that are smaller than the threshold of 1, there is one value equal to 1, but we check for inequality only."); +} + +TEST_CASE("[BitMap] Set bit") { + Size2i dim{ 256, 256 }; + BitMap bit_map{}; + + // Setting a point before a bit map is created should not crash, because there are checks to see if we are out of bounds. + bit_map.set_bitv(Point2i(128, 128), true); + + bit_map.create(dim); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "All values should be initialized to false."); + bit_map.set_bitv(Point2i(128, 128), true); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 1, "One bit should be set to true."); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 128)) == true, "The bit at (128,128) should be set to true"); + + bit_map.set_bitv(Point2i(128, 128), false); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "The bit should now be set to false again"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 128)) == false, "The bit at (128,128) should now be set to false again"); + + bit_map.create(dim); + bit_map.set_bitv(Point2i(512, 512), true); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Nothing should change as we were trying to edit a bit outside of the correct range."); +} + +TEST_CASE("[BitMap] Get bit") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 128)) == false, "Trying to access a bit outside of the BitMap's range should always return false"); + + bit_map.create(dim); + CHECK(bit_map.get_bitv(Point2i(128, 128)) == false); + + bit_map.set_bit_rect(Rect2i(-1, -1, 257, 257), true); + + // Checking that range is [0, 256). + CHECK(bit_map.get_bitv(Point2i(-1, 0)) == false); + CHECK(bit_map.get_bitv(Point2i(0, 0)) == true); + CHECK(bit_map.get_bitv(Point2i(128, 128)) == true); + CHECK(bit_map.get_bitv(Point2i(255, 255)) == true); + CHECK(bit_map.get_bitv(Point2i(256, 256)) == false); + CHECK(bit_map.get_bitv(Point2i(257, 257)) == false); +} + +TEST_CASE("[BitMap] Set bit rect") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + + // Although we have not setup the BitMap yet, this should not crash because we get an empty intersection inside of the method. + bit_map.set_bit_rect(Rect2i{ 0, 0, 128, 128 }, true); + + bit_map.create(dim); + CHECK(bit_map.get_true_bit_count() == 0); + + bit_map.set_bit_rect(Rect2i{ 0, 0, 256, 256 }, true); + CHECK(bit_map.get_true_bit_count() == 65536); + + reset_bit_map(bit_map); + + // Checking out of bounds handling. + bit_map.set_bit_rect(Rect2i{ 128, 128, 256, 256 }, true); + CHECK(bit_map.get_true_bit_count() == 16384); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i{ -128, -128, 256, 256 }, true); + CHECK(bit_map.get_true_bit_count() == 16384); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i{ -128, -128, 512, 512 }, true); + CHECK(bit_map.get_true_bit_count() == 65536); +} + +TEST_CASE("[BitMap] Get true bit count") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + + CHECK(bit_map.get_true_bit_count() == 0); + + bit_map.create(dim); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Unitialized bit map should have no true bits"); + bit_map.set_bit_rect(Rect2i{ 0, 0, 256, 256 }, true); + CHECK(bit_map.get_true_bit_count() == 65536); + bit_map.set_bitv(Point2i{ 0, 0 }, false); + CHECK(bit_map.get_true_bit_count() == 65535); + bit_map.set_bit_rect(Rect2i{ 0, 0, 256, 256 }, false); + CHECK(bit_map.get_true_bit_count() == 0); +} + +TEST_CASE("[BitMap] Get size") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + + CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Unitialized bit map should have a size of 0x0"); + + bit_map.create(dim); + CHECK(bit_map.get_size() == Size2i(256, 256)); + + bit_map.create(Size2i(-1, 0)); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Invalid size should not be accepted by create"); + + bit_map.create(Size2i(256, 128)); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 128), "Bitmap should have updated size"); +} + +TEST_CASE("[BitMap] Resize") { + const Size2i dim{ 128, 128 }; + BitMap bit_map{}; + + bit_map.resize(dim); + CHECK(bit_map.get_size() == dim); + + bit_map.create(dim); + bit_map.set_bit_rect(Rect2i(0, 0, 10, 10), true); + bit_map.set_bit_rect(Rect2i(118, 118, 10, 10), true); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 200, "There should be 100 bits in the top left corner, and 100 bits in the bottom right corner"); + bit_map.resize(Size2i(64, 64)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 50, "There should be 25 bits in the top left corner, and 25 bits in the bottom right corner"); + + bit_map.create(dim); + bit_map.resize(Size2i(-1, 128)); + CHECK_MESSAGE(bit_map.get_size() == Size2i(128, 128), "When an invalid size is given the bit map will keep its size"); + + bit_map.create(dim); + bit_map.set_bit_rect(Rect2i(0, 0, 10, 10), true); + bit_map.set_bit_rect(Rect2i(118, 118, 10, 10), true); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 200, "There should be 100 bits in the top left corner, and 100 bits in the bottom right corner"); + bit_map.resize(Size2i(256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 800, "There should still be 100 bits in the bottom right corner, and all new bits should be initialized to false"); + CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "The bitmap should now be 256x256"); +} + +TEST_CASE("[BitMap] Grow and shrink mask") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialised bit map. + CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Size should still be equal to 0x0"); + + bit_map.create(dim); + + bit_map.set_bit_rect(Rect2i(96, 96, 64, 64), true); + + CHECK_MESSAGE(bit_map.get_true_bit_count() == 4096, "Creating a square of 64x64 should be 4096 bits"); + bit_map.grow_mask(0, Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 4096, "Growing with size of 0 should not change any bits"); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i(96, 96, 64, 64), true); + + CHECK_MESSAGE(bit_map.get_bitv(Point2i(95, 128)) == false, "Bits just outside of the square should not be set"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(160, 128)) == false, "Bits just outside of the square should not be set"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 95)) == false, "Bits just outside of the square should not be set"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 160)) == false, "Bits just outside of the square should not be set"); + bit_map.grow_mask(1, Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 4352, "We should have 4*64 (perimeter of square) more bits set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(95, 128)) == true, "Bits that were just outside of the square should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(160, 128)) == true, "Bits that were just outside of the square should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 95)) == true, "Bits that were just outside of the square should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 160)) == true, "Bits that were just outside of the square should now be set to true"); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i(127, 127, 1, 1), true); + + CHECK(bit_map.get_true_bit_count() == 1); + bit_map.grow_mask(32, Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 3209, "Creates a circle around the initial bit with a radius of 32 bits. Any bit that has a distance within this radius will be set to true"); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i(127, 127, 1, 1), true); + for (int i = 0; i < 32; i++) { + bit_map.grow_mask(1, Rect2i(0, 0, 256, 256)); + } + CHECK_MESSAGE(bit_map.get_true_bit_count() == 2113, "Creates a diamond around the initial bit with diagonals that are 65 bits long."); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i(123, 123, 10, 10), true); + + CHECK(bit_map.get_true_bit_count() == 100); + bit_map.grow_mask(-11, Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Shrinking by more than the width of the square should totally remove it."); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(96, 96, 64, 64), true); + + CHECK_MESSAGE(bit_map.get_bitv(Point2i(96, 129)) == true, "Bits on the edge of the square should be true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(159, 129)) == true, "Bits on the edge of the square should be true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(129, 96)) == true, "Bits on the edge of the square should be true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(129, 159)) == true, "Bits on the edge of the square should be true"); + bit_map.grow_mask(-1, Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 3844, "Shrinking by 1 should set 4*63=252 bits to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(96, 129)) == false, "Bits that were on the edge of the square should now be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(159, 129)) == false, "Bits that were on the edge of the square should now be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(129, 96)) == false, "Bits that were on the edge of the square should now be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(129, 159)) == false, "Bits that were on the edge of the square should now be set to false"); + + reset_bit_map(bit_map); + + bit_map.set_bit_rect(Rect2i(125, 125, 1, 6), true); + bit_map.set_bit_rect(Rect2i(130, 125, 1, 6), true); + bit_map.set_bit_rect(Rect2i(125, 130, 6, 1), true); + + CHECK(bit_map.get_true_bit_count() == 16); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(125, 131)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(131, 131)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(125, 124)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(130, 124)) == false, "Bits that are on the edge of the shape should be set to false"); + bit_map.grow_mask(1, Rect2i(0, 0, 256, 256)); + CHECK(bit_map.get_true_bit_count() == 48); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(125, 131)) == true, "Bits that were on the edge of the shape should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(131, 130)) == true, "Bits that were on the edge of the shape should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(125, 124)) == true, "Bits that were on the edge of the shape should now be set to true"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(130, 124)) == true, "Bits that were on the edge of the shape should now be set to true"); + + CHECK_MESSAGE(bit_map.get_bitv(Point2i(124, 124)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(126, 124)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(124, 131)) == false, "Bits that are on the edge of the shape should be set to false"); + CHECK_MESSAGE(bit_map.get_bitv(Point2i(131, 131)) == false, "Bits that are on the edge of the shape should be set to false"); +} + +TEST_CASE("[BitMap] Blit") { + Point2i blit_pos{ 128, 128 }; + Point2i bit_map_size{ 256, 256 }; + Point2i blit_size{ 32, 32 }; + + BitMap bit_map{}; + Ref<BitMap> blit_bit_map{}; + + // Testing null reference to blit bit map. + bit_map.blit(blit_pos, blit_bit_map); + + blit_bit_map.instantiate(); + + // Testing if uninitialised blit bit map and uninitialised bit map does not crash + bit_map.blit(blit_pos, blit_bit_map); + + // Testing if uninitialised bit map does not crash + blit_bit_map->create(blit_size); + bit_map.blit(blit_pos, blit_bit_map); + + // Testing if uninitialised bit map does not crash + blit_bit_map.unref(); + blit_bit_map.instantiate(); + CHECK_MESSAGE(blit_bit_map->get_size() == Point2i(0, 0), "Size should be cleared by unref and instance calls."); + bit_map.create(bit_map_size); + bit_map.blit(Point2i(128, 128), blit_bit_map); + + // Testing if both initialised does not crash. + blit_bit_map->create(blit_size); + bit_map.blit(blit_pos, blit_bit_map); + + bit_map.set_bit_rect(Rect2i{ 127, 127, 3, 3 }, true); + CHECK(bit_map.get_true_bit_count() == 9); + bit_map.blit(Point2i(112, 112), blit_bit_map); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 9, "No bits should have been changed, as the blit bit map only contains falses"); + + bit_map.create(bit_map_size); + blit_bit_map->create(blit_size); + blit_bit_map->set_bit_rect(Rect2i(15, 15, 3, 3), true); + CHECK(blit_bit_map->get_true_bit_count() == 9); + + CHECK(bit_map.get_true_bit_count() == 0); + bit_map.blit(Point2i(112, 112), blit_bit_map); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 9, "All true bits should have been moved to the bit map"); + for (int x = 127; x < 129; ++x) { + for (int y = 127; y < 129; ++y) { + CHECK_MESSAGE(bit_map.get_bitv(Point2i(x, y)) == true, "All true bits should have been moved to the bit map"); + } + } +} + +TEST_CASE("[BitMap] Convert to image") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + Ref<Image> img; + + img = bit_map.convert_to_image(); + CHECK_MESSAGE(img.is_valid(), "We should receive a valid Image Object even if BitMap is not created yet"); + CHECK_MESSAGE(img->get_format() == Image::FORMAT_L8, "We should receive a valid Image Object even if BitMap is not created yet"); + CHECK_MESSAGE(img->get_size() == (Size2i(0, 0)), "Image should have no width or height, because BitMap has not yet been created"); + + bit_map.create(dim); + img = bit_map.convert_to_image(); + CHECK_MESSAGE(img->get_size() == dim, "Image should have the same dimensions as the BitMap"); + CHECK_MESSAGE(img->get_pixel(0, 0).is_equal_approx(Color(0, 0, 0)), "BitMap is intialized to all 0's, so Image should be all black"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(0, 0, 128, 128), true); + img = bit_map.convert_to_image(); + CHECK_MESSAGE(img->get_pixel(0, 0).is_equal_approx(Color(1, 1, 1)), "BitMap's top-left quadrant is all 1's, so Image should be white"); + CHECK_MESSAGE(img->get_pixel(256, 256).is_equal_approx(Color(0, 0, 0)), "All other quadrants were 0's, so these should be black"); +} + +TEST_CASE("[BitMap] Clip to polygon") { + const Size2i dim{ 256, 256 }; + BitMap bit_map{}; + Vector<Vector<Vector2>> polygons; + + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + CHECK_MESSAGE(polygons.size() == 0, "We should have no polygons, because the BitMap was not initialized"); + + bit_map.create(dim); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + CHECK_MESSAGE(polygons.size() == 0, "We should have no polygons, because the BitMap was all 0's"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(0, 0, 64, 64), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); + CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(0, 0, 32, 32), true); + bit_map.set_bit_rect(Rect2i(64, 64, 32, 32), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + CHECK_MESSAGE(polygons.size() == 2, "We should have exactly 2 polygons"); + CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[1].size() == 4, "The polygon should have exactly 4 points"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(124, 112, 8, 32), true); + bit_map.set_bit_rect(Rect2i(112, 124, 32, 8), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); + CHECK_MESSAGE(polygons[0].size() == 12, "The polygon should have exactly 12 points"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(124, 112, 8, 32), true); + bit_map.set_bit_rect(Rect2i(112, 124, 32, 8), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); + CHECK_MESSAGE(polygons[0].size() == 6, "The polygon should have exactly 6 points"); +} + +} // namespace TestBitmap + +#endif // TEST_BIT_MAP_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index b7d9e297bf..7712189e45 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -85,6 +85,7 @@ #include "tests/core/variant/test_variant.h" #include "tests/scene/test_animation.h" #include "tests/scene/test_audio_stream_wav.h" +#include "tests/scene/test_bit_map.h" #include "tests/scene/test_code_edit.h" #include "tests/scene/test_curve.h" #include "tests/scene/test_gradient.h" |