Browse Source

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).
jfons 4 years ago
parent
commit
3a53ae5d9f

+ 181 - 0
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 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

+ 11 - 15
doc/classes/GeometryInstance3D.xml

@@ -52,25 +52,21 @@
 		</member>
 		</member>
 		<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0">
 		<member name="lod_bias" type="float" setter="set_lod_bias" getter="get_lod_bias" default="1.0">
 		</member>
 		</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 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>
-		<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 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>
-		<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 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>
-		<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 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>
-		<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 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>
 		</member>
 	</members>
 	</members>
 	<constants>
 	<constants>

+ 3 - 0
doc/classes/Node3D.xml

@@ -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 )">
 		<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.
 			Local space [Transform3D] of this node, with respect to the parent node.
 		</member>
 		</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">
 		<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]).
 			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>
 		</member>

+ 27 - 27
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.
 				Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="instance_geometry_set_cast_shadows_setting">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>
@@ -1197,45 +1186,45 @@
 				Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow].
 				Sets the shadow casting setting to one of [enum ShadowCastingSetting]. Equivalent to [member GeometryInstance3D.cast_shadow].
 			</description>
 			</description>
 		</method>
 		</method>
-		<method name="instance_geometry_set_draw_range">
+		<method name="instance_geometry_set_flag">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>
 			<argument index="0" name="instance" type="RID">
 			<argument index="0" name="instance" type="RID">
 			</argument>
 			</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 index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags">
 			</argument>
 			</argument>
-			<argument index="4" name="max_margin" type="float">
+			<argument index="2" name="enabled" type="bool">
 			</argument>
 			</argument>
 			<description>
 			<description>
-				Not implemented in Godot 3.x.
+				Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
 			</description>
 			</description>
 		</method>
 		</method>
-		<method name="instance_geometry_set_flag">
+		<method name="instance_geometry_set_material_override">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>
 			<argument index="0" name="instance" type="RID">
 			<argument index="0" name="instance" type="RID">
 			</argument>
 			</argument>
-			<argument index="1" name="flag" type="int" enum="RenderingServer.InstanceFlags">
-			</argument>
-			<argument index="2" name="enabled" type="bool">
+			<argument index="1" name="material" type="RID">
 			</argument>
 			</argument>
 			<description>
 			<description>
-				Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
+				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>
 			</description>
 		</method>
 		</method>
-		<method name="instance_geometry_set_material_override">
+		<method name="instance_geometry_set_visibility_range">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>
 			<argument index="0" name="instance" type="RID">
 			<argument index="0" name="instance" type="RID">
 			</argument>
 			</argument>
-			<argument index="1" name="material" type="RID">
+			<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>
 			</argument>
 			<description>
 			<description>
-				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.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="instance_set_base">
 		<method name="instance_set_base">
@@ -1341,6 +1330,17 @@
 				Sets the world space transform of the instance. Equivalent to [member Node3D.transform].
 				Sets the world space transform of the instance. Equivalent to [member Node3D.transform].
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="instance_set_visible">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>

+ 52 - 0
scene/3d/node_3d.cpp

@@ -32,6 +32,7 @@
 
 
 #include "core/config/engine.h"
 #include "core/config/engine.h"
 #include "core/object/message_queue.h"
 #include "core/object/message_queue.h"
+#include "scene/3d/visual_instance_3d.h"
 #include "scene/main/scene_tree.h"
 #include "scene/main/scene_tree.h"
 #include "scene/main/window.h"
 #include "scene/main/window.h"
 #include "scene/scene_string_names.h"
 #include "scene/scene_string_names.h"
@@ -148,6 +149,7 @@ void Node3D::_notification(int p_what) {
 			_notify_dirty();
 			_notify_dirty();
 
 
 			notification(NOTIFICATION_ENTER_WORLD);
 			notification(NOTIFICATION_ENTER_WORLD);
+			_update_visibility_parent(true);
 
 
 		} break;
 		} break;
 		case NOTIFICATION_EXIT_TREE: {
 		case NOTIFICATION_EXIT_TREE: {
@@ -161,6 +163,7 @@ void Node3D::_notification(int p_what) {
 			data.parent = nullptr;
 			data.parent = nullptr;
 			data.C = nullptr;
 			data.C = nullptr;
 			data.top_level_active = false;
 			data.top_level_active = false;
+			_update_visibility_parent(true);
 		} break;
 		} break;
 		case NOTIFICATION_ENTER_WORLD: {
 		case NOTIFICATION_ENTER_WORLD: {
 			data.inside_world = true;
 			data.inside_world = true;
@@ -690,6 +693,51 @@ void Node3D::force_update_transform() {
 	notification(NOTIFICATION_TRANSFORM_CHANGED);
 	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() {
 void Node3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
 	ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
 	ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_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("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);
 
 
 	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_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
 	ADD_GROUP("Visibility", "");
 	ADD_GROUP("Visibility", "");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
 	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_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo");
 
 
 	ADD_SIGNAL(MethodInfo("visibility_changed"));
 	ADD_SIGNAL(MethodInfo("visibility_changed"));

+ 10 - 0
scene/3d/node_3d.h

@@ -75,6 +75,8 @@ class Node3D : public Node {
 		bool top_level = false;
 		bool top_level = false;
 		bool inside_world = false;
 		bool inside_world = false;
 
 
+		RID visibility_parent;
+
 		int children_lock = 0;
 		int children_lock = 0;
 		Node3D *parent = nullptr;
 		Node3D *parent = nullptr;
 		List<Node3D *> children;
 		List<Node3D *> children;
@@ -95,12 +97,17 @@ class Node3D : public Node {
 
 
 	} data;
 	} data;
 
 
+	NodePath visibility_parent_path;
+
 	void _update_gizmo();
 	void _update_gizmo();
 	void _notify_dirty();
 	void _notify_dirty();
 	void _propagate_transform_changed(Node3D *p_origin);
 	void _propagate_transform_changed(Node3D *p_origin);
 
 
 	void _propagate_visibility_changed();
 	void _propagate_visibility_changed();
 
 
+	void _propagate_visibility_parent();
+	void _update_visibility_parent(bool p_update_root);
+
 protected:
 protected:
 	_FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; }
 	_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 force_update_transform();
 
 
+	void set_visibility_parent(const NodePath &p_path);
+	NodePath get_visibility_parent() const;
+
 	Node3D();
 	Node3D();
 };
 };
 
 

+ 33 - 33
scene/3d/visual_instance_3d.cpp

@@ -150,40 +150,40 @@ Ref<Material> GeometryInstance3D::get_material_override() const {
 	return material_override;
 	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) {
 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("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("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("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);
 	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_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_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"));
 	//ADD_SIGNAL( MethodInfo("visibility_changed"));
 
 

+ 18 - 12
scene/3d/visual_instance_3d.h

@@ -107,10 +107,13 @@ public:
 private:
 private:
 	ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
 	ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON;
 	Ref<Material> material_override;
 	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;
 	float lod_bias = 1.0;
 
 
@@ -136,17 +139,20 @@ public:
 	void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
 	void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
 	ShadowCastingSetting get_cast_shadows_setting() const;
 	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_visibility_range_end(float p_dist);
+	float get_visibility_range_end() const;
 
 
-	void set_lod_max_distance(float p_dist);
-	float get_lod_max_distance() const;
+	void set_visibility_range_begin_margin(float p_dist);
+	float get_visibility_range_begin_margin() const;
 
 
-	void set_lod_min_hysteresis(float p_dist);
-	float get_lod_min_hysteresis() const;
+	void set_visibility_range_end_margin(float p_dist);
+	float get_visibility_range_end_margin() const;
 
 
-	void set_lod_max_hysteresis(float p_dist);
-	float get_lod_max_hysteresis() const;
+	void set_visibility_range_parent(const Node *p_parent);
+	void clear_visibility_range_parent();
 
 
 	void set_material_override(const Ref<Material> &p_material);
 	void set_material_override(const Ref<Material> &p_material);
 	Ref<Material> get_material_override() const;
 	Ref<Material> get_material_override() const;

+ 4 - 2
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 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 bool is_scenario(RID p_scenario) const = 0;
 	virtual RID scenario_get_environment(RID p_scenario) = 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 RID instance_allocate() = 0;
 	virtual void instance_initialize(RID p_rid) = 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_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_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!
 	// don't use these in a game!
 	virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0;
 	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_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_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_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;
 	virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0;
 
 

+ 474 - 169
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_aabbs.set_page_pool(&instance_aabb_page_pool);
 	scenario->instance_data.set_page_pool(&instance_data_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);
 	RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid);
 
 
@@ -369,6 +370,37 @@ RID RendererSceneCull::scenario_get_environment(RID p_scenario) {
 	return scenario->environment;
 	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 */
 /* INSTANCING API */
 
 
 void RendererSceneCull::_instance_queue_update(Instance *p_instance, bool p_update_aabb, bool p_update_dependencies) {
 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_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::instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) {
+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) {
 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.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.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.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) {
 		switch (p_instance->base_type) {
 			case RS::INSTANCE_MESH:
 			case RS::INSTANCE_MESH:
 			case RS::INSTANCE_MULTIMESH:
 			case RS::INSTANCE_MULTIMESH:
 			case RS::INSTANCE_IMMEDIATE:
 			case RS::INSTANCE_IMMEDIATE:
 			case RS::INSTANCE_PARTICLES: {
 			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;
 			} break;
 			case RS::INSTANCE_LIGHT: {
 			case RS::INSTANCE_LIGHT: {
 				idata.instance_data_rid = static_cast<InstanceLightData *>(p_instance->base_data)->instance.get_id();
 				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_data.push_back(idata);
 		p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb));
 		p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb));
+		_update_instance_visibility_dependencies(p_instance);
 	} else {
 	} else {
 		if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
 		if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
 			p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY].update(p_instance->indexer_id, bvh_aabb);
 			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);
 		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
 	//move instance and repair
 	pair_pass++;
 	pair_pass++;
 
 
@@ -1486,9 +1666,24 @@ void RendererSceneCull::_unpair_instance(Instance *p_instance) {
 	//replace this by last
 	//replace this by last
 	int32_t swap_with_index = p_instance->scenario->instance_data.size() - 1;
 	int32_t swap_with_index = p_instance->scenario->instance_data.size() - 1;
 	if (swap_with_index != p_instance->array_index) {
 	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_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];
 		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
 	// 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_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_decal_instances(geom->geometry_instance, nullptr, 0);
 		scene_render->geometry_instance_pair_voxel_gi_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) {
 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
 #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 cull_total = cull_data->scenario->instance_data.size();
 	uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count();
 	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_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);
 	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();
 	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();
 	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++) {
 	for (uint64_t i = p_from; i < p_to; i++) {
 		bool mesh_visible = false;
 		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;
-
-			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
-				}
-
-			} else if (base_type == RS::INSTANCE_REFLECTION_PROBE) {
-				if (cull_data.render_reflection_probe != idata.instance) {
-					//avoid entering The Matrix
+		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 ((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();
+#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))
 
 
-						idata.flags &= ~uint32_t(InstanceData::FLAG_REFLECTION_PROBE_DIRTY);
+		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 (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));
+				} else if (base_type == RS::INSTANCE_REFLECTION_PROBE) {
+					if (cull_data.render_reflection_probe != idata.instance) {
+						//avoid entering The Matrix
 
 
-			} 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));
+						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);
+						}
 
 
-			} 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 (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));
+
+				} 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));
 
 
-				if (idata.flags & InstanceData::FLAG_REDRAW_IF_VISIBLE) {
-					RenderingServerDefault::redraw_request();
-				}
+				} 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 (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();
 						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;
+					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();
 						}
 						}
 					}
 					}
 
 
