Pārlūkot izejas kodu

Refactor VisibilityNotifier3D

* This is the 3D counterpart to #49632
* Implemented a bit different as 3D works using instancing

After merged, both 2D and 3D classes will most likely be renamed in a separate PR to DisplayNotifier2D/3D.
reduz 4 gadi atpakaļ
vecāks
revīzija
6e98c4cd50

+ 1 - 1
doc/classes/RenderingServer.xml

@@ -3702,7 +3702,7 @@
 		</constant>
 		<constant name="INSTANCE_OCCLUDER" value="11" enum="InstanceType">
 		</constant>
-		<constant name="INSTANCE_MAX" value="12" enum="InstanceType">
+		<constant name="INSTANCE_MAX" value="13" enum="InstanceType">
 			Represents the size of the [enum InstanceType] enum.
 		</constant>
 		<constant name="INSTANCE_GEOMETRY_MASK" value="30" enum="InstanceType">

+ 0 - 7
doc/classes/Viewport.xml

@@ -179,13 +179,6 @@
 			<description>
 			</description>
 		</method>
-		<method name="update_worlds">
-			<return type="void">
-			</return>
-			<description>
-				Forces update of the 2D and 3D worlds.
-			</description>
-		</method>
 		<method name="warp_mouse">
 			<return type="void">
 			</return>

+ 5 - 30
doc/classes/VisibilityEnabler3D.xml

@@ -12,44 +12,19 @@
 	<tutorials>
 	</tutorials>
 	<methods>
-		<method name="is_enabler_enabled" qualifiers="const">
-			<return type="bool">
-			</return>
-			<argument index="0" name="enabler" type="int" enum="VisibilityEnabler3D.Enabler">
-			</argument>
-			<description>
-				Returns whether the enabler identified by given [enum Enabler] constant is active.
-			</description>
-		</method>
-		<method name="set_enabler">
-			<return type="void">
-			</return>
-			<argument index="0" name="enabler" type="int" enum="VisibilityEnabler3D.Enabler">
-			</argument>
-			<argument index="1" name="enabled" type="bool">
-			</argument>
-			<description>
-				Sets active state of the enabler identified by given [enum Enabler] constant.
-			</description>
-		</method>
 	</methods>
 	<members>
-		<member name="freeze_bodies" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="true">
-			If [code]true[/code], [RigidBody3D] nodes will be paused.
+		<member name="enable_mode" type="int" setter="set_enable_mode" getter="get_enable_mode" enum="VisibilityEnabler3D.EnableMode" default="0">
 		</member>
-		<member name="pause_animations" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="true">
-			If [code]true[/code], [AnimationPlayer] nodes will be paused.
+		<member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath(&quot;..&quot;)">
 		</member>
 	</members>
 	<constants>
-		<constant name="ENABLER_PAUSE_ANIMATIONS" value="0" enum="Enabler">
-			This enabler will pause [AnimationPlayer] nodes.
+		<constant name="ENABLE_MODE_INHERIT" value="0" enum="EnableMode">
 		</constant>
-		<constant name="ENABLER_FREEZE_BODIES" value="1" enum="Enabler">
-			This enabler will freeze [RigidBody3D] nodes.
+		<constant name="ENABLE_MODE_ALWAYS" value="1" enum="EnableMode">
 		</constant>
-		<constant name="ENABLER_MAX" value="2" enum="Enabler">
-			Represents the size of the [enum Enabler] enum.
+		<constant name="ENABLE_MODE_WHEN_PAUSED" value="2" enum="EnableMode">
 		</constant>
 	</constants>
 </class>

+ 1 - 15
doc/classes/VisibilityNotifier3D.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisibilityNotifier3D" inherits="Node3D" version="4.0">
+<class name="VisibilityNotifier3D" inherits="VisualInstance3D" version="4.0">
 	<brief_description>
 		Detects approximately when the node is visible on screen.
 	</brief_description>
@@ -26,20 +26,6 @@
 		</member>
 	</members>
 	<signals>
-		<signal name="camera_entered">
-			<argument index="0" name="camera" type="Camera3D">
-			</argument>
-			<description>
-				Emitted when the VisibilityNotifier3D enters a [Camera3D]'s view.
-			</description>
-		</signal>
-		<signal name="camera_exited">
-			<argument index="0" name="camera" type="Camera3D">
-			</argument>
-			<description>
-				Emitted when the VisibilityNotifier3D exits a [Camera3D]'s view.
-			</description>
-		</signal>
 		<signal name="screen_entered">
 			<description>
 				Emitted when the VisibilityNotifier3D enters the screen.

+ 1 - 4
scene/3d/audio_stream_player_3d.cpp

@@ -404,10 +404,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 				break;
 			}
 
