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:
parent
badad53438
commit
38d164c74b
16 changed files with 251 additions and 669 deletions
doc/classes
scene
2d
main
resources
servers
|
@ -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>
|
||||
|
|
|
@ -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("..")">
|
||||
</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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -41,6 +41,8 @@ class RendererScene;
|
|||
|
||||
class RenderingServerGlobals {
|
||||
public:
|
||||
static bool threaded;
|
||||
|
||||
static RendererStorage *storage;
|
||||
static RendererCanvasRender *canvas_render;
|
||||
static RendererCompositor *rasterizer;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue