From 3a53ae5d9fe3d8b3dcc686c6a32510e0fe53b19e Mon Sep 17 00:00:00 2001 From: jfons Date: Sun, 9 May 2021 18:23:20 +0200 Subject: [PATCH] Implement visibility range and dependencies. This commit adds the following properties to GeometryInstance3D: `visibility_range_begin`, `visibility_range_begin_margin`, `visibility_range_end`, `visibility_range_end_margin`. Together they define a range in which the GeometryInstance3D will be visible from the camera, taking hysteresis into account for state changes. A begin or end value of 0 will be ignored, so the visibility range can be open-ended in both directions. This commit also adds the `visibility_parent` property to 'Node3D'. Which defines the visibility parents of the node and its subtree (until another parent is defined). Visual instances with a visibility parent will only be visible when the parent, and all of its ancestors recursively, are hidden because they are closer to the camera than their respective `visibility_range_begin` thresholds. Combining visibility ranges and visibility parents users can set-up a quick HLOD system that shows high detail meshes when close (i.e buildings, trees) and merged low detail meshes for far away groups (i.e. cities, woods). --- core/templates/bin_sorted_array.h | 181 +++++ doc/classes/GeometryInstance3D.xml | 28 +- doc/classes/Node3D.xml | 3 + doc/classes/RenderingServer.xml | 56 +- scene/3d/node_3d.cpp | 52 ++ scene/3d/node_3d.h | 10 + scene/3d/visual_instance_3d.cpp | 66 +- scene/3d/visual_instance_3d.h | 30 +- servers/rendering/renderer_scene.h | 6 +- servers/rendering/renderer_scene_cull.cpp | 685 ++++++++++++++----- servers/rendering/renderer_scene_cull.h | 90 ++- servers/rendering/renderer_viewport.cpp | 4 + servers/rendering/rendering_server_default.h | 4 +- servers/rendering_server.cpp | 4 +- servers/rendering_server.h | 4 +- 15 files changed, 918 insertions(+), 305 deletions(-) create mode 100644 core/templates/bin_sorted_array.h diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h new file mode 100644 index 0000000000..be9d0b5475 --- /dev/null +++ b/core/templates/bin_sorted_array.h @@ -0,0 +1,181 @@ +/*************************************************************************/ +/* bin_sorted_array.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef BIN_SORTED_ARRAY_H +#define BIN_SORTED_ARRAY_H + +#include "core/templates/local_vector.h" +#include "core/templates/paged_array.h" + +template +class BinSortedArray { + PagedArray array; + LocalVector bin_limits; + + // Implement if elements need to keep track of their own index in the array. + _FORCE_INLINE_ virtual void _update_idx(T &r_element, uint64_t p_idx) {} + + _FORCE_INLINE_ void _swap(uint64_t p_a, uint64_t p_b) { + SWAP(array[p_a], array[p_b]); + _update_idx(array[p_a], p_a); + _update_idx(array[p_b], p_b); + } + +public: + uint64_t insert(T &p_element, uint64_t p_bin) { + array.push_back(p_element); + uint64_t new_idx = array.size() - 1; + _update_idx(p_element, new_idx); + bin_limits[0] = new_idx; + if (p_bin != 0) { + new_idx = move(new_idx, p_bin); + } + return new_idx; + } + + uint64_t move(uint64_t p_idx, uint64_t p_bin) { + ERR_FAIL_COND_V(p_idx >= array.size(), -1); + + uint64_t current_bin = bin_limits.size() - 1; + while (p_idx > bin_limits[current_bin]) { + current_bin--; + } + + if (p_bin == current_bin) { + return p_idx; + } + + uint64_t current_idx = p_idx; + if (p_bin > current_bin) { + while (p_bin > current_bin) { + uint64_t swap_idx = 0; + + if (current_bin == bin_limits.size() - 1) { + bin_limits.push_back(0); + } else { + bin_limits[current_bin + 1]++; + swap_idx = bin_limits[current_bin + 1]; + } + + if (current_idx != swap_idx) { + _swap(current_idx, swap_idx); + current_idx = swap_idx; + } + + current_bin++; + } + } else { + while (p_bin < current_bin) { + uint64_t swap_idx = bin_limits[current_bin]; + + if (current_idx != swap_idx) { + _swap(current_idx, swap_idx); + } + + if (current_bin == bin_limits.size() - 1 && bin_limits[current_bin] == 0) { + bin_limits.resize(bin_limits.size() - 1); + } else { + bin_limits[current_bin]--; + } + current_idx = swap_idx; + current_bin--; + } + } + + return current_idx; + } + + void remove(uint64_t p_idx) { + ERR_FAIL_COND(p_idx >= array.size()); + uint64_t new_idx = move(p_idx, 0); + uint64_t swap_idx = array.size() - 1; + + if (new_idx != swap_idx) { + _swap(new_idx, swap_idx); + } + + if (bin_limits[0] > 0) { + bin_limits[0]--; + } + + array.pop_back(); + } + + void set_page_pool(PagedArrayPool *p_page_pool) { + array.set_page_pool(p_page_pool); + } + + _FORCE_INLINE_ const T &operator[](uint64_t p_index) const { + return array[p_index]; + } + + _FORCE_INLINE_ T &operator[](uint64_t p_index) { + return array[p_index]; + } + + int get_bin_count() { + if (array.size() == 0) { + return 0; + } + return bin_limits.size(); + } + + int get_bin_start(int p_bin) { + ERR_FAIL_COND_V(p_bin >= get_bin_count(), ~0U); + if ((unsigned int)p_bin == bin_limits.size() - 1) { + return 0; + } + return bin_limits[p_bin + 1] + 1; + } + + int get_bin_size(int p_bin) { + ERR_FAIL_COND_V(p_bin >= get_bin_count(), 0); + if ((unsigned int)p_bin == bin_limits.size() - 1) { + return bin_limits[p_bin] + 1; + } + return bin_limits[p_bin] - bin_limits[p_bin + 1]; + } + + void reset() { + array.reset(); + bin_limits.clear(); + bin_limits.push_back(0); + } + + BinSortedArray() { + bin_limits.push_back(0); + } + + virtual ~BinSortedArray() { + reset(); + } +}; + +#endif //BIN_SORTED_ARRAY_H diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml index b2c3bfc3ed..667ca2dacf 100644 --- a/doc/classes/GeometryInstance3D.xml +++ b/doc/classes/GeometryInstance3D.xml @@ -52,26 +52,22 @@ - - The GeometryInstance3D's max LOD distance. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance3D's max LOD margin. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance3D's min LOD distance. - [b]Note:[/b] This property currently has no effect. - - - The GeometryInstance3D's min LOD margin. - [b]Note:[/b] This property currently has no effect. - The material override for the whole geometry. If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh. + + Starting distance from which the GeometryInstance3D will be visible, taking [member visibility_range_begin_margin] into account as well. The default value of 0 is used to disable the range check. + + + Margin for the [member visibility_range_begin] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_begin] threshold by this amount. + + + Distance from which the GeometryInstance3D will be hidden, taking [member visibility_range_end_margin] into account as well. The default value of 0 is used to disable the range check.. + + + Margin for the [member visibility_range_end] threshold. The GeometryInstance3D will only change its visibility state when it goes over or under the [member visibility_range_end] threshold by this amount. + diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index a6237708c6..ed1899ca74 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -310,6 +310,9 @@ Local space [Transform3D] of this node, with respect to the parent node. + + Defines the visibility range parent for this node and its subtree. The visibility parent must be a GeometryInstance3D. Any visual instance will only be visible if the visibility parent (and all of its visibility ancestors) is hidden by being closer to the camera than its own [member GeometryInstance3D.visibility_range_begin]. Nodes hidden via the [member Node3D.visible] property are essentially removed from the visibility dependency tree, so dependant instances will not take the hidden node or its ancestors into account. + If [code]true[/code], this node is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]). diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index ba19176788..9b9644093f 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1175,17 +1175,6 @@ Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. - - - - - - - - - Not implemented in Godot 3.x. - - @@ -1197,23 +1186,6 @@ Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow]. - - - - - - - - - - - - - - - Not implemented in Godot 3.x. - - @@ -1238,6 +1210,23 @@ Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance3D.material_override]. + + + + + + + + + + + + + + + Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties. + + @@ -1341,6 +1330,17 @@ Sets the world space transform of the instance. Equivalent to [member Node3D.transform]. + + + + + + + + + Sets the visibility parent for the given instance. Equivalent to [member Node3D.visibility_parent]. + + diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index e96e4df55c..1c0ffa5126 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -32,6 +32,7 @@ #include "core/config/engine.h" #include "core/object/message_queue.h" +#include "scene/3d/visual_instance_3d.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" @@ -148,6 +149,7 @@ void Node3D::_notification(int p_what) { _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); + _update_visibility_parent(true); } break; case NOTIFICATION_EXIT_TREE: { @@ -161,6 +163,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; data.top_level_active = false; + _update_visibility_parent(true); } break; case NOTIFICATION_ENTER_WORLD: { data.inside_world = true; @@ -690,6 +693,51 @@ void Node3D::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void Node3D::_update_visibility_parent(bool p_update_root) { + RID new_parent; + + if (!visibility_parent_path.is_empty()) { + if (!p_update_root) { + return; + } + Node *parent = get_node_or_null(visibility_parent_path); + ERR_FAIL_COND_MSG(!parent, "Can't find visibility parent node at path: " + visibility_parent_path); + ERR_FAIL_COND_MSG(parent == this, "The visibility parent can't be the same node."); + GeometryInstance3D *gi = Object::cast_to(parent); + ERR_FAIL_COND_MSG(!gi, "The visibility parent node must be a GeometryInstance3D, at path: " + visibility_parent_path); + new_parent = gi ? gi->get_instance() : RID(); + } else if (data.parent) { + new_parent = data.parent->data.visibility_parent; + } + + if (new_parent == data.visibility_parent) { + return; + } + + data.visibility_parent = new_parent; + + VisualInstance3D *vi = Object::cast_to(this); + if (vi) { + RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent); + } + + for (List::Element *E = data.children.front(); E; E = E->next()) { + Node3D *c = E->get(); + c->_update_visibility_parent(false); + } +} + +void Node3D::set_visibility_parent(const NodePath &p_path) { + visibility_parent_path = p_path; + if (is_inside_tree()) { + _update_visibility_parent(true); + } +} + +NodePath Node3D::get_visibility_parent() const { + return visibility_parent_path; +} + void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform); @@ -713,6 +761,9 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_transform"), &Node3D::force_update_transform); + ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent); + ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent); + ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo); ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo); @@ -768,6 +819,7 @@ void Node3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo"); ADD_SIGNAL(MethodInfo("visibility_changed")); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 5f076ceb12..423ee59c7a 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -75,6 +75,8 @@ class Node3D : public Node { bool top_level = false; bool inside_world = false; + RID visibility_parent; + int children_lock = 0; Node3D *parent = nullptr; List children; @@ -95,12 +97,17 @@ class Node3D : public Node { } data; + NodePath visibility_parent_path; + void _update_gizmo(); void _notify_dirty(); void _propagate_transform_changed(Node3D *p_origin); void _propagate_visibility_changed(); + void _propagate_visibility_parent(); + void _update_visibility_parent(bool p_update_root); + protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } @@ -196,6 +203,9 @@ public: void force_update_transform(); + void set_visibility_parent(const NodePath &p_path); + NodePath get_visibility_parent() const; + Node3D(); }; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 6971c1ce2a..c16e3c2d78 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -150,40 +150,40 @@ Ref GeometryInstance3D::get_material_override() const { return material_override; } -void GeometryInstance3D::set_lod_min_distance(float p_dist) { - lod_min_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin(float p_dist) { + visibility_range_begin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_distance() const { - return lod_min_distance; +float GeometryInstance3D::get_visibility_range_begin() const { + return visibility_range_begin; } -void GeometryInstance3D::set_lod_max_distance(float p_dist) { - lod_max_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end(float p_dist) { + visibility_range_end = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_distance() const { - return lod_max_distance; +float GeometryInstance3D::get_visibility_range_end() const { + return visibility_range_end; } -void GeometryInstance3D::set_lod_min_hysteresis(float p_dist) { - lod_min_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) { + visibility_range_begin_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_hysteresis() const { - return lod_min_hysteresis; +float GeometryInstance3D::get_visibility_range_begin_margin() const { + return visibility_range_begin_margin; } -void GeometryInstance3D::set_lod_max_hysteresis(float p_dist) { - lod_max_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) { + visibility_range_end_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_hysteresis() const { - return lod_max_hysteresis; +float GeometryInstance3D::get_visibility_range_end_margin() const { + return visibility_range_end_margin; } void GeometryInstance3D::_notification(int p_what) { @@ -357,17 +357,17 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); - ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_end_margin"), &GeometryInstance3D::get_visibility_range_end_margin); - ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); - ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_end", "distance"), &GeometryInstance3D::set_visibility_range_end); + ClassDB::bind_method(D_METHOD("get_visibility_range_end"), &GeometryInstance3D::get_visibility_range_end); - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin_margin", "distance"), &GeometryInstance3D::set_visibility_range_begin_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin_margin"), &GeometryInstance3D::get_visibility_range_begin_margin); - ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance); - ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin", "distance"), &GeometryInstance3D::set_visibility_range_begin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin"), &GeometryInstance3D::get_visibility_range_begin); ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); @@ -398,11 +398,11 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); - ADD_GROUP("LOD", "lod_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis"); + ADD_GROUP("Visibility Range", "visibility_range_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); //ADD_SIGNAL( MethodInfo("visibility_changed")); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 68d29ef81e..2d5699859b 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -107,10 +107,13 @@ public: private: ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON; Ref material_override; - float lod_min_distance = 0.0; - float lod_max_distance = 0.0; - float lod_min_hysteresis = 0.0; - float lod_max_hysteresis = 0.0; + + float visibility_range_begin = 0.0; + float visibility_range_end = 0.0; + float visibility_range_begin_margin = 0.0; + float visibility_range_end_margin = 0.0; + + Vector visibility_range_children; float lod_bias = 1.0; @@ -136,17 +139,20 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; - void set_lod_min_distance(float p_dist); - float get_lod_min_distance() const; + void set_visibility_range_begin(float p_dist); + float get_visibility_range_begin() const; - void set_lod_max_distance(float p_dist); - float get_lod_max_distance() const; + void set_visibility_range_end(float p_dist); + float get_visibility_range_end() const; - void set_lod_min_hysteresis(float p_dist); - float get_lod_min_hysteresis() const; + void set_visibility_range_begin_margin(float p_dist); + float get_visibility_range_begin_margin() const; - void set_lod_max_hysteresis(float p_dist); - float get_lod_max_hysteresis() const; + void set_visibility_range_end_margin(float p_dist); + float get_visibility_range_end_margin() const; + + void set_visibility_range_parent(const Node *p_parent); + void clear_visibility_range_parent(); void set_material_override(const Ref &p_material); Ref get_material_override() const; diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 4d1ea2e382..3b25498ed8 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -63,6 +63,8 @@ public: 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; virtual RID scenario_get_environment(RID p_scenario) = 0; + virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0; + virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) = 0; virtual RID instance_allocate() = 0; virtual void instance_initialize(RID p_rid) = 0; @@ -82,6 +84,7 @@ public: virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0; virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0; + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0; // don't use these in a game! virtual Vector instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0; @@ -92,8 +95,7 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting) = 0; virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0; - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) = 0; virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 290f058fc7..2c66b22089 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -323,6 +323,7 @@ void RendererSceneCull::scenario_initialize(RID p_rid) { scenario->instance_aabbs.set_page_pool(&instance_aabb_page_pool); scenario->instance_data.set_page_pool(&instance_data_page_pool); + scenario->instance_visibility.set_page_pool(&instance_visibility_data_page_pool); RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid); @@ -369,6 +370,37 @@ RID RendererSceneCull::scenario_get_environment(RID p_scenario) { return scenario->environment; } +void RendererSceneCull::scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport) { + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + if (!scenario->viewport_visibility_masks.has(p_viewport)) { + return; + } + + uint64_t mask = scenario->viewport_visibility_masks[p_viewport]; + scenario->used_viewport_visibility_bits &= ~mask; + scenario->viewport_visibility_masks.erase(p_viewport); +} + +void RendererSceneCull::scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport) { + Scenario *scenario = scenario_owner.getornull(p_scenario); + ERR_FAIL_COND(!scenario); + ERR_FAIL_COND(scenario->viewport_visibility_masks.has(p_viewport)); + + uint64_t new_mask = 1; + while (new_mask & scenario->used_viewport_visibility_bits) { + new_mask <<= 1; + } + + if (new_mask == 0) { + ERR_PRINT("Only 64 viewports per scenario allowed when using visibility ranges."); + new_mask = ((uint64_t)1) << 63; + } + + scenario->viewport_visibility_masks[p_viewport] = new_mask; + scenario->used_viewport_visibility_bits |= new_mask; +} + /* INSTANCING API */ void RendererSceneCull::_instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies) { @@ -1103,10 +1135,142 @@ void RendererSceneCull::instance_geometry_set_material_override(RID p_instance, } } -void RendererSceneCull::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) { +void RendererSceneCull::instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) { + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + instance->visibility_range_begin = p_min; + instance->visibility_range_end = p_max; + instance->visibility_range_begin_margin = p_min_margin; + instance->visibility_range_end_margin = p_max_margin; + + _update_instance_visibility_dependencies(instance); + + if (instance->scenario && instance->visibility_index != -1) { + InstanceVisibilityData &vd = instance->scenario->instance_visibility[instance->visibility_index]; + vd.range_begin = instance->visibility_range_begin; + vd.range_end = instance->visibility_range_end; + vd.range_begin_margin = instance->visibility_range_begin_margin; + vd.range_end_margin = instance->visibility_range_end_margin; + } } -void RendererSceneCull::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) { +void RendererSceneCull::instance_set_visibility_parent(RID p_instance, RID p_parent_instance) { + Instance *instance = instance_owner.getornull(p_instance); + ERR_FAIL_COND(!instance); + + Instance *old_parent = instance->visibility_parent; + if (old_parent) { + if ((1 << old_parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && old_parent->base_data) { + InstanceGeometryData *old_parent_geom = static_cast(old_parent->base_data); + old_parent_geom->visibility_dependencies.erase(instance); + _update_instance_visibility_depth(old_parent); + } + instance->visibility_parent = nullptr; + } + + Instance *parent = instance_owner.getornull(p_parent_instance); + ERR_FAIL_COND(p_parent_instance.is_valid() && !parent); + + if (parent) { + if ((1 << parent->base_type) & RS::INSTANCE_GEOMETRY_MASK && parent->base_data) { + InstanceGeometryData *parent_geom = static_cast(parent->base_data); + parent_geom->visibility_dependencies.insert(instance); + _update_instance_visibility_depth(parent); + } + instance->visibility_parent = parent; + } + + _update_instance_visibility_dependencies(instance); +} + +void RendererSceneCull::_update_instance_visibility_depth(Instance *p_instance) { + bool cycle_detected = false; + Set traversed_nodes; + + { + Instance *instance = p_instance; + while (instance && ((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && instance->base_data) { + InstanceGeometryData *geom = static_cast(instance->base_data); + if (!geom->visibility_dependencies.is_empty()) { + uint32_t depth = 0; + for (Set::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + if (((1 << E->get()->base_type) & RS::INSTANCE_GEOMETRY_MASK) == 0 || !E->get()->base_data) { + continue; + } + InstanceGeometryData *child_geom = static_cast(E->get()->base_data); + depth = MAX(depth, child_geom->visibility_dependencies_depth); + } + geom->visibility_dependencies_depth = depth + 1; + } else { + geom->visibility_dependencies_depth = 0; + } + + if (instance->scenario && instance->visibility_index != -1) { + instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth); + } + + traversed_nodes.insert(instance); + + instance = instance->visibility_parent; + if (traversed_nodes.has(instance)) { + cycle_detected = true; + break; + } + } + } + + if (cycle_detected) { + ERR_PRINT("Cycle detected in the visibility dependecies tree."); + for (Set::Element *E = traversed_nodes.front(); E; E = E->next()) { + Instance *instance = E->get(); + InstanceGeometryData *geom = static_cast(instance->base_data); + geom->visibility_dependencies_depth = 0; + if (instance->scenario && instance->visibility_index != -1) { + instance->scenario->instance_visibility.move(instance->visibility_index, geom->visibility_dependencies_depth); + } + } + } +} + +void RendererSceneCull::_update_instance_visibility_dependencies(Instance *p_instance) { + bool is_geometry_instance = ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && p_instance->base_data; + bool has_visibility_range = p_instance->visibility_range_begin > 0.0 || p_instance->visibility_range_end > 0.0; + bool needs_visibility_cull = has_visibility_range && is_geometry_instance && p_instance->array_index != -1; + + if (!needs_visibility_cull && p_instance->visibility_index != -1) { + p_instance->scenario->instance_visibility.remove(p_instance->visibility_index); + p_instance->visibility_index = -1; + } else if (needs_visibility_cull && p_instance->visibility_index == -1) { + InstanceVisibilityData vd; + vd.instance = p_instance; + vd.range_begin = p_instance->visibility_range_begin; + vd.range_end = p_instance->visibility_range_end; + vd.range_begin_margin = p_instance->visibility_range_begin_margin; + vd.range_end_margin = p_instance->visibility_range_end_margin; + vd.position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f; + vd.array_index = p_instance->array_index; + + InstanceGeometryData *geom_data = static_cast(p_instance->base_data); + p_instance->scenario->instance_visibility.insert(vd, geom_data->visibility_dependencies_depth); + } + + if (p_instance->scenario && p_instance->array_index != -1) { + p_instance->scenario->instance_data[p_instance->array_index].visibility_index = p_instance->visibility_index; + + InstanceGeometryData *geom_data = static_cast(p_instance->base_data); + if ((has_visibility_range || p_instance->visibility_parent) && (p_instance->visibility_index == -1 || (geom_data && geom_data->visibility_dependencies_depth == 0))) { + p_instance->scenario->instance_data[p_instance->array_index].flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK; + } else { + p_instance->scenario->instance_data[p_instance->array_index].flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK; + } + + if (p_instance->visibility_parent) { + p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = p_instance->visibility_parent->array_index; + } else { + p_instance->scenario->instance_data[p_instance->array_index].parent_array_index = -1; + } + } } void RendererSceneCull::instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index) { @@ -1352,12 +1516,23 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { idata.layer_mask = p_instance->layer_mask; idata.flags = p_instance->base_type; //changing it means de-indexing, so this never needs to be changed later idata.base_rid = p_instance->base; + idata.parent_array_index = p_instance->visibility_parent ? p_instance->visibility_parent->array_index : -1; + idata.visibility_index = p_instance->visibility_index; + switch (p_instance->base_type) { case RS::INSTANCE_MESH: case RS::INSTANCE_MULTIMESH: case RS::INSTANCE_IMMEDIATE: case RS::INSTANCE_PARTICLES: { - idata.instance_geometry = static_cast(p_instance->base_data)->geometry_instance; + InstanceGeometryData *geom = static_cast(p_instance->base_data); + idata.instance_geometry = geom->geometry_instance; + + for (Set::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = p_instance->array_index; + } + } } break; case RS::INSTANCE_LIGHT: { idata.instance_data_rid = static_cast(p_instance->base_data)->instance.get_id(); @@ -1404,6 +1579,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->scenario->instance_data.push_back(idata); p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb)); + _update_instance_visibility_dependencies(p_instance); } else { if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY].update(p_instance->indexer_id, bvh_aabb); @@ -1413,6 +1589,10 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->scenario->instance_aabbs[p_instance->array_index] = InstanceBounds(p_instance->transformed_aabb); } + if (p_instance->visibility_index != -1) { + p_instance->scenario->instance_visibility[p_instance->visibility_index].position = p_instance->transformed_aabb.get_position() + p_instance->transformed_aabb.get_size() / 2.0f; + } + //move instance and repair pair_pass++; @@ -1486,9 +1666,24 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) { //replace this by last int32_t swap_with_index = p_instance->scenario->instance_data.size() - 1; if (swap_with_index != p_instance->array_index) { - p_instance->scenario->instance_data[swap_with_index].instance->array_index = p_instance->array_index; //swap + Instance *swapped_instance = p_instance->scenario->instance_data[swap_with_index].instance; + swapped_instance->array_index = p_instance->array_index; //swap p_instance->scenario->instance_data[p_instance->array_index] = p_instance->scenario->instance_data[swap_with_index]; p_instance->scenario->instance_aabbs[p_instance->array_index] = p_instance->scenario->instance_aabbs[swap_with_index]; + + if (swapped_instance->visibility_index != -1) { + swapped_instance->scenario->instance_visibility[swapped_instance->visibility_index].array_index = swapped_instance->array_index; + } + + if ((1 << swapped_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { + InstanceGeometryData *geom = static_cast(swapped_instance->base_data); + for (Set::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance != p_instance && dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = swapped_instance->array_index; + } + } + } } // pop last @@ -1505,7 +1700,16 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) { scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, nullptr, 0); scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, nullptr, 0); scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, nullptr, 0); + + for (Set::Element *E = geom->visibility_dependencies.front(); E; E = E->next()) { + Instance *dep_instance = E->get(); + if (dep_instance->array_index != -1) { + dep_instance->scenario->instance_data[dep_instance->array_index].parent_array_index = -1; + } + } } + + _update_instance_visibility_dependencies(p_instance); } void RendererSceneCull::_update_instance_aabb(Instance *p_instance) { @@ -2272,16 +2476,73 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_ #endif } -void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) { +void RendererSceneCull::_visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data) { + uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); + uint32_t bin_from = p_thread * cull_data->cull_count / total_threads; + uint32_t bin_to = (p_thread + 1 == total_threads) ? cull_data->cull_count : ((p_thread + 1) * cull_data->cull_count / total_threads); + + _visibility_cull(*cull_data, cull_data->cull_offset + bin_from, cull_data->cull_offset + bin_to); +} + +void RendererSceneCull::_visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to) { + Scenario *scenario = cull_data.scenario; + for (unsigned int i = p_from; i < p_to; i++) { + InstanceVisibilityData &vd = scenario->instance_visibility[i]; + InstanceData &idata = scenario->instance_data[vd.array_index]; + + if (idata.parent_array_index >= 0) { + uint32_t parent_flags = scenario->instance_data[idata.parent_array_index].flags; + if ((parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN) || (parent_flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE) == 0) { + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + continue; + } + } + + int range_check = _visibility_range_check(vd, cull_data.camera_position, cull_data.viewport_mask); + + if (range_check == -1) { + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } else if (range_check == 1) { + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags |= InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } else { + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN; + idata.flags &= ~InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE; + } + } +} + +int RendererSceneCull::_visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask) { + float dist = p_camera_pos.distance_to(r_vis_data.position); + + bool in_range_last_frame = p_viewport_mask & r_vis_data.viewport_state; + float begin_offset = in_range_last_frame ? -r_vis_data.range_begin_margin : r_vis_data.range_begin_margin; + float end_offset = in_range_last_frame ? r_vis_data.range_end_margin : -r_vis_data.range_end_margin; + + if (r_vis_data.range_end > 0.0f && dist > r_vis_data.range_end + end_offset) { + r_vis_data.viewport_state &= ~p_viewport_mask; + return -1; + } else if (r_vis_data.range_begin > 0.0f && dist < r_vis_data.range_begin + begin_offset) { + r_vis_data.viewport_state &= ~p_viewport_mask; + return 1; + } else { + r_vis_data.viewport_state |= p_viewport_mask; + return 0; + } +} + +void RendererSceneCull::_scene_cull_threaded(uint32_t p_thread, CullData *cull_data) { uint32_t cull_total = cull_data->scenario->instance_data.size(); uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); uint32_t cull_from = p_thread * cull_total / total_threads; uint32_t cull_to = (p_thread + 1 == total_threads) ? cull_total : ((p_thread + 1) * cull_total / total_threads); - _frustum_cull(*cull_data, frustum_cull_result_threads[p_thread], cull_from, cull_to); + _scene_cull(*cull_data, scene_cull_result_threads[p_thread], cull_from, cull_to); } -void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { +void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to) { uint64_t frame_number = RSG::rasterizer->get_frame_number(); float lightmap_probe_update_speed = RSG::storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time(); @@ -2296,177 +2557,192 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu for (uint64_t i = p_from; i < p_to; i++) { bool mesh_visible = false; - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum) && (cull_data.occlusion_buffer == nullptr || cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING || - !cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))) { - InstanceData &idata = cull_data.scenario->instance_data[i]; - uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; + InstanceData &idata = cull_data.scenario->instance_data[i]; + uint32_t visibility_flags = idata.flags & (InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE | InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN); + int32_t visibility_check = -1; - if ((cull_data.visible_layers & idata.layer_mask) == 0) { - //failure - } else if (base_type == RS::INSTANCE_LIGHT) { - cull_result.lights.push_back(idata.instance); - cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid)); - if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) { - scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later - } +#define HIDDEN_BY_VISIBILITY_CHECKS (visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE || visibility_flags == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN) +#define LAYER_CHECK (cull_data.visible_layers & idata.layer_mask) +#define IN_FRUSTUM(f) (cull_data.scenario->instance_aabbs[i].in_frustum(f)) +#define VIS_RANGE_CHECK ((idata.visibility_index == -1) || _visibility_range_check(cull_data.scenario->instance_visibility[idata.visibility_index], cull_data.cam_transform.origin, cull_data.visibility_cull_data->viewport_mask) == 0) +#define VIS_PARENT_CHECK ((idata.parent_array_index == -1) || ((cull_data.scenario->instance_data[idata.parent_array_index].flags & InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK) == InstanceData::FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE)) +#define VIS_CHECK (visibility_check < 0 ? (visibility_check = (visibility_flags != InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK || (VIS_RANGE_CHECK && VIS_PARENT_CHECK))) : visibility_check) +#define OCCLUSION_CULLED (cull_data.occlusion_buffer != nullptr && cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING && cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near)) - } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) { - if (cull_data.render_reflection_probe != idata.instance) { - //avoid entering The Matrix + if (!HIDDEN_BY_VISIBILITY_CHECKS) { + if (LAYER_CHECK && IN_FRUSTUM(cull_data.cull->frustum) && VIS_CHECK && !OCCLUSION_CULLED) { + uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; + if (base_type == RS::INSTANCE_LIGHT) { + cull_result.lights.push_back(idata.instance); + cull_result.light_instances.push_back(RID::from_uint64(idata.instance_data_rid)); + if (cull_data.shadow_atlas.is_valid() && RSG::storage->light_has_shadow(idata.base_rid)) { + scene_render->light_instance_mark_visible(RID::from_uint64(idata.instance_data_rid)); //mark it visible for shadow allocation later + } - if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) { - InstanceReflectionProbeData *reflection_probe = static_cast(idata.instance->base_data); - cull_data.cull->lock.lock(); - if (!reflection_probe->update_list.in_list()) { - reflection_probe->render_step = 0; - reflection_probe_render_list.add_last(&reflection_probe->update_list); + } else if (base_type == RS::INSTANCE_REFLECTION_PROBE) { + if (cull_data.render_reflection_probe != idata.instance) { + //avoid entering The Matrix + + if ((idata.flags & InstanceData::FLAG_REFLECTION_PROBE_DIRTY) || scene_render->reflection_probe_instance_needs_redraw(RID::from_uint64(idata.instance_data_rid))) { + InstanceReflectionProbeData *reflection_probe = static_cast(idata.instance->base_data); + cull_data.cull->lock.lock(); + if (!reflection_probe->update_list.in_list()) { + reflection_probe->render_step = 0; + reflection_probe_render_list.add_last(&reflection_probe->update_list); + } + cull_data.cull->lock.unlock(); + + idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY); } - cull_data.cull->lock.unlock(); - idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY); + if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) { + cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid)); + } } + } else if (base_type == RS::INSTANCE_DECAL) { + cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid)); - if (scene_render->reflection_probe_instance_has_reflection(RID::from_uint64(idata.instance_data_rid))) { - cull_result.reflections.push_back(RID::from_uint64(idata.instance_data_rid)); + } else if (base_type == RS::INSTANCE_VOXEL_GI) { + InstanceVoxelGIData *voxel_gi = static_cast(idata.instance->base_data); + cull_data.cull->lock.lock(); + if (!voxel_gi->update_element.in_list()) { + voxel_gi_update_list.add(&voxel_gi->update_element); } - } - } else if (base_type == RS::INSTANCE_DECAL) { - cull_result.decals.push_back(RID::from_uint64(idata.instance_data_rid)); + cull_data.cull->lock.unlock(); + cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid)); - } else if (base_type == RS::INSTANCE_VOXEL_GI) { - InstanceVoxelGIData *voxel_gi = static_cast(idata.instance->base_data); - cull_data.cull->lock.lock(); - if (!voxel_gi->update_element.in_list()) { - voxel_gi_update_list.add(&voxel_gi->update_element); - } - cull_data.cull->lock.unlock(); - cull_result.voxel_gi_instances.push_back(RID::from_uint64(idata.instance_data_rid)); + } else if (base_type == RS::INSTANCE_LIGHTMAP) { + cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid)); + } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) { + bool keep = true; - } else if (base_type == RS::INSTANCE_LIGHTMAP) { - cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid)); - } else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) { - bool keep = true; - - if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) { - RenderingServerDefault::redraw_request(); - } - - if (base_type == RS::INSTANCE_MESH) { - mesh_visible = true; - } else if (base_type == RS::INSTANCE_PARTICLES) { - //particles visible? process them - if (RSG::storage->particles_is_inactive(idata.base_rid)) { - //but if nothing is going on, don't do it. - keep = false; - } else { - cull_data.cull->lock.lock(); - RSG::storage->particles_request_process(idata.base_rid); - cull_data.cull->lock.unlock(); - RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized()); - //particles visible? request redraw + if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) { RenderingServerDefault::redraw_request(); } - } - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) { - InstanceGeometryData *geom = static_cast(idata.instance->base_data); - uint32_t idx = 0; - - for (Set::Element *E = geom->lights.front(); E; E = E->next()) { - InstanceLightData *light = static_cast(E->get()->base_data); - instance_pair_buffer[idx++] = light->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; - } - } - - scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY); - } - - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) { - InstanceGeometryData *geom = static_cast(idata.instance->base_data); - uint32_t idx = 0; - - for (Set::Element *E = geom->reflection_probes.front(); E; E = E->next()) { - InstanceReflectionProbeData *reflection_probe = static_cast(E->get()->base_data); - - instance_pair_buffer[idx++] = reflection_probe->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; - } - } - - scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY); - } - - if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) { - InstanceGeometryData *geom = static_cast(idata.instance->base_data); - uint32_t idx = 0; - - for (Set::Element *E = geom->decals.front(); E; E = E->next()) { - InstanceDecalData *decal = static_cast(E->get()->base_data); - - instance_pair_buffer[idx++] = decal->instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; - } - } - scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY); - } - - if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) { - InstanceGeometryData *geom = static_cast(idata.instance->base_data); - uint32_t idx = 0; - for (Set::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) { - InstanceVoxelGIData *voxel_gi = static_cast(E->get()->base_data); - - instance_pair_buffer[idx++] = voxel_gi->probe_instance; - if (idx == MAX_INSTANCE_PAIRS) { - break; - } - } - - scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx); - idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); - } - - if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) { - InstanceGeometryData *geom = static_cast(idata.instance->base_data); - Color *sh = idata.instance->lightmap_sh.ptrw(); - const Color *target_sh = idata.instance->lightmap_target_sh.ptr(); - for (uint32_t j = 0; j < 9; j++) { - sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed)); - } - scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh); - idata.instance->last_frame_pass = frame_number; - } - - if (keep) { - cull_result.geometry_instances.push_back(idata.instance_geometry); - } - } - } - - for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) { - for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) { - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->shadows[j].cascades[k].frustum)) { - InstanceData &idata = cull_data.scenario->instance_data[i]; - uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; - - if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) { - cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry); + if (base_type == RS::INSTANCE_MESH) { mesh_visible = true; + } else if (base_type == RS::INSTANCE_PARTICLES) { + //particles visible? process them + if (RSG::storage->particles_is_inactive(idata.base_rid)) { + //but if nothing is going on, don't do it. + keep = false; + } else { + cull_data.cull->lock.lock(); + RSG::storage->particles_request_process(idata.base_rid); + cull_data.cull->lock.unlock(); + RSG::storage->particles_set_view_axis(idata.base_rid, -cull_data.cam_transform.basis.get_axis(2).normalized(), cull_data.cam_transform.basis.get_axis(1).normalized()); + //particles visible? request redraw + RenderingServerDefault::redraw_request(); + } + } + + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_LIGHT) && (idata.flags & InstanceData::FLAG_GEOM_LIGHTING_DIRTY)) { + InstanceGeometryData *geom = static_cast(idata.instance->base_data); + uint32_t idx = 0; + + for (Set::Element *E = geom->lights.front(); E; E = E->next()) { + InstanceLightData *light = static_cast(E->get()->base_data); + instance_pair_buffer[idx++] = light->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } + } + + scene_render->geometry_instance_pair_light_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_LIGHTING_DIRTY); + } + + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (idata.flags & InstanceData::FLAG_GEOM_REFLECTION_DIRTY)) { + InstanceGeometryData *geom = static_cast(idata.instance->base_data); + uint32_t idx = 0; + + for (Set::Element *E = geom->reflection_probes.front(); E; E = E->next()) { + InstanceReflectionProbeData *reflection_probe = static_cast(E->get()->base_data); + + instance_pair_buffer[idx++] = reflection_probe->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } + } + + scene_render->geometry_instance_pair_reflection_probe_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_REFLECTION_DIRTY); + } + + if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (idata.flags & InstanceData::FLAG_GEOM_DECAL_DIRTY)) { + InstanceGeometryData *geom = static_cast(idata.instance->base_data); + uint32_t idx = 0; + + for (Set::Element *E = geom->decals.front(); E; E = E->next()) { + InstanceDecalData *decal = static_cast(E->get()->base_data); + + instance_pair_buffer[idx++] = decal->instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } + } + scene_render->geometry_instance_pair_decal_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_DECAL_DIRTY); + } + + if (idata.flags & InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY) { + InstanceGeometryData *geom = static_cast(idata.instance->base_data); + uint32_t idx = 0; + for (Set::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) { + InstanceVoxelGIData *voxel_gi = static_cast(E->get()->base_data); + + instance_pair_buffer[idx++] = voxel_gi->probe_instance; + if (idx == MAX_INSTANCE_PAIRS) { + break; + } + } + + scene_render->geometry_instance_pair_voxel_gi_instances(geom->geometry_instance, instance_pair_buffer, idx); + idata.flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); + } + + if ((idata.flags & InstanceData::FLAG_LIGHTMAP_CAPTURE) && idata.instance->last_frame_pass != frame_number && !idata.instance->lightmap_target_sh.is_empty() && !idata.instance->lightmap_sh.is_empty()) { + InstanceGeometryData *geom = static_cast(idata.instance->base_data); + Color *sh = idata.instance->lightmap_sh.ptrw(); + const Color *target_sh = idata.instance->lightmap_target_sh.ptr(); + for (uint32_t j = 0; j < 9; j++) { + sh[j] = sh[j].lerp(target_sh[j], MIN(1.0, lightmap_probe_update_speed)); + } + scene_render->geometry_instance_set_lightmap_capture(geom->geometry_instance, sh); + idata.instance->last_frame_pass = frame_number; + } + + if (keep) { + cull_result.geometry_instances.push_back(idata.instance_geometry); + } + } + } + + for (uint32_t j = 0; j < cull_data.cull->shadow_count; j++) { + for (uint32_t k = 0; k < cull_data.cull->shadows[j].cascade_count; k++) { + if (IN_FRUSTUM(cull_data.cull->shadows[j].cascades[k].frustum) && VIS_CHECK) { + uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; + + if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && idata.flags & InstanceData::FLAG_CAST_SHADOWS) { + cull_result.directional_shadows[j].cascade_geometry_instances[k].push_back(idata.instance_geometry); + mesh_visible = true; + } } } } } +#undef HIDDEN_BY_VISIBILITY_CHECKS +#undef LAYER_CHECK +#undef IN_FRUSTUM +#undef VIS_RANGE_CHECK +#undef VIS_PARENT_CHECK +#undef VIS_CHECK +#undef OCCLUSION_CULLED + for (uint32_t j = 0; j < cull_data.cull->sdfgi.region_count; j++) { if (cull_data.scenario->instance_aabbs[i].in_aabb(cull_data.cull->sdfgi.region_aabb[j])) { - InstanceData &idata = cull_data.scenario->instance_data[i]; uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; if (base_type == RS::INSTANCE_LIGHT) { @@ -2507,7 +2783,35 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c scene_render->sdfgi_update(p_render_buffers, p_environment, p_camera_data->main_transform.origin); //update conditions for SDFGI (whether its used or not) } - RENDER_TIMESTAMP("Frustum Culling"); + RENDER_TIMESTAMP("Visibility Dependencies"); + + VisibilityCullData visibility_cull_data; + if (scenario->instance_visibility.get_bin_count() > 0) { + if (!scenario->viewport_visibility_masks.has(p_viewport)) { + scenario_add_viewport_visibility_mask(scenario->self, p_viewport); + } + + visibility_cull_data.scenario = scenario; + visibility_cull_data.viewport_mask = scenario->viewport_visibility_masks[p_viewport]; + visibility_cull_data.camera_position = p_camera_data->main_transform.origin; + + for (int i = scenario->instance_visibility.get_bin_count() - 1; i > 0; i--) { // We skip bin 0 + visibility_cull_data.cull_offset = scenario->instance_visibility.get_bin_start(i); + visibility_cull_data.cull_count = scenario->instance_visibility.get_bin_size(i); + + if (visibility_cull_data.cull_count == 0) { + continue; + } + + if (visibility_cull_data.cull_count > thread_cull_threshold) { + RendererThreadPool::singleton->thread_work_pool.do_work(RendererThreadPool::singleton->thread_work_pool.get_thread_count(), this, &RendererSceneCull::_visibility_cull_threaded, &visibility_cull_data); + } else { + _visibility_cull(visibility_cull_data, visibility_cull_data.cull_offset, visibility_cull_data.cull_offset + visibility_cull_data.cull_count); + } + } + } + + RENDER_TIMESTAMP("Culling"); //rasterizer->set_camera(p_camera_data->main_transform, p_camera_data.main_projection, p_camera_data.is_ortogonal); @@ -2580,7 +2884,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } } - frustum_cull_result.clear(); + scene_cull_result.clear(); { uint64_t cull_from = 0; @@ -2603,19 +2907,19 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c #endif if (cull_to > thread_cull_threshold) { //multiple threads - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].clear(); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].clear(); } - RendererThreadPool::singleton->thread_work_pool.do_work(frustum_cull_result_threads.size(), this, &RendererSceneCull::_frustum_cull_threaded, &cull_data); + RendererThreadPool::singleton->thread_work_pool.do_work(scene_cull_result_threads.size(), this, &RendererSceneCull::_scene_cull_threaded, &cull_data); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result.append_from(frustum_cull_result_threads[i]); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result.append_from(scene_cull_result_threads[i]); } } else { //single threaded - _frustum_cull(cull_data, frustum_cull_result, cull_from, cull_to); + _scene_cull(cull_data, scene_cull_result, cull_from, cull_to); } #ifdef DEBUG_CULL_TIME @@ -2626,9 +2930,9 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c print_line("time taken: " + rtos(time_avg / time_count)); #endif - if (frustum_cull_result.mesh_instances.size()) { - for (uint64_t i = 0; i < frustum_cull_result.mesh_instances.size(); i++) { - RSG::storage->mesh_instance_check_for_update(frustum_cull_result.mesh_instances[i]); + if (scene_cull_result.mesh_instances.size()) { + for (uint64_t i = 0; i < scene_cull_result.mesh_instances.size(); i++) { + RSG::storage->mesh_instance_check_for_update(scene_cull_result.mesh_instances[i]); } RSG::storage->update_mesh_instances(); } @@ -2652,14 +2956,14 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } render_shadow_data[max_shadows_used].light = cull.shadows[i].light_instance; render_shadow_data[max_shadows_used].pass = j; - render_shadow_data[max_shadows_used].instances.merge_unordered(frustum_cull_result.directional_shadows[i].cascade_geometry_instances[j]); + render_shadow_data[max_shadows_used].instances.merge_unordered(scene_cull_result.directional_shadows[i].cascade_geometry_instances[j]); max_shadows_used++; } } // Positional Shadowss - for (uint32_t i = 0; i < (uint32_t)frustum_cull_result.lights.size(); i++) { - Instance *ins = frustum_cull_result.lights[i]; + for (uint32_t i = 0; i < (uint32_t)scene_cull_result.lights.size(); i++) { + Instance *ins = scene_cull_result.lights[i]; if (!p_shadow_atlas.is_valid() || !RSG::storage->light_has_shadow(ins->base)) { continue; @@ -2763,13 +3067,13 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c if (cull.sdfgi.region_count > 0) { //update regions for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) { - render_sdfgi_data[i].instances.merge_unordered(frustum_cull_result.sdfgi_region_geometry_instances[i]); + render_sdfgi_data[i].instances.merge_unordered(scene_cull_result.sdfgi_region_geometry_instances[i]); render_sdfgi_data[i].region = i; } //check if static lights were culled bool static_lights_culled = false; for (uint32_t i = 0; i < cull.sdfgi.cascade_light_count; i++) { - if (frustum_cull_result.sdfgi_cascade_lights[i].size()) { + if (scene_cull_result.sdfgi_cascade_lights[i].size()) { static_lights_culled = true; break; } @@ -2778,7 +3082,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c if (static_lights_culled) { sdfgi_update_data.static_cascade_count = cull.sdfgi.cascade_light_count; sdfgi_update_data.static_cascade_indices = cull.sdfgi.cascade_light_index; - sdfgi_update_data.static_positional_lights = frustum_cull_result.sdfgi_cascade_lights; + sdfgi_update_data.static_positional_lights = scene_cull_result.sdfgi_cascade_lights; sdfgi_update_data.update_static = true; } } @@ -2792,7 +3096,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c //append the directional lights to the lights culled for (int i = 0; i < directional_lights.size(); i++) { - frustum_cull_result.light_instances.push_back(directional_lights[i]); + scene_cull_result.light_instances.push_back(directional_lights[i]); } RID camera_effects; @@ -2809,7 +3113,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } RENDER_TIMESTAMP("Render Scene "); - scene_render->render_scene(p_render_buffers, p_camera_data, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.voxel_gi_instances, frustum_cull_result.decals, frustum_cull_result.lightmaps, 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_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); + scene_render->render_scene(p_render_buffers, p_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, 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_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); for (uint32_t i = 0; i < max_shadows_used; i++) { render_shadow_data[i].instances.clear(); @@ -3144,7 +3448,7 @@ void RendererSceneCull::render_probes() { update_lights = true; } - frustum_cull_result.geometry_instances.clear(); + scene_cull_result.geometry_instances.clear(); RID instance_pair_buffer[MAX_INSTANCE_PAIRS]; @@ -3171,10 +3475,10 @@ void RendererSceneCull::render_probes() { ins->scenario->instance_data[ins->array_index].flags &= ~uint32_t(InstanceData::FLAG_GEOM_VOXEL_GI_DIRTY); } - frustum_cull_result.geometry_instances.push_back(geom->geometry_instance); + scene_cull_result.geometry_instances.push_back(geom->geometry_instance); } - scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, frustum_cull_result.geometry_instances); + scene_render->voxel_gi_update(probe->probe_instance, update_lights, probe->light_instances, scene_cull_result.geometry_instances); voxel_gi_update_list.remove(voxel_gi); @@ -3189,7 +3493,7 @@ void RendererSceneCull::render_particle_colliders() { if (hfpc->scenario && hfpc->base_type == RS::INSTANCE_PARTICLES_COLLISION && RSG::storage->particles_collision_is_heightfield(hfpc->base)) { //update heightfield instance_cull_result.clear(); - frustum_cull_result.geometry_instances.clear(); + scene_cull_result.geometry_instances.clear(); struct CullAABB { PagedArray *result; @@ -3211,10 +3515,10 @@ void RendererSceneCull::render_particle_colliders() { continue; } InstanceGeometryData *geom = static_cast(instance->base_data); - frustum_cull_result.geometry_instances.push_back(geom->geometry_instance); + scene_cull_result.geometry_instances.push_back(geom->geometry_instance); } - scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, frustum_cull_result.geometry_instances); + scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, scene_cull_result.geometry_instances); } heightfield_particle_colliders_update_list.erase(heightfield_particle_colliders_update_list.front()); } @@ -3503,6 +3807,7 @@ bool RendererSceneCull::free(RID p_rid) { } scenario->instance_aabbs.reset(); scenario->instance_data.reset(); + scenario->instance_visibility.reset(); scene_render->free(scenario->reflection_probe_shadow_atlas); scene_render->free(scenario->reflection_atlas); @@ -3571,10 +3876,10 @@ RendererSceneCull::RendererSceneCull() { render_sdfgi_data[i].instances.set_page_pool(&geometry_instance_cull_page_pool); } - frustum_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); - frustum_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); + scene_cull_result.init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); + scene_cull_result_threads.resize(RendererThreadPool::singleton->thread_work_pool.get_thread_count()); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].init(&rid_cull_page_pool, &geometry_instance_cull_page_pool, &instance_cull_page_pool); } indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame"); @@ -3595,11 +3900,11 @@ RendererSceneCull::~RendererSceneCull() { render_sdfgi_data[i].instances.reset(); } - frustum_cull_result.reset(); - for (uint32_t i = 0; i < frustum_cull_result_threads.size(); i++) { - frustum_cull_result_threads[i].reset(); + scene_cull_result.reset(); + for (uint32_t i = 0; i < scene_cull_result_threads.size(); i++) { + scene_cull_result_threads[i].reset(); } - frustum_cull_result_threads.clear(); + scene_cull_result_threads.clear(); if (dummy_occlusion_culling) { memdelete(dummy_occlusion_culling); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index e42d4bb46c..bdfbea95a2 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -31,6 +31,7 @@ #ifndef RENDERING_SERVER_SCENE_CULL_H #define RENDERING_SERVER_SCENE_CULL_H +#include "core/templates/bin_sorted_array.h" #include "core/templates/pass_func.h" #include "servers/rendering/renderer_compositor.h" @@ -259,6 +260,9 @@ public: FLAG_USES_MESH_INSTANCE = (1 << 17), FLAG_REFLECTION_PROBE_DIRTY = (1 << 18), FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19), + FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK = (3 << 20), // 2 bits, overlaps with the other vis. dependency flags + FLAG_VISIBILITY_DEPENDENCY_HIDDEN_CLOSE_RANGE = (1 << 20), + FLAG_VISIBILITY_DEPENDENCY_HIDDEN = (1 << 21), }; uint32_t flags = 0; @@ -269,10 +273,33 @@ public: RendererSceneRender::GeometryInstance *instance_geometry; }; Instance *instance = nullptr; + int32_t parent_array_index = -1; + int32_t visibility_index = -1; + }; + + struct InstanceVisibilityData { + uint64_t viewport_state = 0; + int32_t array_index = -1; + Vector3 position; + Instance *instance = nullptr; + float range_begin = 0.0f; + float range_end = 0.0f; + float range_begin_margin = 0.0f; + float range_end_margin = 0.0f; + }; + + class VisibilityArray : public BinSortedArray { + _FORCE_INLINE_ virtual void _update_idx(InstanceVisibilityData &r_element, uint64_t p_idx) { + r_element.instance->visibility_index = p_idx; + if (r_element.instance->scenario && r_element.instance->array_index != -1) { + r_element.instance->scenario->instance_data[r_element.instance->array_index].visibility_index = p_idx; + } + } }; PagedArrayPool instance_aabb_page_pool; PagedArrayPool instance_data_page_pool; + PagedArrayPool instance_visibility_data_page_pool; struct Scenario { enum IndexerType { @@ -292,6 +319,8 @@ public: RID camera_effects; RID reflection_probe_shadow_atlas; RID reflection_atlas; + uint64_t used_viewport_visibility_bits; + Map viewport_visibility_masks; SelfList::List instances; @@ -299,11 +328,13 @@ public: PagedArray instance_aabbs; PagedArray instance_data; + VisibilityArray instance_visibility; Scenario() { indexers[INDEXER_GEOMETRY].set_index(INDEXER_GEOMETRY); indexers[INDEXER_VOLUMES].set_index(INDEXER_VOLUMES); debug = RS::SCENARIO_DEBUG_DISABLED; + used_viewport_visibility_bits = 0; } }; @@ -326,6 +357,8 @@ public: 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; virtual RID scenario_get_environment(RID p_scenario); + virtual void scenario_add_viewport_visibility_mask(RID p_scenario, RID p_viewport); + virtual void scenario_remove_viewport_visibility_mask(RID p_scenario, RID p_viewport); /* INSTANCING API */ @@ -399,6 +432,12 @@ public: //scenario stuff DynamicBVH::ID indexer_id; int32_t array_index; + int32_t visibility_index = -1; + float visibility_range_begin; + float visibility_range_end; + float visibility_range_begin_margin; + float visibility_range_end_margin; + Instance *visibility_parent = nullptr; Scenario *scenario; SelfList scenario_item; @@ -412,12 +451,6 @@ public: float extra_margin; ObjectID object_id; - float lod_begin; - float lod_end; - float lod_begin_hysteresis; - float lod_end_hysteresis; - RID lod_instance; - Vector lightmap_target_sh; //target is used for incrementally changing the SH over time, this avoids pops in some corner cases and when going interior <-> exterior uint64_t last_frame_pass; @@ -495,10 +528,10 @@ public: visible = true; - lod_begin = 0; - lod_end = 0; - lod_begin_hysteresis = 0; - lod_end_hysteresis = 0; + visibility_range_begin = 0; + visibility_range_end = 0; + visibility_range_begin_margin = 0; + visibility_range_end_margin = 0; last_frame_pass = 0; version = 1; @@ -537,6 +570,8 @@ public: Set reflection_probes; Set voxel_gi_instances; Set lightmap_captures; + Set visibility_dependencies; + uint32_t visibility_dependencies_depth = 0; InstanceGeometryData() { can_cast_shadows = true; @@ -717,7 +752,7 @@ public: PagedArray instance_cull_result; PagedArray instance_shadow_cull_result; - struct FrustumCullResult { + struct InstanceCullResult { PagedArray geometry_instances; PagedArray lights; PagedArray light_instances; @@ -782,7 +817,7 @@ public: } } - void append_from(FrustumCullResult &p_cull_result) { + void append_from(InstanceCullResult &p_cull_result) { geometry_instances.merge_unordered(p_cull_result.geometry_instances); lights.merge_unordered(p_cull_result.lights); light_instances.merge_unordered(p_cull_result.light_instances); @@ -832,8 +867,8 @@ public: } }; - FrustumCullResult frustum_cull_result; - LocalVector frustum_cull_result_threads; + InstanceCullResult scene_cull_result; + LocalVector scene_cull_result_threads; RendererSceneRender::RenderShadowData render_shadow_data[MAX_UPDATE_SHADOWS]; uint32_t max_shadows_used = 0; @@ -866,6 +901,11 @@ public: virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin); + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance); + + void _update_instance_visibility_depth(Instance *p_instance); + void _update_instance_visibility_dependencies(Instance *p_instance); + // don't use these in a game! virtual Vector instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const; virtual Vector instances_cull_ray(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario = RID()) const; @@ -875,8 +915,8 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, RS::ShadowCastingSetting p_shadow_casting_setting); virtual void instance_geometry_set_material_override(RID p_instance, RID p_material); - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance); + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin); + virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_slice_index); virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias); @@ -937,6 +977,19 @@ public: Frustum frustum; } cull; + struct VisibilityCullData { + uint64_t viewport_mask; + Scenario *scenario; + Vector3 camera_position; + uint32_t cull_offset; + uint32_t cull_count; + }; + + void _visibility_cull_threaded(uint32_t p_thread, VisibilityCullData *cull_data); + void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_from, uint64_t p_to); + _FORCE_INLINE_ void _visibility_cull(const VisibilityCullData &cull_data, uint64_t p_idx); + _FORCE_INLINE_ int _visibility_range_check(InstanceVisibilityData &r_vis_data, const Vector3 &p_camera_pos, uint64_t p_viewport_mask); + struct CullData { Cull *cull; Scenario *scenario; @@ -946,10 +999,11 @@ public: Instance *render_reflection_probe; const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer; const CameraMatrix *camera_matrix; + const VisibilityCullData *visibility_cull_data; }; - void _frustum_cull_threaded(uint32_t p_thread, CullData *cull_data); - void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); + void _scene_cull_threaded(uint32_t p_thread, CullData *cull_data); + void _scene_cull(CullData &cull_data, InstanceCullResult &cull_result, uint64_t p_from, uint64_t p_to); 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_lod_threshold, bool p_using_shadows = true); diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 21f849e822..34bdb15c62 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -824,6 +824,10 @@ void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); + if (viewport->scenario.is_valid()) { + RSG::scene->scenario_remove_viewport_visibility_mask(viewport->scenario, p_viewport); + } + viewport->scenario = p_scenario; if (viewport->use_occlusion_culling) { RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 6476d7c75d..e4d319ed6c 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -726,6 +726,7 @@ public: FUNC2(instance_set_exterior, RID, bool) FUNC2(instance_set_extra_visibility_margin, RID, real_t) + FUNC2(instance_set_visibility_parent, RID, RID) // don't use these in a game! FUNC2RC(Vector, instances_cull_aabb, const AABB &, RID) @@ -736,8 +737,7 @@ public: FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting) FUNC2(instance_geometry_set_material_override, RID, RID) - FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float) - FUNC2(instance_geometry_set_as_instance_lod, RID, RID) + FUNC5(instance_geometry_set_visibility_range, RID, float, float, float, float) FUNC4(instance_geometry_set_lightmap, RID, RID, const Rect2 &, int) FUNC2(instance_geometry_set_lod_bias, RID, float) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 7b535a0bda..f25f255321 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1725,11 +1725,11 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_attach_skeleton", "instance", "skeleton"), &RenderingServer::instance_attach_skeleton); ClassDB::bind_method(D_METHOD("instance_set_exterior", "instance", "enabled"), &RenderingServer::instance_set_exterior); ClassDB::bind_method(D_METHOD("instance_set_extra_visibility_margin", "instance", "margin"), &RenderingServer::instance_set_extra_visibility_margin); + ClassDB::bind_method(D_METHOD("instance_set_visibility_parent", "instance", "parent"), &RenderingServer::instance_set_visibility_parent); ClassDB::bind_method(D_METHOD("instance_geometry_set_flag", "instance", "flag", "enabled"), &RenderingServer::instance_geometry_set_flag); ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &RenderingServer::instance_geometry_set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &RenderingServer::instance_geometry_set_material_override); - ClassDB::bind_method(D_METHOD("instance_geometry_set_draw_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_draw_range); - ClassDB::bind_method(D_METHOD("instance_geometry_set_as_instance_lod", "instance", "as_lod_of_instance"), &RenderingServer::instance_geometry_set_as_instance_lod); + ClassDB::bind_method(D_METHOD("instance_geometry_set_visibility_range", "instance", "min", "max", "min_margin", "max_margin"), &RenderingServer::instance_geometry_set_visibility_range); ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID())); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 3247717e98..1806f1da18 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1172,6 +1172,7 @@ public: virtual void instance_set_exterior(RID p_instance, bool p_enabled) = 0; virtual void instance_set_extra_visibility_margin(RID p_instance, real_t p_margin) = 0; + virtual void instance_set_visibility_parent(RID p_instance, RID p_parent_instance) = 0; // don't use these in a game! virtual Vector instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0; @@ -1201,8 +1202,7 @@ public: virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, ShadowCastingSetting p_shadow_casting_setting) = 0; virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0; - virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; - virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0; + virtual void instance_geometry_set_visibility_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0; virtual void instance_geometry_set_lightmap(RID p_instance, RID p_lightmap, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice) = 0; virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0;