-			List<Camera3D *> cameras;
-			world_3d->get_camera_list(&cameras);
-
-			for (List<Camera3D *>::Element *E = cameras.front(); E; E = E->next()) {
+			for (const Set<Camera3D *>::Element *E = world_3d->get_cameras().front(); E; E = E->next()) {
 				Camera3D *camera = E->get();
 				Viewport *vp = camera->get_viewport();
 				if (!vp->is_audio_listener()) {

+ 0 - 4
scene/3d/camera_3d.cpp

@@ -93,10 +93,6 @@ void Camera3D::_update_camera() {
 	}
 
 	get_viewport()->_camera_transform_changed_notify();
-
-	if (get_world_3d().is_valid()) {
-		get_world_3d()->_update_camera(this);
-	}
 }
 
 void Camera3D::_notification(int p_what) {

+ 87 - 142
scene/3d/visibility_notifier_3d.cpp

@@ -36,27 +36,23 @@
 #include "scene/animation/animation_player.h"
 #include "scene/scene_string_names.h"
 
-void VisibilityNotifier3D::_enter_camera(Camera3D *p_camera) {
-	ERR_FAIL_COND(cameras.has(p_camera));
-	cameras.insert(p_camera);
-	if (cameras.size() == 1) {
-		emit_signal(SceneStringNames::get_singleton()->screen_entered);
-		_screen_enter();
+void VisibilityNotifier3D::_visibility_enter() {
+	if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
+		return;
 	}
 
-	emit_signal(SceneStringNames::get_singleton()->camera_entered, p_camera);
+	on_screen = true;
+	emit_signal(SceneStringNames::get_singleton()->screen_entered);
+	_screen_enter();
 }
-
-void VisibilityNotifier3D::_exit_camera(Camera3D *p_camera) {
-	ERR_FAIL_COND(!cameras.has(p_camera));
-	cameras.erase(p_camera);
-
-	emit_signal(SceneStringNames::get_singleton()->camera_exited, p_camera);
-	if (cameras.size() == 0) {
-		emit_signal(SceneStringNames::get_singleton()->screen_exited);
-
-		_screen_exit();
+void VisibilityNotifier3D::_visibility_exit() {
+	if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
+		return;
 	}
+
+	on_screen = false;
+	emit_signal(SceneStringNames::get_singleton()->screen_exited);
+	_screen_exit();
 }
 
 void VisibilityNotifier3D::set_aabb(const AABB &p_aabb) {
@@ -65,9 +61,7 @@ void VisibilityNotifier3D::set_aabb(const AABB &p_aabb) {
 	}
 	aabb = p_aabb;
 
-	if (is_inside_world()) {
-		get_world_3d()->_update_notifier(this, get_global_transform().xform(aabb));
-	}
+	RS::get_singleton()->visibility_notifier_set_aabb(get_base(), aabb);
 
 	update_gizmo();
 }
@@ -76,178 +70,129 @@ AABB VisibilityNotifier3D::get_aabb() const {
 	return aabb;
 }
 
-void VisibilityNotifier3D::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_WORLD: {
-			world = get_world_3d();
-			ERR_FAIL_COND(!world.is_valid());
-			world->_register_notifier(this, get_global_transform().xform(aabb));
-		} break;
-		case NOTIFICATION_TRANSFORM_CHANGED: {
-			world->_update_notifier(this, get_global_transform().xform(aabb));
-		} break;
-		case NOTIFICATION_EXIT_WORLD: {
-			ERR_FAIL_COND(!world.is_valid());
-			world->_remove_notifier(this);
-		} break;
-	}
+bool VisibilityNotifier3D::is_on_screen() const {
+	return on_screen;
 }
 
-bool VisibilityNotifier3D::is_on_screen() const {
-	return cameras.size() != 0;
+void VisibilityNotifier3D::_notification(int p_what) {
+	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_EXIT_TREE) {
+		on_screen = false;
+	}
 }
 
 void VisibilityNotifier3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibilityNotifier3D::set_aabb);
-	ClassDB::bind_method(D_METHOD("get_aabb"), &VisibilityNotifier3D::get_aabb);
 	ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibilityNotifier3D::is_on_screen);
 
 	ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb");
 
-	ADD_SIGNAL(MethodInfo("camera_entered", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D")));
-	ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D")));
 	ADD_SIGNAL(MethodInfo("screen_entered"));
 	ADD_SIGNAL(MethodInfo("screen_exited"));
 }
 
+Vector<Face3> VisibilityNotifier3D::get_faces(uint32_t p_usage_flags) const {
+	return Vector<Face3>();
+}
+
 VisibilityNotifier3D::VisibilityNotifier3D() {
-	set_notify_transform(true);
+	RID notifier = RS::get_singleton()->visibility_notifier_create();
+	RS::get_singleton()->visibility_notifier_set_aabb(notifier, aabb);
+	RS::get_singleton()->visibility_notifier_set_callbacks(notifier, callable_mp(this, &VisibilityNotifier3D::_visibility_enter), callable_mp(this, &VisibilityNotifier3D::_visibility_exit));
+	set_base(notifier);
 }
 
 //////////////////////////////////////
 
 void VisibilityEnabler3D::_screen_enter() {
-	for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
-		_change_node_state(E->key(), true);
-	}
-
-	visible = true;
+	_update_enable_mode(true);
 }
 
 void VisibilityEnabler3D::_screen_exit() {
-	for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
-		_change_node_state(E->key(), false);
-	}
-
-	visible = false;
+	_update_enable_mode(false);
 }
 
-void VisibilityEnabler3D::_find_nodes(Node *p_node) {
-	bool add = false;
-	Variant meta;
-
-	{
-		RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node);
-		if (rb && ((rb->get_mode() == RigidBody3D::MODE_DYNAMIC || rb->get_mode() == RigidBody3D::MODE_DYNAMIC_LOCKED))) {
-			add = true;
-			meta = rb->get_mode();
-		}
+void VisibilityEnabler3D::set_enable_mode(EnableMode p_mode) {
+	enable_mode = p_mode;
+	if (is_inside_tree()) {
+		_update_enable_mode(is_on_screen());
 	}
+}
+VisibilityEnabler3D::EnableMode VisibilityEnabler3D::get_enable_mode() {
+	return enable_mode;
+}
 
-	{
-		AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
-		if (ap) {
-			add = true;
-		}
+void VisibilityEnabler3D::set_enable_node_path(NodePath p_path) {
+	if (enable_node_path == p_path) {
+		return;
 	}
-
-	if (add) {
-		p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed), varray(p_node), CONNECT_ONESHOT);
-		nodes[p_node] = meta;
-		_change_node_state(p_node, false);
+	enable_node_path = p_path;
+	if (is_inside_tree()) {
+		node_id = ObjectID();
+		Node *node = get_node(enable_node_path);
+		if (node) {
+			node_id = node->get_instance_id();
+			_update_enable_mode(is_on_screen());
+		}
 	}
-
-	for (int i = 0; i < p_node->get_child_count(); i++) {
-		Node *c = p_node->get_child(i);
-		if (c->get_filename() != String()) {
-			continue; //skip, instance
+}
+NodePath VisibilityEnabler3D::get_enable_node_path() {
+	return enable_node_path;
+}
+
+void VisibilityEnabler3D::_update_enable_mode(bool p_enable) {
+	Node *node = static_cast<Node *>(ObjectDB::get_instance(node_id));
+	if (node) {
+		if (p_enable) {
+			switch (enable_mode) {
+				case ENABLE_MODE_INHERIT: {
+					node->set_process_mode(PROCESS_MODE_INHERIT);
+				} break;
+				case ENABLE_MODE_ALWAYS: {
+					node->set_process_mode(PROCESS_MODE_ALWAYS);
+				} break;
+				case ENABLE_MODE_WHEN_PAUSED: {
+					node->set_process_mode(PROCESS_MODE_WHEN_PAUSED);
+				} break;
+			}
+		} else {
+			node->set_process_mode(PROCESS_MODE_DISABLED);
 		}
-
-		_find_nodes(c);
 	}
 }
-
 void VisibilityEnabler3D::_notification(int p_what) {
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 		if (Engine::get_singleton()->is_editor_hint()) {
 			return;
 		}
 
-		Node *from = this;
-		//find where current scene starts
-		while (from->get_parent() && from->get_filename() == String()) {
-			from = from->get_parent();
+		node_id = ObjectID();
+		Node *node = get_node(enable_node_path);
+		if (node) {
+			node_id = node->get_instance_id();
+			node->set_process_mode(PROCESS_MODE_DISABLED);
 		}
-
-		_find_nodes(from);
 	}
 
 	if (p_what == NOTIFICATION_EXIT_TREE) {
-		if (Engine::get_singleton()->is_editor_hint()) {
-			return;
-		}
-
-		for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
-			if (!visible) {
-				_change_node_state(E->key(), true);
-			}
-			E->key()->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed));
-		}
-
-		nodes.clear();
-	}
-}
-
-void VisibilityEnabler3D::_change_node_state(Node *p_node, bool p_enabled) {
-	ERR_FAIL_COND(!nodes.has(p_node));
-
-	if (enabler[ENABLER_FREEZE_BODIES]) {
-		RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node);
-		if (rb) {
-			rb->set_sleeping(!p_enabled);
-		}
-	}
-
-	if (enabler[ENABLER_PAUSE_ANIMATIONS]) {
-		AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
-
-		if (ap) {
-			ap->set_active(p_enabled);
-		}
+		node_id = ObjectID();
 	}
 }
 
