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).
This commit is contained in:
jfons 2021-05-09 18:23:20 +02:00
parent 12e0f10c74
commit 3a53ae5d9f
15 changed files with 918 additions and 305 deletions

View file

@ -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 T>
class BinSortedArray {
PagedArray<T> array;
LocalVector<uint64_t> 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<T> *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

View file

@ -52,26 +52,22 @@
</member>
<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0">
</member>
<member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0">
The GeometryInstance3D's max LOD distance.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0">
The GeometryInstance3D's max LOD margin.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0">
The GeometryInstance3D's min LOD distance.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0">
The GeometryInstance3D's min LOD margin.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
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.
</member>
<member name="visibility_range_begin" type="float" setter="set_visibility_range_begin" getter="get_visibility_range_begin" default="0.0">
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.
</member>
<member name="visibility_range_begin_margin" type="float" setter="set_visibility_range_begin_margin" getter="get_visibility_range_begin_margin" default="0.0">
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.
</member>
<member name="visibility_range_end" type="float" setter="set_visibility_range_end" getter="get_visibility_range_end" default="0.0">
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..
</member>
<member name="visibility_range_end_margin" type="float" setter="set_visibility_range_end_margin" getter="get_visibility_range_end_margin" default="0.0">
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.
</member>
</members>
<constants>
<constant name="SHADOW_CASTING_SETTING_OFF" value="0" enum="ShadowCastingSetting">

View file

@ -310,6 +310,9 @@
<member name="transform" type="Transform3D" setter="set_transform" getter="get_transform" default="Transform3D( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
Local space [Transform3D] of this node, with respect to the parent node.
</member>
<member name="visibility_parent" type="NodePath" setter="set_visibility_parent" getter="get_visibility_parent" default="NodePath(&quot;&quot;)">
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.
</member>
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
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]).
</member>

View file

@ -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.
</description>
</method>
<method name="instance_geometry_set_as_instance_lod">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
<argument index="1" name="as_lod_of_instance" type="RID">
</argument>
<description>
Not implemented in Godot 3.x.
</description>
</method>
<method name="instance_geometry_set_cast_shadows_setting">
<return type="void">
</return>
@ -1197,23 +1186,6 @@
Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow].
</description>
</method>
<method name="instance_geometry_set_draw_range">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
<argument index="1" name="min" type="float">
</argument>
<argument index="2" name="max" type="float">
</argument>
<argument index="3" name="min_margin" type="float">
</argument>
<argument index="4" name="max_margin" type="float">
</argument>
<description>
Not implemented in Godot 3.x.
</description>
</method>
<method name="instance_geometry_set_flag">
<return type="void">
</return>
@ -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].
</description>
</method>
<method name="instance_geometry_set_visibility_range">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
<argument index="1" name="min" type="float">
</argument>
<argument index="2" name="max" type="float">
</argument>
<argument index="3" name="min_margin" type="float">
</argument>
<argument index="4" name="max_margin" type="float">
</argument>
<description>
Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties.
</description>
</method>
<method name="instance_set_base">
<return type="void">
</return>
@ -1341,6 +1330,17 @@
Sets the world space transform of the instance. Equivalent to [member Node3D.transform].
</description>
</method>
<method name="instance_set_visibility_parent">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
<argument index="1" name="parent" type="RID">
</argument>
<description>
Sets the visibility parent for the given instance. Equivalent to [member Node3D.visibility_parent].
</description>
</method>
<method name="instance_set_visible">
<return type="void">
</return>

View file

@ -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<GeometryInstance3D>(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<VisualInstance3D>(this);
if (vi) {
RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent);
}
for (List<Node3D *>::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"));

View file

@ -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<Node3D *> 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();
};

View file

@ -150,40 +150,40 @@ Ref<Material> 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"));

View file

@ -107,10 +107,13 @@ public:
private:
ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
Ref<Material> 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<NodePath> 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<Material> &p_material);
Ref<Material> get_material_override() const;

View file

@ -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<ObjectID> 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;

View file

@ -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<InstanceGeometryData *>(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<InstanceGeometryData *>(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<Instance *> traversed_nodes;
{
Instance *instance = p_instance;
while (instance && ((1 << instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) && instance->base_data) {
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
if (!geom->visibility_dependencies.is_empty()) {
uint32_t depth = 0;
for (Set<Instance *>::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<InstanceGeometryData *>(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<Instance *>::Element *E = traversed_nodes.front(); E; E = E->next()) {
Instance *instance = E->get();
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(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<InstanceGeometryData *>(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<InstanceGeometryData *>(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<InstanceGeometryData *>(p_instance->base_data)->geometry_instance;
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(p_instance->base_data);
idata.instance_geometry = geom->geometry_instance;
for (Set<Instance *>::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<InstanceLightData *>(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<InstanceGeometryData *>(swapped_instance->base_data);
for (Set<Instance *>::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<Instance *>::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<InstanceReflectionProbeData *>(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<InstanceReflectionProbeData *>(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<InstanceVoxelGIData *>(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<InstanceVoxelGIData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) {
InstanceLightData *light = static_cast<InstanceLightData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) {
InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) {
InstanceDecalData *decal = static_cast<InstanceDecalData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) {
InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(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<InstanceGeometryData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->lights.front(); E; E = E->next()) {
InstanceLightData *light = static_cast<InstanceLightData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->reflection_probes.front(); E; E = E->next()) {
InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->decals.front(); E; E = E->next()) {
InstanceDecalData *decal = static_cast<InstanceDecalData *>(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<InstanceGeometryData *>(idata.instance->base_data);
uint32_t idx = 0;
for (Set<Instance *>::Element *E = geom->voxel_gi_instances.front(); E; E = E->next()) {
InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(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<InstanceGeometryData *>(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<Instance *> *result;
@ -3211,10 +3515,10 @@ void RendererSceneCull::render_particle_colliders() {
continue;
}
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(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);

View file

@ -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<InstanceVisibilityData> {
_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<InstanceBounds> instance_aabb_page_pool;
PagedArrayPool<InstanceData> instance_data_page_pool;
PagedArrayPool<InstanceVisibilityData> 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<RID, uint64_t> viewport_visibility_masks;
SelfList<Instance>::List instances;
@ -299,11 +328,13 @@ public:
PagedArray<InstanceBounds> instance_aabbs;
PagedArray<InstanceData> 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<Instance> 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<Color> 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<Instance *> reflection_probes;
Set<Instance *> voxel_gi_instances;
Set<Instance *> lightmap_captures;
Set<Instance *> visibility_dependencies;
uint32_t visibility_dependencies_depth = 0;
InstanceGeometryData() {
can_cast_shadows = true;
@ -717,7 +752,7 @@ public:
PagedArray<Instance *> instance_cull_result;
PagedArray<Instance *> instance_shadow_cull_result;
struct FrustumCullResult {
struct InstanceCullResult {
PagedArray<RendererSceneRender::GeometryInstance *> geometry_instances;
PagedArray<Instance *> lights;
PagedArray<RID> 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<FrustumCullResult> frustum_cull_result_threads;
InstanceCullResult scene_cull_result;
LocalVector<InstanceCullResult> 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<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const;
virtual Vector<ObjectID> 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);

View file

@ -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);

View file

@ -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<ObjectID>, 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)

View file

@ -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()));

View file

@ -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<ObjectID> 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;