-					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_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;
+					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);
+						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;
+							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);
-				}
+						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;
+					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);
+						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;
+							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);
 					}
 					}
-					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);
+					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;
+							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);
-				}
+						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));
+					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;
 					}
 					}
-					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);
+					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;
+			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;
+						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++) {
 		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])) {
 			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;
 				uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK;
 
 
 				if (base_type == RS::INSTANCE_LIGHT) {
 				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)
 		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);
 	//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;
 		uint64_t cull_from = 0;
@@ -2603,19 +2907,19 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
 #endif
 #endif
 		if (cull_to > thread_cull_threshold) {
 		if (cull_to > thread_cull_threshold) {
 			//multiple threads
 			//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 {
 		} else {
 			//single threaded
 			//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
 #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));
 		print_line("time taken: " + rtos(time_avg / time_count));
 #endif
 #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();
 			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].light = cull.shadows[i].light_instance;
 				render_shadow_data[max_shadows_used].pass = j;
 				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++;
 				max_shadows_used++;
 			}
 			}
 		}
 		}
 
 
 		// Positional Shadowss
 		// 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)) {
 			if (!p_shadow_atlas.is_valid() || !RSG::storage->light_has_shadow(ins->base)) {
 				continue;
 				continue;
@@ -2763,13 +3067,13 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
 		if (cull.sdfgi.region_count > 0) {
 		if (cull.sdfgi.region_count > 0) {
 			//update regions
 			//update regions
 			for (uint32_t i = 0; i < cull.sdfgi.region_count; i++) {
 			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;
 				render_sdfgi_data[i].region = i;
 			}
 			}
 			//check if static lights were culled
 			//check if static lights were culled
 			bool static_lights_culled = false;
 			bool static_lights_culled = false;
 			for (uint32_t i = 0; i < cull.sdfgi.cascade_light_count; i++) {
 			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;
 					static_lights_culled = true;
 					break;
 					break;
 				}
 				}
@@ -2778,7 +3082,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
 			if (static_lights_culled) {
 			if (static_lights_culled) {
 				sdfgi_update_data.static_cascade_count = cull.sdfgi.cascade_light_count;
 				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_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;
 				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
 	//append the directional lights to the lights culled
 	for (int i = 0; i < directional_lights.size(); i++) {
 	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;
 	RID camera_effects;
@@ -2809,7 +3113,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c
 	}
 	}
 
 
 	RENDER_TIMESTAMP("Render Scene ");
 	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++) {
 	for (uint32_t i = 0; i < max_shadows_used; i++) {
 		render_shadow_data[i].instances.clear();
 		render_shadow_data[i].instances.clear();
@@ -3144,7 +3448,7 @@ void RendererSceneCull::render_probes() {
 			update_lights = true;
 			update_lights = true;
 		}
 		}
 
 
-		frustum_cull_result.geometry_instances.clear();
+		scene_cull_result.geometry_instances.clear();
 
 
 		RID instance_pair_buffer[MAX_INSTANCE_PAIRS];
 		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);
 				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);
 		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)) {
 		if (hfpc->scenario && hfpc->base_type == RS::INSTANCE_PARTICLES_COLLISION && RSG::storage->particles_collision_is_heightfield(hfpc->base)) {
 			//update heightfield
 			//update heightfield
 			instance_cull_result.clear();
 			instance_cull_result.clear();
-			frustum_cull_result.geometry_instances.clear();
+			scene_cull_result.geometry_instances.clear();
 
 
 			struct CullAABB {
 			struct CullAABB {
 				PagedArray<Instance *> *result;
 				PagedArray<Instance *> *result;
@@ -3211,10 +3515,10 @@ void RendererSceneCull::render_particle_colliders() {
 					continue;
 					continue;
 				}
 				}
 				InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
 				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());
 		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_aabbs.reset();
 		scenario->instance_data.reset();
 		scenario->instance_data.reset();
+		scenario->instance_visibility.reset();
 
 
 		scene_render->free(scenario->reflection_probe_shadow_atlas);
 		scene_render->free(scenario->reflection_probe_shadow_atlas);
 		scene_render->free(scenario->reflection_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);
 		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");
 	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();
 		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) {
 	if (dummy_occlusion_culling) {
 		memdelete(dummy_occlusion_culling);
 		memdelete(dummy_occlusion_culling);

+ 72 - 18
servers/rendering/renderer_scene_cull.h

@@ -31,6 +31,7 @@
 #ifndef RENDERING_SERVER_SCENE_CULL_H
 #ifndef RENDERING_SERVER_SCENE_CULL_H
 #define 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 "core/templates/pass_func.h"
 #include "servers/rendering/renderer_compositor.h"
 #include "servers/rendering/renderer_compositor.h"
 
 
@@ -259,6 +260,9 @@ public:
 			FLAG_USES_MESH_INSTANCE = (1 << 17),
 			FLAG_USES_MESH_INSTANCE = (1 << 17),
 			FLAG_REFLECTION_PROBE_DIRTY = (1 << 18),
 			FLAG_REFLECTION_PROBE_DIRTY = (1 << 18),
 			FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19),
 			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;
 		uint32_t flags = 0;
@@ -269,10 +273,33 @@ public:
 			RendererSceneRender::GeometryInstance *instance_geometry;
 			RendererSceneRender::GeometryInstance *instance_geometry;
 		};
 		};
 		Instance *instance = nullptr;
 		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<InstanceBounds> instance_aabb_page_pool;
 	PagedArrayPool<InstanceData> instance_data_page_pool;
 	PagedArrayPool<InstanceData> instance_data_page_pool;