-void VisibilityEnabler3D::_node_removed(Node *p_node) {
-	if (!visible) {
-		_change_node_state(p_node, true);
-	}
-	nodes.erase(p_node);
-}
-
 void VisibilityEnabler3D::_bind_methods() {
-	ClassDB::bind_method(D_METHOD("set_enabler", "enabler", "enabled"), &VisibilityEnabler3D::set_enabler);
-	ClassDB::bind_method(D_METHOD("is_enabler_enabled", "enabler"), &VisibilityEnabler3D::is_enabler_enabled);
+	ClassDB::bind_method(D_METHOD("set_enable_mode", "mode"), &VisibilityEnabler3D::set_enable_mode);
+	ClassDB::bind_method(D_METHOD("get_enable_mode"), &VisibilityEnabler3D::get_enable_mode);
 
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "pause_animations"), "set_enabler", "is_enabler_enabled", ENABLER_PAUSE_ANIMATIONS);
-	ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "freeze_bodies"), "set_enabler", "is_enabler_enabled", ENABLER_FREEZE_BODIES);
+	ClassDB::bind_method(D_METHOD("set_enable_node_path", "path"), &VisibilityEnabler3D::set_enable_node_path);
+	ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibilityEnabler3D::get_enable_node_path);
 
-	BIND_ENUM_CONSTANT(ENABLER_PAUSE_ANIMATIONS);
-	BIND_ENUM_CONSTANT(ENABLER_FREEZE_BODIES);
-	BIND_ENUM_CONSTANT(ENABLER_MAX);
-}
+	ADD_GROUP("Enabling", "enable_");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path");
 
-void VisibilityEnabler3D::set_enabler(Enabler p_enabler, bool p_enable) {
-	ERR_FAIL_INDEX(p_enabler, ENABLER_MAX);
-	enabler[p_enabler] = p_enable;
-}
-
-bool VisibilityEnabler3D::is_enabler_enabled(Enabler p_enabler) const {
-	ERR_FAIL_INDEX_V(p_enabler, ENABLER_MAX, false);
-	return enabler[p_enabler];
+	BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT);
+	BIND_ENUM_CONSTANT(ENABLE_MODE_ALWAYS);
+	BIND_ENUM_CONSTANT(ENABLE_MODE_WHEN_PAUSED);
 }
 
 VisibilityEnabler3D::VisibilityEnabler3D() {
-	for (int i = 0; i < ENABLER_MAX; i++) {
-		enabler[i] = true;
-	}
 }

+ 26 - 27
scene/3d/visibility_notifier_3d.h

@@ -31,34 +31,34 @@
 #ifndef VISIBILITY_NOTIFIER_H
 #define VISIBILITY_NOTIFIER_H
 
-#include "scene/3d/node_3d.h"
+#include "scene/3d/visual_instance_3d.h"
 
 class World3D;
 class Camera3D;
-class VisibilityNotifier3D : public Node3D {
-	GDCLASS(VisibilityNotifier3D, Node3D);
-
-	Ref<World3D> world;
-	Set<Camera3D *> cameras;
+class VisibilityNotifier3D : public VisualInstance3D {
+	GDCLASS(VisibilityNotifier3D, VisualInstance3D);
 
 	AABB aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
 
+private:
+	bool on_screen = false;
+	void _visibility_enter();
+	void _visibility_exit();
+
 protected:
 	virtual void _screen_enter() {}
 	virtual void _screen_exit() {}
 
 	void _notification(int p_what);
 	static void _bind_methods();
-	friend struct SpatialIndexer;
-
-	void _enter_camera(Camera3D *p_camera);
-	void _exit_camera(Camera3D *p_camera);
 
 public:
 	void set_aabb(const AABB &p_aabb);
-	AABB get_aabb() const;
+	virtual AABB get_aabb() const override;
 	bool is_on_screen() const;
 
+	virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
+
 	VisibilityNotifier3D();
 };
 
@@ -66,36 +66,35 @@ class VisibilityEnabler3D : public VisibilityNotifier3D {
 	GDCLASS(VisibilityEnabler3D, VisibilityNotifier3D);
 
 public:
-	enum Enabler {
-		ENABLER_PAUSE_ANIMATIONS,
-		ENABLER_FREEZE_BODIES,
-		ENABLER_MAX
+	enum EnableMode {
+		ENABLE_MODE_INHERIT,
+		ENABLE_MODE_ALWAYS,
+		ENABLE_MODE_WHEN_PAUSED,
 	};
 
 protected:
+	ObjectID node_id;
 	virtual void _screen_enter() override;
 	virtual void _screen_exit() override;
 
-	bool visible = false;
-
-	void _find_nodes(Node *p_node);
-
-	Map<Node *, Variant> nodes;
-	void _node_removed(Node *p_node);
-	bool enabler[ENABLER_MAX];
-
-	void _change_node_state(Node *p_node, bool p_enabled);
+	EnableMode enable_mode = ENABLE_MODE_INHERIT;
+	NodePath enable_node_path = NodePath("..");
 
 	void _notification(int p_what);
 	static void _bind_methods();
 
+	void _update_enable_mode(bool p_enable);
+
 public:
-	void set_enabler(Enabler p_enabler, bool p_enable);
-	bool is_enabler_enabled(Enabler p_enabler) const;
+	void set_enable_mode(EnableMode p_mode);
+	EnableMode get_enable_mode();
+
+	void set_enable_node_path(NodePath p_path);
+	NodePath get_enable_node_path();
 
 	VisibilityEnabler3D();
 };
 
-VARIANT_ENUM_CAST(VisibilityEnabler3D::Enabler);
+VARIANT_ENUM_CAST(VisibilityEnabler3D::EnableMode);
 
 #endif // VISIBILITY_NOTIFIER_H

+ 0 - 2
scene/main/scene_tree.cpp

@@ -413,7 +413,6 @@ bool SceneTree::physics_process(float p_time) {
 	_flush_ugc();
 	MessageQueue::get_singleton()->flush(); //small little hack
 	flush_transform_notifications();
-	call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds");
 	root_lock--;
 
 	_flush_delete_queue();
@@ -445,7 +444,6 @@ bool SceneTree::process(float p_time) {
 	_flush_ugc();
 	MessageQueue::get_singleton()->flush(); //small little hack
 	flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications
-	call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds");
 
 	root_lock--;
 

+ 0 - 10
scene/main/viewport.cpp

@@ -183,14 +183,6 @@ public:
 
 /////////////////////////////////////
 
-void Viewport::update_worlds() {
-	if (!is_inside_tree()) {
-		return;
-	}
-
-	find_world_3d()->_update(get_tree()->get_frame());
-}
-
 void Viewport::_collision_object_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
 	Transform3D object_transform = p_object->get_global_transform();
 	Transform3D camera_transform = p_camera->get_global_transform();
@@ -3513,8 +3505,6 @@ void Viewport::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("input", "event", "in_local_coords"), &Viewport::input, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("unhandled_input", "event", "in_local_coords"), &Viewport::unhandled_input, DEFVAL(false));
 
-	ClassDB::bind_method(D_METHOD("update_worlds"), &Viewport::update_worlds);
-
 	ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d);
 	ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d);
 

+ 0 - 2
scene/main/viewport.h

@@ -397,8 +397,6 @@ private:
 
 	void _gui_input_event(Ref<InputEvent> p_event);
 
-	void update_worlds();
-
 	_FORCE_INLINE_ Transform2D _get_input_pre_xform() const;
 
 	Ref<InputEvent> _make_input_local(const Ref<InputEvent> &ev);

+ 2 - 209
scene/resources/world_3d.cpp

@@ -37,206 +37,15 @@
 #include "scene/scene_string_names.h"
 #include "servers/navigation_server_3d.h"
 
-struct SpatialIndexer {
-	Octree<VisibilityNotifier3D> octree;
-
-	struct NotifierData {
-		AABB aabb;
-		OctreeElementID id;
-	};
-
-	Map<VisibilityNotifier3D *, NotifierData> notifiers;
-	struct CameraData {
-		Map<VisibilityNotifier3D *, uint64_t> notifiers;
-	};
-
-	Map<Camera3D *, CameraData> cameras;
-
-	enum {
-		VISIBILITY_CULL_MAX = 32768
-	};
-
-	Vector<VisibilityNotifier3D *> cull;
-
-	bool changed;
-	uint64_t pass;
-	uint64_t last_frame;
-
-	void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-		ERR_FAIL_COND(notifiers.has(p_notifier));
-		notifiers[p_notifier].aabb = p_rect;
-		notifiers[p_notifier].id = octree.create(p_notifier, p_rect);
-		changed = true;
-	}
-
-	void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-		Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier);
-		ERR_FAIL_COND(!E);
-		if (E->get().aabb == p_rect) {
-			return;
-		}
-
-		E->get().aabb = p_rect;
-		octree.move(E->get().id, E->get().aabb);
-		changed = true;
-	}
-
-	void _notifier_remove(VisibilityNotifier3D *p_notifier) {
-		Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier);
-		ERR_FAIL_COND(!E);
-
-		octree.erase(E->get().id);
-		notifiers.erase(p_notifier);
-
-		List<Camera3D *> removed;
-		for (Map<Camera3D *, CameraData>::Element *F = cameras.front(); F; F = F->next()) {
-			Map<VisibilityNotifier3D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier);
-
-			if (G) {
-				F->get().notifiers.erase(G);
-				removed.push_back(F->key());
-			}
-		}
-
-		while (!removed.is_empty()) {
-			p_notifier->_exit_camera(removed.front()->get());
-			removed.pop_front();
-		}
-
-		changed = true;
-	}
-
-	void _add_camera(Camera3D *p_camera) {
-		ERR_FAIL_COND(cameras.has(p_camera));
-		CameraData vd;
-		cameras[p_camera] = vd;
-		changed = true;
-	}
-
-	void _update_camera(Camera3D *p_camera) {
-		Map<Camera3D *, CameraData>::Element *E = cameras.find(p_camera);
-		ERR_FAIL_COND(!E);
-		changed = true;
-	}
-
-	void _remove_camera(Camera3D *p_camera) {
-		ERR_FAIL_COND(!cameras.has(p_camera));
-		List<VisibilityNotifier3D *> removed;
-		for (Map<VisibilityNotifier3D *, uint64_t>::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) {
-			removed.push_back(E->key());
-		}
-
-		while (!removed.is_empty()) {
-			removed.front()->get()->_exit_camera(p_camera);
-			removed.pop_front();
-		}
-
-		cameras.erase(p_camera);
-	}
-
-	void _update(uint64_t p_frame) {
-		if (p_frame == last_frame) {
-			return;
-		}
-		last_frame = p_frame;
-
-		if (!changed) {
-			return;
-		}
-
-		for (Map<Camera3D *, CameraData>::Element *E = cameras.front(); E; E = E->next()) {
-			pass++;
-
-			Camera3D *c = E->key();
-
-			Vector<Plane> planes = c->get_frustum();
-
-			int culled = octree.cull_convex(planes, cull.ptrw(), cull.size());
-
-			VisibilityNotifier3D **ptr = cull.ptrw();
-
-			List<VisibilityNotifier3D *> added;
-			List<VisibilityNotifier3D *> removed;
-
-			for (int i = 0; i < culled; i++) {
-				//notifiers in frustum
-
-				Map<VisibilityNotifier3D *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]);
-				if (!H) {
-					E->get().notifiers.insert(ptr[i], pass);
-					added.push_back(ptr[i]);
-				} else {
-					H->get() = pass;
-				}
-			}
-
-			for (Map<VisibilityNotifier3D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) {
-				if (F->get() != pass) {
-					removed.push_back(F->key());
-				}
-			}
-
-			while (!added.is_empty()) {
-				added.front()->get()->_enter_camera(E->key());
-				added.pop_front();
-			}
-
-			while (!removed.is_empty()) {
-				E->get().notifiers.erase(removed.front()->get());
-				removed.front()->get()->_exit_camera(E->key());
-				removed.pop_front();
-			}
-		}
-		changed = false;
-	}
-
-	SpatialIndexer() {
-		pass = 0;
-		last_frame = 0;
-		changed = false;
-		cull.resize(VISIBILITY_CULL_MAX);
-	}
-};
-
 void World3D::_register_camera(Camera3D *p_camera) {
 #ifndef _3D_DISABLED
-	indexer->_add_camera(p_camera);
-#endif
-}
-
-void World3D::_update_camera(Camera3D *p_camera) {
-#ifndef _3D_DISABLED
-	indexer->_update_camera(p_camera);
+	cameras.insert(p_camera);
 #endif
 }
 
 void World3D::_remove_camera(Camera3D *p_camera) {
 #ifndef _3D_DISABLED
-	indexer->_remove_camera(p_camera);
-#endif
-}
-
-void World3D::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-#ifndef _3D_DISABLED
-	indexer->_notifier_add(p_notifier, p_rect);
-#endif
-}
-
-void World3D::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) {
-#ifndef _3D_DISABLED
-	indexer->_notifier_update(p_notifier, p_rect);
-#endif
-}
-
-void World3D::_remove_notifier(VisibilityNotifier3D *p_notifier) {
-#ifndef _3D_DISABLED
-	indexer->_notifier_remove(p_notifier);
-#endif
-}
-
-void World3D::_update(uint64_t p_frame) {
-#ifndef _3D_DISABLED
-	indexer->_update(p_frame);
+	cameras.erase(p_camera);
 #endif
 }
 
