Refactor VisibilityNotifier

* Works from RenderinServer
* Accurately tells when on or off-scren, its no longer approximate.
* VisibilityEnabler also simplified to use the process mode instead.
This commit is contained in:
reduz 2021-06-15 17:33:24 -03:00
parent badad53438
commit 38d164c74b
16 changed files with 251 additions and 669 deletions

View file

@ -1616,9 +1616,6 @@
<member name="rendering/xr/enabled" type="bool" setter="" getter="" default="false">
If [code]true[/code], XR support is enabled in Godot, this ensures required shaders are compiled.
</member>
<member name="world/2d/cell_size" type="int" setter="" getter="" default="100">
Cell size used for the 2D hash grid that [VisibilityNotifier2D] uses (in pixels).
</member>
</members>
<constants>
</constants>

View file

@ -1,79 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisibilityEnabler2D" inherits="VisibilityNotifier2D" version="4.0">
<brief_description>
Enables certain nodes only when approximately visible.
</brief_description>
<description>
The VisibilityEnabler2D will disable [RigidBody2D], [AnimationPlayer], and other nodes when they are not visible. It will only affect nodes with the same root node as the VisibilityEnabler2D, and the root node itself.
If you just want to receive notifications, use [VisibilityNotifier2D] instead.
[b]Note:[/b] For performance reasons, VisibilityEnabler2D uses an approximate heuristic with precision determined by [member ProjectSettings.world/2d/cell_size]. If you need precise visibility checking, use another method such as adding an [Area2D] node as a child of a [Camera2D] node.
[b]Note:[/b] VisibilityEnabler2D will not affect nodes added after scene initialization.
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_enabler_enabled" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="enabler" type="int" enum="VisibilityEnabler2D.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="VisibilityEnabler2D.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], [RigidBody2D] nodes will be paused.
<member name="enable_mode" type="int" setter="set_enable_mode" getter="get_enable_mode" enum="VisibilityEnabler2D.EnableMode" default="0">
</member>
<member name="pause_animated_sprites" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="true">
If [code]true[/code], [AnimatedSprite2D] nodes will be paused.
</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>
<member name="pause_particles" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="true">
If [code]true[/code], [GPUParticles2D] nodes will be paused.
</member>
<member name="physics_process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="false">
If [code]true[/code], the parent's [method Node._physics_process] will be stopped.
</member>
<member name="process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled" default="false">
If [code]true[/code], the parent's [method Node._process] will be stopped.
<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 [RigidBody2D] nodes.
<constant name="ENABLE_MODE_ALWAYS" value="1" enum="EnableMode">
</constant>
<constant name="ENABLER_PAUSE_PARTICLES" value="2" enum="Enabler">
This enabler will stop [GPUParticles2D] nodes.
</constant>
<constant name="ENABLER_PARENT_PROCESS" value="3" enum="Enabler">
This enabler will stop the parent's _process function.
</constant>
<constant name="ENABLER_PARENT_PHYSICS_PROCESS" value="4" enum="Enabler">
This enabler will stop the parent's _physics_process function.
</constant>
<constant name="ENABLER_PAUSE_ANIMATED_SPRITES" value="5" enum="Enabler">
This enabler will stop [AnimatedSprite2D] nodes animations.
</constant>
<constant name="ENABLER_MAX" value="6" 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,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisibilityNotifier2D" inherits="Node2D" version="4.0">
<brief_description>
Detects approximately when the node is visible on screen.
Detects when the node extents are visible on screen.
</brief_description>
<description>
The VisibilityNotifier2D detects when it is visible on the screen. It also notifies when its bounding rectangle enters or exits the screen or a viewport.
If you want nodes to be disabled automatically when they exit the screen, use [VisibilityEnabler2D] instead.
[b]Note:[/b] For performance reasons, VisibilityNotifier2D uses an approximate heuristic with precision determined by [member ProjectSettings.world/2d/cell_size]. If you need precise visibility checking, use another method such as adding an [Area2D] node as a child of a [Camera2D] node.
</description>
<tutorials>
<link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link>
@ -37,20 +36,6 @@
Emitted when the VisibilityNotifier2D exits the screen.
</description>
</signal>
<signal name="viewport_entered">
<argument index="0" name="viewport" type="Viewport">
</argument>
<description>
Emitted when the VisibilityNotifier2D enters a [Viewport]'s view.
</description>
</signal>
<signal name="viewport_exited">
<argument index="0" name="viewport" type="Viewport">
</argument>
<description>
Emitted when the VisibilityNotifier2D exits a [Viewport]'s view.
</description>
</signal>
</signals>
<constants>
</constants>

View file

@ -170,7 +170,6 @@ void AudioStreamPlayer2D::_notification(int p_what) {
//update anything related to position first, if possible of course
if (!output_ready.is_set()) {
List<Viewport *> viewports;
Ref<World2D> world_2d = get_world_2d();
ERR_FAIL_COND(world_2d.is_null());
@ -203,8 +202,9 @@ void AudioStreamPlayer2D::_notification(int p_what) {
break;
}
world_2d->get_viewport_list(&viewports);
for (List<Viewport *>::Element *E = viewports.front(); E; E = E->next()) {
const Set<Viewport *> viewports = world_2d->get_viewports();
for (Set<Viewport *>::Element *E = viewports.front(); E; E = E->next()) {
Viewport *vp = E->get();
if (vp->is_audio_listener_2d()) {
//compute matrix to convert to screen

View file

@ -48,46 +48,29 @@ bool VisibilityNotifier2D::_edit_use_rect() const {
}
#endif
void VisibilityNotifier2D::_enter_viewport(Viewport *p_viewport) {
ERR_FAIL_COND(viewports.has(p_viewport));
viewports.insert(p_viewport);
if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
void VisibilityNotifier2D::_visibility_enter() {
if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
return;
}
if (viewports.size() == 1) {
emit_signal(SceneStringNames::get_singleton()->screen_entered);
_screen_enter();
}
emit_signal(SceneStringNames::get_singleton()->viewport_entered, p_viewport);
on_screen = true;
emit_signal(SceneStringNames::get_singleton()->screen_entered);
_screen_enter();
}
void VisibilityNotifier2D::_exit_viewport(Viewport *p_viewport) {
ERR_FAIL_COND(!viewports.has(p_viewport));
viewports.erase(p_viewport);
if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
void VisibilityNotifier2D::_visibility_exit() {
if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
return;
}
emit_signal(SceneStringNames::get_singleton()->viewport_exited, p_viewport);
if (viewports.size() == 0) {
emit_signal(SceneStringNames::get_singleton()->screen_exited);
_screen_exit();
}
on_screen = false;
emit_signal(SceneStringNames::get_singleton()->screen_exited);
_screen_exit();
}
void VisibilityNotifier2D::set_rect(const Rect2 &p_rect) {
rect = p_rect;
if (is_inside_tree()) {
get_world_2d()->_update_notifier(this, get_global_transform().xform(rect));
if (Engine::get_singleton()->is_editor_hint()) {
update();
item_rect_changed();
}
RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibilityNotifier2D::_visibility_enter), callable_mp(this, &VisibilityNotifier2D::_visibility_exit));
}
}
@ -99,11 +82,8 @@ void VisibilityNotifier2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
//get_world_2d()->
get_world_2d()->_register_notifier(this, get_global_transform().xform(rect));
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
//get_world_2d()->
get_world_2d()->_update_notifier(this, get_global_transform().xform(rect));
on_screen = false;
RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibilityNotifier2D::_visibility_enter), callable_mp(this, &VisibilityNotifier2D::_visibility_exit));
} break;
case NOTIFICATION_DRAW: {
if (Engine::get_singleton()->is_editor_hint()) {
@ -111,13 +91,14 @@ void VisibilityNotifier2D::_notification(int p_what) {
}
} break;
case NOTIFICATION_EXIT_TREE: {
get_world_2d()->_remove_notifier(this);
on_screen = false;
RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), false, Rect2(), Callable(), Callable());
} break;
}
}
bool VisibilityNotifier2D::is_on_screen() const {
return viewports.size() > 0;
return on_screen;
}
void VisibilityNotifier2D::_bind_methods() {
@ -127,235 +108,106 @@ void VisibilityNotifier2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "rect"), "set_rect", "get_rect");
ADD_SIGNAL(MethodInfo("viewport_entered", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport")));
ADD_SIGNAL(MethodInfo("viewport_exited", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport")));
ADD_SIGNAL(MethodInfo("screen_entered"));
ADD_SIGNAL(MethodInfo("screen_exited"));
}
VisibilityNotifier2D::VisibilityNotifier2D() {
rect = Rect2(-10, -10, 20, 20);
set_notify_transform(true);
}
//////////////////////////////////////
void VisibilityEnabler2D::_screen_enter() {
for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
_change_node_state(E->key(), true);
}
if (enabler[ENABLER_PARENT_PHYSICS_PROCESS] && get_parent()) {
get_parent()->set_physics_process(true);
}
if (enabler[ENABLER_PARENT_PROCESS] && get_parent()) {
get_parent()->set_process(true);
}
visible = true;
_update_enable_mode(true);
}
void VisibilityEnabler2D::_screen_exit() {
for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) {
_change_node_state(E->key(), false);
}
if (enabler[ENABLER_PARENT_PHYSICS_PROCESS] && get_parent()) {
get_parent()->set_physics_process(false);
}
if (enabler[ENABLER_PARENT_PROCESS] && get_parent()) {
get_parent()->set_process(false);
}
visible = false;
_update_enable_mode(false);
}
void VisibilityEnabler2D::_find_nodes(Node *p_node) {
bool add = false;
Variant meta;
{
RigidBody2D *rb2d = Object::cast_to<RigidBody2D>(p_node);
if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_DYNAMIC || rb2d->get_mode() == RigidBody2D::MODE_DYNAMIC_LOCKED))) {
add = true;
meta = rb2d->get_mode();
}
}
{
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
if (ap) {
add = true;
}
}
{
AnimatedSprite2D *as = Object::cast_to<AnimatedSprite2D>(p_node);
if (as) {
add = true;
}
}
{
GPUParticles2D *ps = Object::cast_to<GPUParticles2D>(p_node);
if (ps) {
add = true;
}
}
if (add) {
p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler2D::_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 VisibilityEnabler2D::set_enable_mode(EnableMode p_mode) {
enable_mode = p_mode;
if (is_inside_tree()) {
_update_enable_mode(is_on_screen());
}
}
VisibilityEnabler2D::EnableMode VisibilityEnabler2D::get_enable_mode() {
return enable_mode;
}
void VisibilityEnabler2D::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 VisibilityEnabler2D::get_enable_node_path() {
return enable_node_path;
}
void VisibilityEnabler2D::_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 VisibilityEnabler2D::_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();
}
_find_nodes(from);
// We need to defer the call of set_process and set_physics_process,
// otherwise they are overwritten inside NOTIFICATION_READY.
// We can't use call_deferred, because it happens after a physics frame.
// The ready signal works as it's emitted immediately after NOTIFICATION_READY.
if (enabler[ENABLER_PARENT_PHYSICS_PROCESS] && get_parent()) {
get_parent()->connect(SceneStringNames::get_singleton()->ready,
callable_mp(get_parent(), &Node::set_physics_process), varray(false), CONNECT_ONESHOT);
}
if (enabler[ENABLER_PARENT_PROCESS] && get_parent()) {
get_parent()->connect(SceneStringNames::get_singleton()->ready,
callable_mp(get_parent(), &Node::set_process), varray(false), CONNECT_ONESHOT);
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);
}
}
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, &VisibilityEnabler2D::_node_removed));
}
nodes.clear();
node_id = ObjectID();
}
}
void VisibilityEnabler2D::_change_node_state(Node *p_node, bool p_enabled) {
ERR_FAIL_COND(!nodes.has(p_node));
if (enabler[ENABLER_FREEZE_BODIES]) {
RigidBody2D *rb = Object::cast_to<RigidBody2D>(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);
}
}
if (enabler[ENABLER_PAUSE_ANIMATED_SPRITES]) {
AnimatedSprite2D *as = Object::cast_to<AnimatedSprite2D>(p_node);
if (as) {
if (p_enabled) {
as->play();
} else {
as->stop();
}
}
}
if (enabler[ENABLER_PAUSE_PARTICLES]) {
GPUParticles2D *ps = Object::cast_to<GPUParticles2D>(p_node);
if (ps) {
ps->set_emitting(p_enabled);
}
}
}
void VisibilityEnabler2D::_node_removed(Node *p_node) {
if (!visible) {
_change_node_state(p_node, true);
}
nodes.erase(p_node);
}
TypedArray<String> VisibilityEnabler2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
#ifdef TOOLS_ENABLED
if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) {
warnings.push_back(TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent."));
}
#endif
return warnings;
}
void VisibilityEnabler2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabler", "enabler", "enabled"), &VisibilityEnabler2D::set_enabler);
ClassDB::bind_method(D_METHOD("is_enabler_enabled", "enabler"), &VisibilityEnabler2D::is_enabler_enabled);
ClassDB::bind_method(D_METHOD("_node_removed"), &VisibilityEnabler2D::_node_removed);
ClassDB::bind_method(D_METHOD("set_enable_mode", "mode"), &VisibilityEnabler2D::set_enable_mode);
ClassDB::bind_method(D_METHOD("get_enable_mode"), &VisibilityEnabler2D::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);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "pause_particles"), "set_enabler", "is_enabler_enabled", ENABLER_PAUSE_PARTICLES);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "pause_animated_sprites"), "set_enabler", "is_enabler_enabled", ENABLER_PAUSE_ANIMATED_SPRITES);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "process_parent"), "set_enabler", "is_enabler_enabled", ENABLER_PARENT_PROCESS);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "physics_process_parent"), "set_enabler", "is_enabler_enabled", ENABLER_PARENT_PHYSICS_PROCESS);
ClassDB::bind_method(D_METHOD("set_enable_node_path", "path"), &VisibilityEnabler2D::set_enable_node_path);
ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibilityEnabler2D::get_enable_node_path);
BIND_ENUM_CONSTANT(ENABLER_PAUSE_ANIMATIONS);
BIND_ENUM_CONSTANT(ENABLER_FREEZE_BODIES);
BIND_ENUM_CONSTANT(ENABLER_PAUSE_PARTICLES);
BIND_ENUM_CONSTANT(ENABLER_PARENT_PROCESS);
BIND_ENUM_CONSTANT(ENABLER_PARENT_PHYSICS_PROCESS);
BIND_ENUM_CONSTANT(ENABLER_PAUSE_ANIMATED_SPRITES);
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 VisibilityEnabler2D::set_enabler(Enabler p_enabler, bool p_enable) {
ERR_FAIL_INDEX(p_enabler, ENABLER_MAX);
enabler[p_enabler] = p_enable;
}
bool VisibilityEnabler2D::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);
}
VisibilityEnabler2D::VisibilityEnabler2D() {
for (int i = 0; i < ENABLER_MAX; i++) {
enabler[i] = true;
}
enabler[ENABLER_PARENT_PROCESS] = false;
enabler[ENABLER_PARENT_PHYSICS_PROCESS] = false;
}

View file

@ -41,12 +41,12 @@ class VisibilityNotifier2D : public Node2D {
Rect2 rect;
private:
bool on_screen = false;
void _visibility_enter();
void _visibility_exit();
protected:
friend struct SpatialIndexer2D;
void _enter_viewport(Viewport *p_viewport);
void _exit_viewport(Viewport *p_viewport);
virtual void _screen_enter() {}
virtual void _screen_exit() {}
@ -71,42 +71,35 @@ class VisibilityEnabler2D : public VisibilityNotifier2D {
GDCLASS(VisibilityEnabler2D, VisibilityNotifier2D);
public:
enum Enabler {
ENABLER_PAUSE_ANIMATIONS,
ENABLER_FREEZE_BODIES,
ENABLER_PAUSE_PARTICLES,
ENABLER_PARENT_PROCESS,
ENABLER_PARENT_PHYSICS_PROCESS,
ENABLER_PAUSE_ANIMATED_SPRITES,
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();
public:
void set_enabler(Enabler p_enabler, bool p_enable);
bool is_enabler_enabled(Enabler p_enabler) const;
void _update_enable_mode(bool p_enable);
TypedArray<String> get_configuration_warnings() const override;
public:
void set_enable_mode(EnableMode p_mode);
EnableMode get_enable_mode();
void set_enable_node_path(NodePath p_path);
NodePath get_enable_node_path();
VisibilityEnabler2D();
};
VARIANT_ENUM_CAST(VisibilityEnabler2D::Enabler);
VARIANT_ENUM_CAST(VisibilityEnabler2D::EnableMode);
#endif // VISIBILITY_NOTIFIER_2D_H

View file

@ -188,11 +188,6 @@ void Viewport::update_worlds() {
return;
}
Rect2 abstracted_rect = Rect2(Vector2(), get_visible_rect().size);
Rect2 xformed_rect = (global_canvas_transform * canvas_transform).affine_inverse().xform(abstracted_rect);
find_world_2d()->_update_viewport(this, xformed_rect);
find_world_2d()->_update();
find_world_3d()->_update(get_tree()->get_frame());
}
@ -441,8 +436,6 @@ void Viewport::_notification(int p_what) {
_update_listener();
_update_listener_2d();
find_world_2d()->_register_viewport(this, Rect2());
add_to_group("_viewports");
if (get_tree()->is_debugging_collisions_hint()) {
//2D
@ -499,9 +492,6 @@ void Viewport::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
_gui_cancel_tooltip();
if (world_2d.is_valid()) {
world_2d->_remove_viewport(this);
}
RenderingServer::get_singleton()->viewport_set_scenario(viewport, RID());
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
@ -1156,7 +1146,6 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
}
if (is_inside_tree()) {
find_world_2d()->_remove_viewport(this);
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
}
@ -1172,7 +1161,6 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
if (is_inside_tree()) {
current_canvas = find_world_2d()->get_canvas();
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
find_world_2d()->_register_viewport(this, Rect2());
}
}

View file

@ -38,284 +38,6 @@
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
struct SpatialIndexer2D {
struct CellRef {
int ref = 0;
_FORCE_INLINE_ int inc() {
ref++;
return ref;
}
_FORCE_INLINE_ int dec() {
ref--;
return ref;
}
};
struct CellKey {
union {
struct {
int32_t x;
int32_t y;
};
uint64_t key = 0;
};
bool operator==(const CellKey &p_key) const { return key == p_key.key; }
_FORCE_INLINE_ bool operator<(const CellKey &p_key) const {
return key < p_key.key;
}
};
struct CellData {
Map<VisibilityNotifier2D *, CellRef> notifiers;
};
Map<CellKey, CellData> cells;
int cell_size;
Map<VisibilityNotifier2D *, Rect2> notifiers;
struct ViewportData {
Map<VisibilityNotifier2D *, uint64_t> notifiers;
Rect2 rect;
};
Map<Viewport *, ViewportData> viewports;
bool changed = false;
uint64_t pass = 0;
void _notifier_update_cells(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect, bool p_add) {
Point2i begin = p_rect.position;
begin /= cell_size;
Point2i end = p_rect.position + p_rect.size;
end /= cell_size;
for (int i = begin.x; i <= end.x; i++) {
for (int j = begin.y; j <= end.y; j++) {
CellKey ck;
ck.x = i;
ck.y = j;
Map<CellKey, CellData>::Element *E = cells.find(ck);
if (p_add) {
if (!E) {
E = cells.insert(ck, CellData());
}
E->get().notifiers[p_notifier].inc();
} else {
ERR_CONTINUE(!E);
if (E->get().notifiers[p_notifier].dec() == 0) {
E->get().notifiers.erase(p_notifier);
if (E->get().notifiers.is_empty()) {
cells.erase(E);
}
}
}
}
}
}
void _notifier_add(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
ERR_FAIL_COND(notifiers.has(p_notifier));
notifiers[p_notifier] = p_rect;
_notifier_update_cells(p_notifier, p_rect, true);
changed = true;
}
void _notifier_update(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier);
ERR_FAIL_COND(!E);
if (E->get() == p_rect) {
return;
}
_notifier_update_cells(p_notifier, p_rect, true);
_notifier_update_cells(p_notifier, E->get(), false);
E->get() = p_rect;
changed = true;
}
void _notifier_remove(VisibilityNotifier2D *p_notifier) {
Map<VisibilityNotifier2D *, Rect2>::Element *E = notifiers.find(p_notifier);
ERR_FAIL_COND(!E);
_notifier_update_cells(p_notifier, E->get(), false);
notifiers.erase(p_notifier);
List<Viewport *> removed;
for (Map<Viewport *, ViewportData>::Element *F = viewports.front(); F; F = F->next()) {
Map<VisibilityNotifier2D *, 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_viewport(removed.front()->get());
removed.pop_front();
}
changed = true;
}
void _add_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
ERR_FAIL_COND(viewports.has(p_viewport));
ViewportData vd;
vd.rect = p_rect;
viewports[p_viewport] = vd;
changed = true;
}
void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
Map<Viewport *, ViewportData>::Element *E = viewports.find(p_viewport);
ERR_FAIL_COND(!E);
if (E->get().rect == p_rect) {
return;
}
E->get().rect = p_rect;
changed = true;
}
void _remove_viewport(Viewport *p_viewport) {
ERR_FAIL_COND(!viewports.has(p_viewport));
List<VisibilityNotifier2D *> removed;
for (Map<VisibilityNotifier2D *, uint64_t>::Element *E = viewports[p_viewport].notifiers.front(); E; E = E->next()) {
removed.push_back(E->key());
}
while (!removed.is_empty()) {
removed.front()->get()->_exit_viewport(p_viewport);
removed.pop_front();
}
viewports.erase(p_viewport);
}
void _update() {
if (!changed) {
return;
}
for (Map<Viewport *, ViewportData>::Element *E = viewports.front(); E; E = E->next()) {
Point2i begin = E->get().rect.position;
begin /= cell_size;
Point2i end = E->get().rect.position + E->get().rect.size;
end /= cell_size;
pass++;
List<VisibilityNotifier2D *> added;
List<VisibilityNotifier2D *> removed;
uint64_t visible_cells = (uint64_t)(end.x - begin.x) * (uint64_t)(end.y - begin.y);
if (visible_cells > 10000) {
//well you zoomed out a lot, it's your problem. To avoid freezing in the for loops below, we'll manually check cell by cell
for (Map<CellKey, CellData>::Element *F = cells.front(); F; F = F->next()) {
const CellKey &ck = F->key();
if (ck.x < begin.x || ck.x > end.x) {
continue;
}
if (ck.y < begin.y || ck.y > end.y) {
continue;
}
//notifiers in cell
for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) {
Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key());
if (!H) {
H = E->get().notifiers.insert(G->key(), pass);
added.push_back(G->key());
} else {
H->get() = pass;
}
}
}
} else {
//check cells in grid fashion
for (int i = begin.x; i <= end.x; i++) {
for (int j = begin.y; j <= end.y; j++) {
CellKey ck;
ck.x = i;
ck.y = j;
Map<CellKey, CellData>::Element *F = cells.find(ck);
if (!F) {
continue;
}
//notifiers in cell
for (Map<VisibilityNotifier2D *, CellRef>::Element *G = F->get().notifiers.front(); G; G = G->next()) {
Map<VisibilityNotifier2D *, uint64_t>::Element *H = E->get().notifiers.find(G->key());
if (!H) {
H = E->get().notifiers.insert(G->key(), pass);
added.push_back(G->key());
} else {
H->get() = pass;
}
}
}
}
}
for (Map<VisibilityNotifier2D *, 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_viewport(E->key());
added.pop_front();
}
while (!removed.is_empty()) {
E->get().notifiers.erase(removed.front()->get());
removed.front()->get()->_exit_viewport(E->key());
removed.pop_front();
}
}
changed = false;
}
SpatialIndexer2D() {
cell_size = GLOBAL_DEF("world/2d/cell_size", 100);
}
};
void World2D::_register_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
indexer->_add_viewport(p_viewport, p_rect);
}
void World2D::_update_viewport(Viewport *p_viewport, const Rect2 &p_rect) {
indexer->_update_viewport(p_viewport, p_rect);
}
void World2D::_remove_viewport(Viewport *p_viewport) {
indexer->_remove_viewport(p_viewport);
}
void World2D::_register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
indexer->_notifier_add(p_notifier, p_rect);
}
void World2D::_update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect) {
indexer->_notifier_update(p_notifier, p_rect);
}
void World2D::_remove_notifier(VisibilityNotifier2D *p_notifier) {
indexer->_notifier_remove(p_notifier);
}
void World2D::_update() {
indexer->_update();
}
RID World2D::get_canvas() const {
return canvas;
}
@ -328,12 +50,6 @@ RID World2D::get_navigation_map() const {
return navigation_map;
}
void World2D::get_viewport_list(List<Viewport *> *r_viewports) {
for (Map<Viewport *, SpatialIndexer2D::ViewportData>::Element *E = indexer->viewports.front(); E; E = E->next()) {
r_viewports->push_back(E->key());
}
}
void World2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas);
ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space);
@ -369,13 +85,10 @@ World2D::World2D() {
NavigationServer2D::get_singleton()->map_set_active(navigation_map, true);
NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10));
NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5));
indexer = memnew(SpatialIndexer2D);
}
World2D::~World2D() {
RenderingServer::get_singleton()->free(canvas);
PhysicsServer2D::get_singleton()->free(space);
NavigationServer2D::get_singleton()->free(navigation_map);
memdelete(indexer);
}

View file

@ -46,23 +46,15 @@ class World2D : public Resource {
RID space;
RID navigation_map;
SpatialIndexer2D *indexer;
Set<Viewport *> viewports;
protected:
static void _bind_methods();
friend class Viewport;
friend class VisibilityNotifier2D;
void _register_viewport(Viewport *p_viewport, const Rect2 &p_rect);
void _update_viewport(Viewport *p_viewport, const Rect2 &p_rect);
void _register_viewport(Viewport *p_viewport);
void _remove_viewport(Viewport *p_viewport);
void _register_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect);
void _update_notifier(VisibilityNotifier2D *p_notifier, const Rect2 &p_rect);
void _remove_notifier(VisibilityNotifier2D *p_notifier);
void _update();
public:
RID get_canvas() const;
RID get_space() const;
@ -70,7 +62,7 @@ public:
PhysicsDirectSpaceState2D *get_direct_space_state();
void get_viewport_list(List<Viewport *> *r_viewports);
_FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; }
World2D();
~World2D();

View file

@ -104,7 +104,7 @@ void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner, RID_PtrOwner<Rende
} while (ysort_owner && ysort_owner->sort_y);
}
void _attach_canvas_item_for_draw(RendererCanvasCull::Item *ci, RendererCanvasCull::Item *p_canvas_clip, RendererCanvasRender::Item **z_list, RendererCanvasRender::Item **z_last_list, const Transform2D &xform, const Rect2 &p_clip_rect, Rect2 global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool use_canvas_group, RendererCanvasRender::Item *canvas_group_from) {
void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *ci, RendererCanvasCull::Item *p_canvas_clip, RendererCanvasRender::Item **z_list, RendererCanvasRender::Item **z_last_list, const Transform2D &xform, const Rect2 &p_clip_rect, Rect2 global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool use_canvas_group, RendererCanvasRender::Item *canvas_group_from, const Transform2D &p_xform) {
if (ci->copy_back_buffer) {
ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).intersection(p_clip_rect);
}
@ -173,32 +173,44 @@ void _attach_canvas_item_for_draw(RendererCanvasCull::Item *ci, RendererCanvasCu
}
}
if (ci->update_when_visible) {
RenderingServerDefault::redraw_request();
}
if ((ci->commands != nullptr && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) {
if (((ci->commands != nullptr || ci->visibility_notifier) && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) {
//something to draw?
ci->final_transform = xform;
ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a);
ci->global_rect_cache = global_rect;
ci->global_rect_cache.position -= p_clip_rect.position;
ci->light_masked = false;
int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
if (z_last_list[zidx]) {
z_last_list[zidx]->next = ci;
z_last_list[zidx] = ci;
} else {
z_list[zidx] = ci;
z_last_list[zidx] = ci;
if (ci->update_when_visible) {
RenderingServerDefault::redraw_request();
}
ci->z_final = p_z;
if (ci->commands != nullptr) {
ci->final_transform = xform;
ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a);
ci->global_rect_cache = global_rect;
ci->global_rect_cache.position -= p_clip_rect.position;
ci->light_masked = false;
ci->next = nullptr;
int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
if (z_last_list[zidx]) {
z_last_list[zidx]->next = ci;
z_last_list[zidx] = ci;
} else {
z_list[zidx] = ci;
z_last_list[zidx] = ci;
}
ci->z_final = p_z;
ci->next = nullptr;
}
if (ci->visibility_notifier) {
if (!ci->visibility_notifier->visible_element.in_list()) {
visibility_notifier_list.add(&ci->visibility_notifier->visible_element);
ci->visibility_notifier->just_visible = true;
}
ci->visibility_notifier->visible_in_frame = RSG::rasterizer->get_frame_number();
}
}
}
@ -215,6 +227,13 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
Rect2 rect = ci->get_rect();
if (ci->visibility_notifier) {
if (ci->visibility_notifier->area.size != Vector2()) {
rect = rect.merge(ci->visibility_notifier->area);
}
}
Transform2D xform = ci->xform;
if (snapping_2d_transforms_to_pixel) {
xform.elements[2] = xform.elements[2].floor();
@ -289,7 +308,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
canvas_group_from = z_last_list[zidx];
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, z_list, z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
_attach_canvas_item_for_draw(ci, p_canvas_clip, z_list, z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from, xform);
}
} else {
RendererCanvasRender::Item *canvas_group_from = nullptr;
@ -305,7 +324,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
_cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, z_list, z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, z_list, z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
_attach_canvas_item_for_draw(ci, p_canvas_clip, z_list, z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from, xform);
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || use_canvas_group) {
continue;
@ -1095,6 +1114,26 @@ void RendererCanvasCull::canvas_item_set_use_parent_material(RID p_item, bool p_
canvas_item->use_parent_material = p_enable;
}
void RendererCanvasCull::canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callable, const Callable &p_exit_callable) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
if (p_enable) {
if (!canvas_item->visibility_notifier) {
canvas_item->visibility_notifier = visibility_notifier_allocator.alloc();
}
canvas_item->visibility_notifier->area = p_area;
canvas_item->visibility_notifier->enter_callable = p_enter_callable;
canvas_item->visibility_notifier->exit_callable = p_exit_callable;
} else {
if (canvas_item->visibility_notifier) {
visibility_notifier_allocator.free(canvas_item->visibility_notifier);
canvas_item->visibility_notifier = nullptr;
}
}
}
void RendererCanvasCull::canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin, bool p_fit_empty, float p_fit_margin, bool p_blur_mipmaps) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
@ -1477,6 +1516,44 @@ void RendererCanvasCull::canvas_item_set_default_texture_repeat(RID p_item, RS::
ci->texture_repeat = p_repeat;
}
void RendererCanvasCull::update_visibility_notifiers() {
SelfList<Item::VisibilityNotifierData> *E = visibility_notifier_list.first();
while (E) {
SelfList<Item::VisibilityNotifierData> *N = E->next();
Item::VisibilityNotifierData *visibility_notifier = E->self();
if (visibility_notifier->just_visible) {
visibility_notifier->just_visible = false;
if (!visibility_notifier->enter_callable.is_null()) {
if (RSG::threaded) {
visibility_notifier->enter_callable.call_deferred(nullptr, 0);
} else {
Callable::CallError ce;
Variant ret;
visibility_notifier->enter_callable.call(nullptr, 0, ret, ce);
}
}
} else {
if (visibility_notifier->visible_in_frame != RSG::rasterizer->get_frame_number()) {
visibility_notifier_list.remove(E);
if (!visibility_notifier->exit_callable.is_null()) {
if (RSG::threaded) {
visibility_notifier->exit_callable.call_deferred(nullptr, 0);
} else {
Callable::CallError ce;
Variant ret;
visibility_notifier->exit_callable.call(nullptr, 0, ret, ce);
}
}
}
}
E = N;
}
}
bool RendererCanvasCull::free(RID p_rid) {
if (canvas_owner.owns(p_rid)) {
Canvas *canvas = canvas_owner.getornull(p_rid);
@ -1531,6 +1608,10 @@ bool RendererCanvasCull::free(RID p_rid) {
canvas_item->child_items[i]->parent = RID();
}
if (canvas_item->visibility_notifier != nullptr) {
visibility_notifier_allocator.free(canvas_item->visibility_notifier);
}
/*
if (canvas_item->material) {
canvas_item->material->owners.erase(canvas_item);

View file

@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_CANVAS_CULL_H
#define RENDERING_SERVER_CANVAS_CULL_H
#include "core/templates/paged_allocator.h"
#include "renderer_compositor.h"
#include "renderer_viewport.h"
@ -55,6 +56,20 @@ public:
Vector<Item *> child_items;
struct VisibilityNotifierData {
Rect2 area;
Callable enter_callable;
Callable exit_callable;
bool just_visible = false;
uint64_t visible_in_frame = 0;
SelfList<VisibilityNotifierData> visible_element;
VisibilityNotifierData() :
visible_element(this) {
}
};
VisibilityNotifierData *visibility_notifier = nullptr;
Item() {
children_order_dirty = true;
E = nullptr;
@ -156,6 +171,11 @@ public:
bool sdf_used = false;
bool snapping_2d_transforms_to_pixel = false;
PagedAllocator<Item::VisibilityNotifierData> visibility_notifier_allocator;
SelfList<Item::VisibilityNotifierData>::List visibility_notifier_list;
_FORCE_INLINE_ void _attach_canvas_item_for_draw(Item *ci, Item *p_canvas_clip, RendererCanvasRender::Item **z_list, RendererCanvasRender::Item **z_last_list, const Transform2D &xform, const Rect2 &p_clip_rect, Rect2 global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool use_canvas_group, RendererCanvasRender::Item *canvas_group_from, const Transform2D &p_xform);
private:
void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **z_list, RendererCanvasRender::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool allow_y_sort);
@ -224,6 +244,8 @@ public:
void canvas_item_set_use_parent_material(RID p_item, bool p_enable);
void canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callable, const Callable &p_exit_callable);
void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
RID canvas_light_allocate();
@ -283,6 +305,8 @@ public:
void canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter);
void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat);
void update_visibility_notifiers();
bool free(RID p_rid);
RendererCanvasCull();
~RendererCanvasCull();

View file

@ -117,6 +117,8 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
_draw_margins();
RSG::rasterizer->end_frame(p_swap_buffers);
RSG::canvas->update_visibility_notifiers();
while (frame_drawn_callbacks.front()) {
Object *obj = ObjectDB::get_instance(frame_drawn_callbacks.front()->get().object);
if (obj) {
@ -396,6 +398,7 @@ RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
server_thread = 0;
}
RSG::threaded = p_create_thread;
RSG::canvas = memnew(RendererCanvasCull);
RSG::viewport = memnew(RendererViewport);
RendererSceneCull *sr = memnew(RendererSceneCull);

View file

@ -820,6 +820,8 @@ public:
FUNC2(canvas_item_set_use_parent_material, RID, bool)
FUNC5(canvas_item_set_visibility_notifier, RID, bool, const Rect2 &, const Callable &, const Callable &)
FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
FUNCRIDSPLIT(canvas_light)

View file

@ -30,6 +30,8 @@
#include "rendering_server_globals.h"
bool RenderingServerGlobals::threaded = false;
RendererStorage *RenderingServerGlobals::storage = nullptr;
RendererCanvasRender *RenderingServerGlobals::canvas_render = nullptr;
RendererCompositor *RenderingServerGlobals::rasterizer = nullptr;

View file

@ -41,6 +41,8 @@ class RendererScene;
class RenderingServerGlobals {
public:
static bool threaded;
static RendererStorage *storage;
static RendererCanvasRender *canvas_render;
static RendererCompositor *rasterizer;

View file

@ -1301,6 +1301,8 @@ public:
virtual void canvas_item_set_use_parent_material(RID p_item, bool p_enable) = 0;
virtual void canvas_item_set_visibility_notifier(RID p_item, bool p_enable, const Rect2 &p_area, const Callable &p_enter_callbable, const Callable &p_exit_callable) = 0;
enum CanvasGroupMode {
CANVAS_GROUP_MODE_DISABLED,
CANVAS_GROUP_MODE_OPAQUE,