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.
This commit is contained in:
reduz 2021-06-16 15:43:02 -03:00
parent 341cb8da31
commit 6e98c4cd50
23 changed files with 318 additions and 476 deletions

View file

@ -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">

View file

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

View file

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

View file

@ -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.

View file

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

View file

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

View file

@ -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();
}
}
{
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
if (ap) {
add = true;
}
}
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);
}
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
}
_find_nodes(c);
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;
}
void VisibilityEnabler3D::set_enable_node_path(NodePath p_path) {
if (enable_node_path == p_path) {
return;
}
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());
}
}
}
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);
}
}
}
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();
node_id = ObjectID();
}
}
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);
}
}
}
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;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}

View file

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

View file

@ -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 */

View file

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

View file

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

View file

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

View file

@ -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 */
/*******************************/

View file

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

View file

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

View file

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

View file

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

View file

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