@@ -307,12 +116,6 @@ PhysicsDirectSpaceState3D *World3D::get_direct_space_state() {
 	return PhysicsServer3D::get_singleton()->space_get_direct_state(space);
 }
 
-void World3D::get_camera_list(List<Camera3D *> *r_cameras) {
-	for (Map<Camera3D *, SpatialIndexer::CameraData>::Element *E = indexer->cameras.front(); E; E = E->next()) {
-		r_cameras->push_back(E->key());
-	}
-}
-
 void World3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space);
 	ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map);
@@ -349,20 +152,10 @@ World3D::World3D() {
 	NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);
 	NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3));
 	NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3));
-
-#ifdef _3D_DISABLED
-	indexer = nullptr;
-#else
-	indexer = memnew(SpatialIndexer);
-#endif
 }
 
 World3D::~World3D() {
 	PhysicsServer3D::get_singleton()->free(space);
 	RenderingServer::get_singleton()->free(scenario);
 	NavigationServer3D::get_singleton()->free(navigation_map);
-
-#ifndef _3D_DISABLED
-	memdelete(indexer);
-#endif
 }

+ 4 - 10
scene/resources/world_3d.h

@@ -48,27 +48,21 @@ private:
 	RID space;
 	RID navigation_map;
 	RID scenario;