+	PagedArrayPool<InstanceVisibilityData> instance_visibility_data_page_pool;
 
 
 	struct Scenario {
 	struct Scenario {
 		enum IndexerType {
 		enum IndexerType {
@@ -292,6 +319,8 @@ public:
 		RID camera_effects;
 		RID camera_effects;
 		RID reflection_probe_shadow_atlas;
 		RID reflection_probe_shadow_atlas;
 		RID reflection_atlas;
 		RID reflection_atlas;
+		uint64_t used_viewport_visibility_bits;
+		Map<RID, uint64_t> viewport_visibility_masks;
 
 
 		SelfList<Instance>::List instances;
 		SelfList<Instance>::List instances;
 
 
@@ -299,11 +328,13 @@ public:
 
 
 		PagedArray<InstanceBounds> instance_aabbs;
 		PagedArray<InstanceBounds> instance_aabbs;
 		PagedArray<InstanceData> instance_data;
 		PagedArray<InstanceData> instance_data;
+		VisibilityArray instance_visibility;
 
 
 		Scenario() {
 		Scenario() {
 			indexers[INDEXER_GEOMETRY].set_index(INDEXER_GEOMETRY);
 			indexers[INDEXER_GEOMETRY].set_index(INDEXER_GEOMETRY);
 			indexers[INDEXER_VOLUMES].set_index(INDEXER_VOLUMES);
 			indexers[INDEXER_VOLUMES].set_index(INDEXER_VOLUMES);
 			debug = RS::SCENARIO_DEBUG_DISABLED;
 			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 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 bool is_scenario(RID p_scenario) const;
 	virtual RID scenario_get_environment(RID p_scenario);
 	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 */
 	/* INSTANCING API */
 
 
@@ -399,6 +432,12 @@ public:
 		//scenario stuff
 		//scenario stuff
 		DynamicBVH::ID indexer_id;
 		DynamicBVH::ID indexer_id;
 		int32_t array_index;
 		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;
 		Scenario *scenario;
 		SelfList<Instance> scenario_item;
 		SelfList<Instance> scenario_item;
 
 
@@ -412,12 +451,6 @@ public:
 		float extra_margin;
 		float extra_margin;
 		ObjectID object_id;
 		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
 		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;
 		uint64_t last_frame_pass;
@@ -495,10 +528,10 @@ public:
 
 
 			visible = true;
 			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;
 			last_frame_pass = 0;
 			version = 1;
 			version = 1;
@@ -537,6 +570,8 @@ public:
 		Set<Instance *> reflection_probes;
 		Set<Instance *> reflection_probes;
 		Set<Instance *> voxel_gi_instances;
 		Set<Instance *> voxel_gi_instances;
 		Set<Instance *> lightmap_captures;
 		Set<Instance *> lightmap_captures;
+		Set<Instance *> visibility_dependencies;
+		uint32_t visibility_dependencies_depth = 0;
 
 
 		InstanceGeometryData() {
 		InstanceGeometryData() {
 			can_cast_shadows = true;
 			can_cast_shadows = true;
@@ -717,7 +752,7 @@ public:
 	PagedArray<Instance *> instance_cull_result;
 	PagedArray<Instance *> instance_cull_result;
 	PagedArray<Instance *> instance_shadow_cull_result;
 	PagedArray<Instance *> instance_shadow_cull_result;
 
 
-	struct FrustumCullResult {
+	struct InstanceCullResult {
 		PagedArray<RendererSceneRender::GeometryInstance *> geometry_instances;
 		PagedArray<RendererSceneRender::GeometryInstance *> geometry_instances;
 		PagedArray<Instance *> lights;
 		PagedArray<Instance *> lights;
 		PagedArray<RID> light_instances;
 		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);
 			geometry_instances.merge_unordered(p_cull_result.geometry_instances);
 			lights.merge_unordered(p_cull_result.lights);
 			lights.merge_unordered(p_cull_result.lights);
 			light_instances.merge_unordered(p_cull_result.light_instances);
 			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];
 	RendererSceneRender::RenderShadowData render_shadow_data[MAX_UPDATE_SHADOWS];
 	uint32_t max_shadows_used = 0;
 	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_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!
 	// 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_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;
 	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_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_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_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);
 	virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias);
 
 
@@ -937,6 +977,19 @@ public:
 		Frustum frustum;
 		Frustum frustum;
 	} cull;
 	} 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 {
 	struct CullData {
 		Cull *cull;
 		Cull *cull;
 		Scenario *scenario;
 		Scenario *scenario;
@@ -946,10 +999,11 @@ public:
 		Instance *render_reflection_probe;
 		Instance *render_reflection_probe;
 		const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer;
 		const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer;
 		const CameraMatrix *camera_matrix;
 		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);
 	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);
 	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);

+ 4 - 0
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);
 	Viewport *viewport = viewport_owner.getornull(p_viewport);
 	ERR_FAIL_COND(!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;
 	viewport->scenario = p_scenario;
 	if (viewport->use_occlusion_culling) {
 	if (viewport->use_occlusion_culling) {
 		RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario);
 		RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario);

+ 2 - 2
servers/rendering/rendering_server_default.h

@@ -726,6 +726,7 @@ public:
 	FUNC2(instance_set_exterior, RID, bool)
 	FUNC2(instance_set_exterior, RID, bool)
 
 
 	FUNC2(instance_set_extra_visibility_margin, RID, real_t)
 	FUNC2(instance_set_extra_visibility_margin, RID, real_t)
+	FUNC2(instance_set_visibility_parent, RID, RID)
 
 
 	// don't use these in a game!
 	// don't use these in a game!
 	FUNC2RC(Vector<ObjectID>, instances_cull_aabb, const AABB &, RID)
 	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_cast_shadows_setting, RID, ShadowCastingSetting)
 	FUNC2(instance_geometry_set_material_override, RID, RID)
 	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)
 	FUNC4(instance_geometry_set_lightmap, RID, RID, const Rect2 &, int)
 	FUNC2(instance_geometry_set_lod_bias, RID, float)
 	FUNC2(instance_geometry_set_lod_bias, RID, float)
 
 

+ 2 - 2
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_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_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_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_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_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_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_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()));
 	ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID()));

+ 2 - 2
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_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_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!
 	// don't use these in a game!
 	virtual Vector<ObjectID> instances_cull_aabb(const AABB &p_aabb, RID p_scenario = RID()) const = 0;
 	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_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_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_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;
 	virtual void instance_geometry_set_lod_bias(RID p_instance, float p_lod_bias) = 0;