-	SpatialIndexer *indexer;
+
 	Ref<Environment> environment;
 	Ref<Environment> fallback_environment;
 	Ref<CameraEffects> camera_effects;
 
+	Set<Camera3D *> cameras;
+
 protected:
 	static void _bind_methods();
 
 	friend class Camera3D;
-	friend class VisibilityNotifier3D;
 
 	void _register_camera(Camera3D *p_camera);
-	void _update_camera(Camera3D *p_camera);
 	void _remove_camera(Camera3D *p_camera);
 
-	void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect);
-	void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect);
-	void _remove_notifier(VisibilityNotifier3D *p_notifier);
-	friend class Viewport;
-	void _update(uint64_t p_frame);
-
 public:
 	RID get_space() const;
 	RID get_navigation_map() const;
@@ -83,7 +77,7 @@ public:
 	void set_camera_effects(const Ref<CameraEffects> &p_camera_effects);
 	Ref<CameraEffects> get_camera_effects() const;
 
-	void get_camera_list(List<Camera3D *> *r_cameras);
+	_FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; }
 
 	PhysicsDirectSpaceState3D *get_direct_space_state();
 

+ 12 - 3
servers/rendering/rasterizer_dummy.h

@@ -621,9 +621,18 @@ public:
 	bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; }
 	RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override { return RID(); }
 
-	RID particles_collision_instance_create(RID p_collision) override { return RID(); };
-	void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override{};
-	void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override{};
+	RID particles_collision_instance_create(RID p_collision) override { return RID(); }
+	void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override {}
+	void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override {}
+
+	/* VISIBILITY NOTIFIER */
+	virtual RID visibility_notifier_allocate() override { return RID(); }
+	virtual void visibility_notifier_initialize(RID p_notifier) override {}
+	virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override {}
+	virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override {}
+
+	virtual AABB visibility_notifier_get_aabb(RID p_notifier) const override { return AABB(); }
+	virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override {}
 
 	/* GLOBAL VARIABLES */
 

+ 63 - 0
servers/rendering/renderer_rd/renderer_storage_rd.cpp

@@ -5535,6 +5535,59 @@ void RendererStorageRD::particles_collision_instance_set_active(RID p_collision_
 	pci->active = p_active;
 }
 
+/* VISIBILITY NOTIFIER */
+
+RID RendererStorageRD::visibility_notifier_allocate() {
+	return visibility_notifier_owner.allocate_rid();
+}
+void RendererStorageRD::visibility_notifier_initialize(RID p_notifier) {
+	visibility_notifier_owner.initialize_rid(p_notifier, VisibilityNotifier());
+}
+void RendererStorageRD::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) {
+	VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_notifier);
+	ERR_FAIL_COND(!vn);
+	vn->aabb = p_aabb;
+	vn->dependency.changed_notify(DEPENDENCY_CHANGED_AABB);
+}
+void RendererStorageRD::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) {
+	VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_notifier);
+	ERR_FAIL_COND(!vn);
+	vn->enter_callback = p_enter_callbable;
+	vn->exit_callback = p_exit_callable;
+}
+
+AABB RendererStorageRD::visibility_notifier_get_aabb(RID p_notifier) const {
+	const VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_notifier);
+	ERR_FAIL_COND_V(!vn, AABB());
+	return vn->aabb;
+}
+void RendererStorageRD::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) {
+	VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_notifier);
+	ERR_FAIL_COND(!vn);
+
+	if (p_enter) {
+		if (!vn->enter_callback.is_null()) {
+			if (p_deferred) {
+				vn->enter_callback.call_deferred(nullptr, 0);
+			} else {
+				Variant r;
+				Callable::CallError ce;
+				vn->enter_callback.call(nullptr, 0, r, ce);
+			}
+		}
+	} else {
+		if (!vn->exit_callback.is_null()) {
+			if (p_deferred) {
+				vn->exit_callback.call_deferred(nullptr, 0);
+			} else {
+				Variant r;
+				Callable::CallError ce;
+				vn->exit_callback.call(nullptr, 0, r, ce);
+			}
+		}
+	}
+}
+
 /* SKELETON API */
 
 RID RendererStorageRD::skeleton_allocate() {
@@ -7590,6 +7643,9 @@ void RendererStorageRD::base_update_dependency(RID p_base, DependencyTracker *p_
 	} else if (particles_collision_owner.owns(p_base)) {
 		ParticlesCollision *pc = particles_collision_owner.getornull(p_base);
 		p_instance->update_dependency(&pc->dependency);
+	} else if (visibility_notifier_owner.owns(p_base)) {
+		VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_base);
+		p_instance->update_dependency(&vn->dependency);
 	}
 }
 
@@ -7628,6 +7684,9 @@ RS::InstanceType RendererStorageRD::get_base_type(RID p_rid) const {
 	if (particles_collision_owner.owns(p_rid)) {
 		return RS::INSTANCE_PARTICLES_COLLISION;
 	}
+	if (visibility_notifier_owner.owns(p_rid)) {
+		return RS::INSTANCE_VISIBLITY_NOTIFIER;
+	}
 
 	return RS::INSTANCE_NONE;
 }
@@ -8704,6 +8763,10 @@ bool RendererStorageRD::free(RID p_rid) {
 		}
 		particles_collision->dependency.deleted_notify(p_rid);
 		particles_collision_owner.free(p_rid);
+	} else if (visibility_notifier_owner.owns(p_rid)) {
+		VisibilityNotifier *vn = visibility_notifier_owner.getornull(p_rid);
+		vn->dependency.deleted_notify(p_rid);
+		visibility_notifier_owner.free(p_rid);
 	} else if (particles_collision_instance_owner.owns(p_rid)) {
 		particles_collision_instance_owner.free(p_rid);
 	} else if (render_target_owner.owns(p_rid)) {

+ 19 - 0
servers/rendering/renderer_rd/renderer_storage_rd.h

@@ -947,6 +947,17 @@ private:
 
 	mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner;
 
+	/* visibility_notifier */
+
+	struct VisibilityNotifier {
+		AABB aabb;
+		Callable enter_callback;
+		Callable exit_callback;
+		Dependency dependency;
+	};
+
+	mutable RID_Owner<VisibilityNotifier> visibility_notifier_owner;
+
 	/* Skeleton */
 
 	struct Skeleton {
@@ -2253,6 +2264,14 @@ public:
 	virtual bool particles_collision_is_heightfield(RID p_particles_collision) const;
 	RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const;
 
+	virtual RID visibility_notifier_allocate();
+	virtual void visibility_notifier_initialize(RID p_notifier);
+	virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb);
+	virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable);
+
+	virtual AABB visibility_notifier_get_aabb(RID p_notifier) const;
+	virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred);
+
 	//used from 2D and 3D
 	virtual RID particles_collision_instance_create(RID p_collision);
 	virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform);

+ 1 - 0
servers/rendering/renderer_scene.h

@@ -207,6 +207,7 @@ public:
 
 	virtual void update() = 0;
 	virtual void render_probes() = 0;
+	virtual void update_visibility_notifiers() = 0;
 
 	virtual bool free(RID p_rid) = 0;
 

+ 45 - 0
servers/rendering/renderer_scene_cull.cpp

@@ -506,6 +506,9 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) {
 				InstanceParticlesCollisionData *collision = static_cast<InstanceParticlesCollisionData *>(instance->base_data);
 				RSG::storage->free(collision->instance);
 			} break;
+			case RS::INSTANCE_VISIBLITY_NOTIFIER: {
+				//none
+			} break;
 			case RS::INSTANCE_REFLECTION_PROBE: {
 				InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(instance->base_data);
 				scene_render->free(reflection_probe->instance);
@@ -615,6 +618,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) {
 				RSG::storage->particles_collision_instance_set_active(collision->instance, instance->visible);
 				instance->base_data = collision;
 			} break;
+			case RS::INSTANCE_VISIBLITY_NOTIFIER: {
+				InstanceVisibilityNotifierData *vnd = memnew(InstanceVisibilityNotifierData);
+				vnd->base = p_base;
+				instance->base_data = vnd;
+			} break;
 			case RS::INSTANCE_REFLECTION_PROBE: {
 				InstanceReflectionProbeData *reflection_probe = memnew(InstanceReflectionProbeData);
 				reflection_probe->owner = instance;
@@ -1549,6 +1557,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
 			case RS::INSTANCE_VOXEL_GI: {
 				idata.instance_data_rid = static_cast<InstanceVoxelGIData *>(p_instance->base_data)->probe_instance.get_id();
 			} break;
+			case RS::INSTANCE_VISIBLITY_NOTIFIER: {
+				idata.visibility_notifier = static_cast<InstanceVisibilityNotifierData *>(p_instance->base_data);
+			} break;
 			default: {
 			}
 		}
@@ -1758,6 +1769,9 @@ void RendererSceneCull::_update_instance_aabb(Instance *p_instance) {
 			new_aabb = RSG::storage->particles_collision_get_aabb(p_instance->base);
 
 		} break;
+		case RenderingServer::INSTANCE_VISIBLITY_NOTIFIER: {
+			new_aabb = RSG::storage->visibility_notifier_get_aabb(p_instance->base);
+		} break;
 		case RenderingServer::INSTANCE_LIGHT: {
 			new_aabb = RSG::storage->light_get_aabb(p_instance->base);
 
@@ -2613,6 +2627,15 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul
 
 				} else if (base_type == RS::INSTANCE_LIGHTMAP) {
 					cull_result.lightmaps.push_back(RID::from_uint64(idata.instance_data_rid));
+				} else if (base_type == RS::INSTANCE_VISIBLITY_NOTIFIER) {
+					InstanceVisibilityNotifierData *vnd = idata.visibility_notifier;
+					if (!vnd->list_element.in_list()) {
+						visible_notifier_list_lock.lock();
+						visible_notifier_list.add(&vnd->list_element);
+						visible_notifier_list_lock.unlock();
+						vnd->just_visible = true;
+					}
+					vnd->visible_in_frame = RSG::rasterizer->get_frame_number();
 				} else if (((1 << base_type) & RS::INSTANCE_GEOMETRY_MASK) && !(idata.flags & InstanceData::FLAG_CAST_SHADOWS_ONLY)) {
 					bool keep = true;
 
@@ -3849,6 +3872,28 @@ TypedArray<Image> RendererSceneCull::bake_render_uv2(RID p_base, const Vector<RI
 	return scene_render->bake_render_uv2(p_base, p_material_overrides, p_image_size);
 }
 
+void RendererSceneCull::update_visibility_notifiers() {
+	SelfList<InstanceVisibilityNotifierData> *E = visible_notifier_list.first();
+	while (E) {
+		SelfList<InstanceVisibilityNotifierData> *N = E->next();
+
+		InstanceVisibilityNotifierData *visibility_notifier = E->self();
+		if (visibility_notifier->just_visible) {
+			visibility_notifier->just_visible = false;
+
+			RSG::storage->visibility_notifier_call(visibility_notifier->base, true, RSG::threaded);
+		} else {
+			if (visibility_notifier->visible_in_frame != RSG::rasterizer->get_frame_number()) {
+				visible_notifier_list.remove(E);
+
+				RSG::storage->visibility_notifier_call(visibility_notifier->base, false, RSG::threaded);
+			}
+		}
+
+		E = N;
+	}
+}
+
 /*******************************/
 /* Passthrough to Scene Render */
 /*******************************/

+ 19 - 0
servers/rendering/renderer_scene_cull.h

@@ -118,6 +118,8 @@ public:
 	virtual void occluder_initialize(RID p_occluder);
 	virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices);
 
+	/* VISIBILITY NOTIFIER API */
+
 	RendererSceneOcclusionCull *dummy_occlusion_culling;
 
 	/* SCENARIO API */
@@ -243,6 +245,8 @@ public:
 		}
 	};
 
+	struct InstanceVisibilityNotifierData;
+
 	struct InstanceData {
 		// Store instance pointer as well as common instance processing information,
 		// to make processing more cache friendly.
@@ -271,6 +275,7 @@ public:
 		union {
 			uint64_t instance_data_rid;
 			RendererSceneRender::GeometryInstance *instance_geometry;
+			InstanceVisibilityNotifierData *visibility_notifier;
 		};
 		Instance *instance = nullptr;
 		int32_t parent_array_index = -1;
@@ -611,6 +616,18 @@ public:
 		RID instance;
 	};
 
+	struct InstanceVisibilityNotifierData : public InstanceBaseData {
+		bool just_visible = false;
+		uint64_t visible_in_frame = 0;
+		RID base;
+		SelfList<InstanceVisibilityNotifierData> list_element;
+		InstanceVisibilityNotifierData() :
+				list_element(this) {}
+	};
+
+	SpinLock visible_notifier_list_lock;
+	SelfList<InstanceVisibilityNotifierData>::List visible_notifier_list;
+
 	struct InstanceLightData : public InstanceBaseData {
 		RID instance;
 		uint64_t last_version;
@@ -1123,6 +1140,8 @@ public:
 
 	void set_scene_render(RendererSceneRender *p_scene_render);
 
+	virtual void update_visibility_notifiers();
+
 	RendererSceneCull();
 	virtual ~RendererSceneCull();
 };

+ 8 - 0
servers/rendering/renderer_storage.h

@@ -557,6 +557,14 @@ public:
 	virtual bool particles_collision_is_heightfield(RID p_particles_collision) const = 0;
 	virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const = 0;
 
+	virtual RID visibility_notifier_allocate() = 0;
+	virtual void visibility_notifier_initialize(RID p_notifier) = 0;
+	virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) = 0;
+	virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) = 0;
+
+	virtual AABB visibility_notifier_get_aabb(RID p_notifier) const = 0;
+	virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) = 0;
+
 	//used from 2D and 3D
 	virtual RID particles_collision_instance_create(RID p_collision) = 0;
 	virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) = 0;

+ 1 - 0
servers/rendering/rendering_server_default.cpp

@@ -118,6 +118,7 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
 	RSG::rasterizer->end_frame(p_swap_buffers);
 
 	RSG::canvas->update_visibility_notifiers();
+	RSG::scene->update_visibility_notifiers();
 
 	while (frame_drawn_callbacks.front()) {
 		Object *obj = ObjectDB::get_instance(frame_drawn_callbacks.front()->get().object);

+ 7 - 1
servers/rendering/rendering_server_default.h

@@ -529,6 +529,12 @@ public:
 	FUNC1(particles_collision_height_field_update, RID)
 	FUNC2(particles_collision_set_height_field_resolution, RID, ParticlesCollisionHeightfieldResolution)
 
+	/* VISIBILITY_NOTIFIER */
+
+	FUNCRIDSPLIT(visibility_notifier)
+	FUNC2(visibility_notifier_set_aabb, RID, const AABB &)
+	FUNC3(visibility_notifier_set_callbacks, RID, const Callable &, const Callable &)
+
 #undef server_name
 #undef ServerName
 //from now on, calls forwarded to this singleton
@@ -549,7 +555,7 @@ public:
 
 	/* OCCLUDER */
 	FUNCRIDSPLIT(occluder)
-	FUNC3(occluder_set_mesh, RID, const PackedVector3Array &, const PackedInt32Array &);
+	FUNC3(occluder_set_mesh, RID, const PackedVector3Array &, const PackedInt32Array &)
 
 #undef server_name
 #undef ServerName

+ 12 - 5
servers/rendering_server.h

@@ -722,6 +722,17 @@ public:
 
 	virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, ParticlesCollisionHeightfieldResolution p_resolution) = 0; //for SDF and vector field
 
+	/* VISIBILITY NOTIFIER API */
+
+	virtual RID visibility_notifier_create() = 0;
+	virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) = 0;
+	virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) = 0;
+
+	/* OCCLUDER API */
+
+	virtual RID occluder_create() = 0;
+	virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) = 0;
+
 	/* CAMERA API */
 
 	virtual RID camera_create() = 0;
@@ -734,11 +745,6 @@ public:
 	virtual void camera_set_camera_effects(RID p_camera, RID p_camera_effects) = 0;
 	virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0;
 
-	/* OCCLUDER API */
-
-	virtual RID occluder_create() = 0;
-	virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) = 0;
-
 	/* VIEWPORT TARGET API */
 
 	enum CanvasItemTextureFilter {
@@ -1148,6 +1154,7 @@ public:
 		INSTANCE_VOXEL_GI,
 		INSTANCE_LIGHTMAP,
 		INSTANCE_OCCLUDER,
+		INSTANCE_VISIBLITY_NOTIFIER,
 		INSTANCE_MAX,
 
 		INSTANCE_GEOMETRY_MASK = (1 << INSTANCE_MESH) | (1 << INSTANCE_MULTIMESH) | (1 << INSTANCE_IMMEDIATE) | (1 << INSTANCE_PARTICLES)