diff options
Diffstat (limited to 'scene')
55 files changed, 1284 insertions, 1613 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 6d8d6058eb..127ef6762d 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -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 diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index e79dfb019c..e39c8841cd 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -711,12 +711,17 @@ Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() { void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); + + Set<Vector2i> coords; for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { TileSetSource *source = *tile_set->get_source(E->get().source_id); if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - set_cell(E->key(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + coords.insert(E->key()); } } + for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { + set_cell(E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + } } void TileMap::_recreate_quadrants() { diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp deleted file mode 100644 index c85b2c85a4..0000000000 --- a/scene/2d/visibility_notifier_2d.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/*************************************************************************/ -/* visibility_notifier_2d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "visibility_notifier_2d.h" - -#include "core/config/engine.h" -#include "gpu_particles_2d.h" -#include "scene/2d/animated_sprite_2d.h" -#include "scene/2d/physics_body_2d.h" -#include "scene/animation/animation_player.h" -#include "scene/main/window.h" -#include "scene/scene_string_names.h" - -#ifdef TOOLS_ENABLED -Rect2 VisibilityNotifier2D::_edit_get_rect() const { - return rect; -} - -bool VisibilityNotifier2D::_edit_use_rect() const { - return true; -} -#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()) { - return; - } - - if (viewports.size() == 1) { - emit_signal(SceneStringNames::get_singleton()->screen_entered); - - _screen_enter(); - } - emit_signal(SceneStringNames::get_singleton()->viewport_entered, p_viewport); -} - -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()) { - return; - } - - emit_signal(SceneStringNames::get_singleton()->viewport_exited, p_viewport); - if (viewports.size() == 0) { - 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(); - } - } -} - -Rect2 VisibilityNotifier2D::get_rect() const { - return rect; -} - -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)); - } break; - case NOTIFICATION_DRAW: { - if (Engine::get_singleton()->is_editor_hint()) { - draw_rect(rect, Color(1, 0.5, 1, 0.2)); - } - } break; - case NOTIFICATION_EXIT_TREE: { - get_world_2d()->_remove_notifier(this); - } break; - } -} - -bool VisibilityNotifier2D::is_on_screen() const { - return viewports.size() > 0; -} - -void VisibilityNotifier2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_rect", "rect"), &VisibilityNotifier2D::set_rect); - ClassDB::bind_method(D_METHOD("get_rect"), &VisibilityNotifier2D::get_rect); - ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibilityNotifier2D::is_on_screen); - - 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; -} - -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; -} - -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::_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); - } - } - - 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(); - } -} - -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); - - 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); - - 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); -} - -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]; -} - -VisibilityEnabler2D::VisibilityEnabler2D() { - for (int i = 0; i < ENABLER_MAX; i++) { - enabler[i] = true; - } - enabler[ENABLER_PARENT_PROCESS] = false; - enabler[ENABLER_PARENT_PHYSICS_PROCESS] = false; -} diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp new file mode 100644 index 0000000000..25237edacf --- /dev/null +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -0,0 +1,213 @@ +/*************************************************************************/ +/* visible_on_screen_notifier_2d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "visible_on_screen_notifier_2d.h" + +#include "core/config/engine.h" +#include "gpu_particles_2d.h" +#include "scene/2d/animated_sprite_2d.h" +#include "scene/2d/physics_body_2d.h" +#include "scene/animation/animation_player.h" +#include "scene/main/window.h" +#include "scene/scene_string_names.h" + +#ifdef TOOLS_ENABLED +Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const { + return rect; +} + +bool VisibleOnScreenNotifier2D::_edit_use_rect() const { + return true; +} +#endif + +void VisibleOnScreenNotifier2D::_visibility_enter() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = true; + emit_signal(SceneStringNames::get_singleton()->screen_entered); + _screen_enter(); +} +void VisibleOnScreenNotifier2D::_visibility_exit() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = false; + emit_signal(SceneStringNames::get_singleton()->screen_exited); + _screen_exit(); +} + +void VisibleOnScreenNotifier2D::set_rect(const Rect2 &p_rect) { + rect = p_rect; + if (is_inside_tree()) { + RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_exit)); + } +} + +Rect2 VisibleOnScreenNotifier2D::get_rect() const { + return rect; +} + +void VisibleOnScreenNotifier2D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + //get_world_2d()-> + on_screen = false; + RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), true, rect, callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier2D::_visibility_exit)); + } break; + case NOTIFICATION_DRAW: { + if (Engine::get_singleton()->is_editor_hint()) { + draw_rect(rect, Color(1, 0.5, 1, 0.2)); + } + } break; + case NOTIFICATION_EXIT_TREE: { + on_screen = false; + RS::get_singleton()->canvas_item_set_visibility_notifier(get_canvas_item(), false, Rect2(), Callable(), Callable()); + } break; + } +} + +bool VisibleOnScreenNotifier2D::is_on_screen() const { + return on_screen; +} + +void VisibleOnScreenNotifier2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_rect", "rect"), &VisibleOnScreenNotifier2D::set_rect); + ClassDB::bind_method(D_METHOD("get_rect"), &VisibleOnScreenNotifier2D::get_rect); + ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibleOnScreenNotifier2D::is_on_screen); + + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "rect"), "set_rect", "get_rect"); + + ADD_SIGNAL(MethodInfo("screen_entered")); + ADD_SIGNAL(MethodInfo("screen_exited")); +} + +VisibleOnScreenNotifier2D::VisibleOnScreenNotifier2D() { + rect = Rect2(-10, -10, 20, 20); +} + +////////////////////////////////////// + +void VisibleOnScreenEnabler2D::_screen_enter() { + _update_enable_mode(true); +} + +void VisibleOnScreenEnabler2D::_screen_exit() { + _update_enable_mode(false); +} + +void VisibleOnScreenEnabler2D::set_enable_mode(EnableMode p_mode) { + enable_mode = p_mode; + if (is_inside_tree()) { + _update_enable_mode(is_on_screen()); + } +} +VisibleOnScreenEnabler2D::EnableMode VisibleOnScreenEnabler2D::get_enable_mode() { + return enable_mode; +} + +void VisibleOnScreenEnabler2D::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 VisibleOnScreenEnabler2D::get_enable_node_path() { + return enable_node_path; +} + +void VisibleOnScreenEnabler2D::_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 VisibleOnScreenEnabler2D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + + 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) { + node_id = ObjectID(); + } +} + +void VisibleOnScreenEnabler2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enable_mode", "mode"), &VisibleOnScreenEnabler2D::set_enable_mode); + ClassDB::bind_method(D_METHOD("get_enable_mode"), &VisibleOnScreenEnabler2D::get_enable_mode); + + ClassDB::bind_method(D_METHOD("set_enable_node_path", "path"), &VisibleOnScreenEnabler2D::set_enable_node_path); + ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler2D::get_enable_node_path); + + 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"); + + BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT); + BIND_ENUM_CONSTANT(ENABLE_MODE_ALWAYS); + BIND_ENUM_CONSTANT(ENABLE_MODE_WHEN_PAUSED); +} + +VisibleOnScreenEnabler2D::VisibleOnScreenEnabler2D() { +} diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h index 7f4a5bc193..9c236a138f 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visible_on_screen_notifier_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visibility_notifier_2d.h */ +/* visible_on_screen_notifier_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,25 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISIBILITY_NOTIFIER_2D_H -#define VISIBILITY_NOTIFIER_2D_H +#ifndef VISIBLE_ON_SCREEN_NOTIFIER_2D_H +#define VISIBLE_ON_SCREEN_NOTIFIER_2D_H #include "scene/2d/node_2d.h" class Viewport; -class VisibilityNotifier2D : public Node2D { - GDCLASS(VisibilityNotifier2D, Node2D); +class VisibleOnScreenNotifier2D : public Node2D { + GDCLASS(VisibleOnScreenNotifier2D, Node2D); Set<Viewport *> viewports; Rect2 rect; -protected: - friend struct SpatialIndexer2D; - - void _enter_viewport(Viewport *p_viewport); - void _exit_viewport(Viewport *p_viewport); +private: + bool on_screen = false; + void _visibility_enter(); + void _visibility_exit(); +protected: virtual void _screen_enter() {} virtual void _screen_exit() {} @@ -64,49 +64,42 @@ public: bool is_on_screen() const; - VisibilityNotifier2D(); + VisibleOnScreenNotifier2D(); }; -class VisibilityEnabler2D : public VisibilityNotifier2D { - GDCLASS(VisibilityEnabler2D, VisibilityNotifier2D); +class VisibleOnScreenEnabler2D : public VisibleOnScreenNotifier2D { + GDCLASS(VisibleOnScreenEnabler2D, VisibleOnScreenNotifier2D); 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(); + void _update_enable_mode(bool p_enable); + public: - void set_enabler(Enabler p_enabler, bool p_enable); - bool is_enabler_enabled(Enabler p_enabler) const; + void set_enable_mode(EnableMode p_mode); + EnableMode get_enable_mode(); - TypedArray<String> get_configuration_warnings() const override; + void set_enable_node_path(NodePath p_path); + NodePath get_enable_node_path(); - VisibilityEnabler2D(); + VisibleOnScreenEnabler2D(); }; -VARIANT_ENUM_CAST(VisibilityEnabler2D::Enabler); +VARIANT_ENUM_CAST(VisibleOnScreenEnabler2D::EnableMode); #endif // VISIBILITY_NOTIFIER_2D_H diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index cad4330c17..9640043031 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -404,10 +404,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { break; } - List<Camera3D *> cameras; - world_3d->get_camera_list(&cameras); - - for (List<Camera3D *>::Element *E = cameras.front(); E; E = E->next()) { + for (const Set<Camera3D *>::Element *E = world_3d->get_cameras().front(); E; E = E->next()) { Camera3D *camera = E->get(); Viewport *vp = camera->get_viewport(); if (!vp->is_audio_listener()) { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 1c9418ae83..62bbebe6e7 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -93,10 +93,6 @@ void Camera3D::_update_camera() { } get_viewport()->_camera_transform_changed_notify(); - - if (get_world_3d().is_valid()) { - get_world_3d()->_update_camera(this); - } } void Camera3D::_notification(int p_what) { @@ -500,6 +496,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking); ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum); + ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit); @@ -623,6 +620,16 @@ Vector<Plane> Camera3D::get_frustum() const { return cm.get_projection_planes(get_camera_transform()); } +bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const { + Vector<Plane> frustum = get_frustum(); + for (int i = 0; i < frustum.size(); i++) { + if (frustum[i].is_point_over(p_position)) { + return false; + } + } + return true; +} + void Camera3D::set_v_offset(float p_offset) { v_offset = p_offset; _update_camera(); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index d9ebe78f1a..c6efc8f9a9 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -153,6 +153,7 @@ public: bool get_cull_mask_bit(int p_layer) const; virtual Vector<Plane> get_frustum() const; + bool is_position_in_frustum(const Vector3 &p_position) const; void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index e96e4df55c..e96b8ee1f9 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -32,6 +32,7 @@ #include "core/config/engine.h" #include "core/object/message_queue.h" +#include "scene/3d/visual_instance_3d.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" @@ -148,6 +149,7 @@ void Node3D::_notification(int p_what) { _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); + _update_visibility_parent(true); } break; case NOTIFICATION_EXIT_TREE: { @@ -161,6 +163,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; data.top_level_active = false; + _update_visibility_parent(true); } break; case NOTIFICATION_ENTER_WORLD: { data.inside_world = true; @@ -282,8 +285,12 @@ Transform3D Node3D::get_local_gizmo_transform() const { } #endif -Node3D *Node3D::get_parent_spatial() const { - return data.parent; +Node3D *Node3D::get_parent_node_3d() const { + if (data.top_level) { + return nullptr; + } + + return Object::cast_to<Node3D>(get_parent()); } Transform3D Node3D::get_relative_transform(const Node *p_parent) const { @@ -690,6 +697,51 @@ void Node3D::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void Node3D::_update_visibility_parent(bool p_update_root) { + RID new_parent; + + if (!visibility_parent_path.is_empty()) { + if (!p_update_root) { + return; + } + Node *parent = get_node_or_null(visibility_parent_path); + ERR_FAIL_COND_MSG(!parent, "Can't find visibility parent node at path: " + visibility_parent_path); + ERR_FAIL_COND_MSG(parent == this, "The visibility parent can't be the same node."); + GeometryInstance3D *gi = Object::cast_to<GeometryInstance3D>(parent); + ERR_FAIL_COND_MSG(!gi, "The visibility parent node must be a GeometryInstance3D, at path: " + visibility_parent_path); + new_parent = gi ? gi->get_instance() : RID(); + } else if (data.parent) { + new_parent = data.parent->data.visibility_parent; + } + + if (new_parent == data.visibility_parent) { + return; + } + + data.visibility_parent = new_parent; + + VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(this); + if (vi) { + RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent); + } + + for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) { + Node3D *c = E->get(); + c->_update_visibility_parent(false); + } +} + +void Node3D::set_visibility_parent(const NodePath &p_path) { + visibility_parent_path = p_path; + if (is_inside_tree()) { + _update_visibility_parent(true); + } +} + +NodePath Node3D::get_visibility_parent() const { + return visibility_parent_path; +} + void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform); @@ -703,7 +755,7 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale); ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); - ClassDB::bind_method(D_METHOD("get_parent_spatial"), &Node3D::get_parent_spatial); + ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d); ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification); ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level); ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &Node3D::is_set_as_top_level); @@ -713,6 +765,9 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update_transform"), &Node3D::force_update_transform); + ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent); + ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent); + ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo); ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo); @@ -768,6 +823,7 @@ void Node3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo"); ADD_SIGNAL(MethodInfo("visibility_changed")); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 5f076ceb12..c7e36cf2ec 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -75,6 +75,8 @@ class Node3D : public Node { bool top_level = false; bool inside_world = false; + RID visibility_parent; + int children_lock = 0; Node3D *parent = nullptr; List<Node3D *> children; @@ -95,12 +97,17 @@ class Node3D : public Node { } data; + NodePath visibility_parent_path; + void _update_gizmo(); void _notify_dirty(); void _propagate_transform_changed(Node3D *p_origin); void _propagate_visibility_changed(); + void _propagate_visibility_parent(); + void _update_visibility_parent(bool p_update_root); + protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } @@ -118,7 +125,7 @@ public: NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 44, }; - Node3D *get_parent_spatial() const; + Node3D *get_parent_node_3d() const; Ref<World3D> get_world_3d() const; @@ -196,6 +203,9 @@ public: void force_update_transform(); + void set_visibility_parent(const NodePath &p_path); + NodePath get_visibility_parent() const; + Node3D(); }; diff --git a/scene/3d/visibility_notifier_3d.cpp b/scene/3d/visibility_notifier_3d.cpp deleted file mode 100644 index b230cb2fd7..0000000000 --- a/scene/3d/visibility_notifier_3d.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/*************************************************************************/ -/* visibility_notifier_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "visibility_notifier_3d.h" - -#include "core/config/engine.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/animation/animation_player.h" -#include "scene/scene_string_names.h" - -void VisibilityNotifier3D::_enter_camera(Camera3D *p_camera) { - ERR_FAIL_COND(cameras.has(p_camera)); - cameras.insert(p_camera); - if (cameras.size() == 1) { - emit_signal(SceneStringNames::get_singleton()->screen_entered); - _screen_enter(); - } - - emit_signal(SceneStringNames::get_singleton()->camera_entered, p_camera); -} - -void VisibilityNotifier3D::_exit_camera(Camera3D *p_camera) { - ERR_FAIL_COND(!cameras.has(p_camera)); - cameras.erase(p_camera); - - emit_signal(SceneStringNames::get_singleton()->camera_exited, p_camera); - if (cameras.size() == 0) { - emit_signal(SceneStringNames::get_singleton()->screen_exited); - - _screen_exit(); - } -} - -void VisibilityNotifier3D::set_aabb(const AABB &p_aabb) { - if (aabb == p_aabb) { - return; - } - aabb = p_aabb; - - if (is_inside_world()) { - get_world_3d()->_update_notifier(this, get_global_transform().xform(aabb)); - } - - update_gizmo(); -} - -AABB VisibilityNotifier3D::get_aabb() const { - return aabb; -} - -void VisibilityNotifier3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_WORLD: { - world = get_world_3d(); - ERR_FAIL_COND(!world.is_valid()); - world->_register_notifier(this, get_global_transform().xform(aabb)); - } break; - case NOTIFICATION_TRANSFORM_CHANGED: { - world->_update_notifier(this, get_global_transform().xform(aabb)); - } break; - case NOTIFICATION_EXIT_WORLD: { - ERR_FAIL_COND(!world.is_valid()); - world->_remove_notifier(this); - } break; - } -} - -bool VisibilityNotifier3D::is_on_screen() const { - return cameras.size() != 0; -} - -void VisibilityNotifier3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibilityNotifier3D::set_aabb); - ClassDB::bind_method(D_METHOD("get_aabb"), &VisibilityNotifier3D::get_aabb); - ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibilityNotifier3D::is_on_screen); - - ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb"); - - ADD_SIGNAL(MethodInfo("camera_entered", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"))); - ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"))); - ADD_SIGNAL(MethodInfo("screen_entered")); - ADD_SIGNAL(MethodInfo("screen_exited")); -} - -VisibilityNotifier3D::VisibilityNotifier3D() { - set_notify_transform(true); -} - -////////////////////////////////////// - -void VisibilityEnabler3D::_screen_enter() { - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - _change_node_state(E->key(), true); - } - - visible = true; -} - -void VisibilityEnabler3D::_screen_exit() { - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - _change_node_state(E->key(), false); - } - - visible = false; -} - -void VisibilityEnabler3D::_find_nodes(Node *p_node) { - bool add = false; - Variant meta; - - { - RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node); - if (rb && ((rb->get_mode() == RigidBody3D::MODE_DYNAMIC || rb->get_mode() == RigidBody3D::MODE_DYNAMIC_LOCKED))) { - add = true; - meta = rb->get_mode(); - } - } - - { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); - if (ap) { - add = true; - } - } - - if (add) { - p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed), varray(p_node), CONNECT_ONESHOT); - nodes[p_node] = meta; - _change_node_state(p_node, false); - } - - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *c = p_node->get_child(i); - if (c->get_filename() != String()) { - continue; //skip, instance - } - - _find_nodes(c); - } -} - -void VisibilityEnabler3D::_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); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - if (Engine::get_singleton()->is_editor_hint()) { - return; - } - - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - if (!visible) { - _change_node_state(E->key(), true); - } - E->key()->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed)); - } - - nodes.clear(); - } -} - -void VisibilityEnabler3D::_change_node_state(Node *p_node, bool p_enabled) { - ERR_FAIL_COND(!nodes.has(p_node)); - - if (enabler[ENABLER_FREEZE_BODIES]) { - RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node); - if (rb) { - rb->set_sleeping(!p_enabled); - } - } - - if (enabler[ENABLER_PAUSE_ANIMATIONS]) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); - - if (ap) { - ap->set_active(p_enabled); - } - } -} - -void VisibilityEnabler3D::_node_removed(Node *p_node) { - if (!visible) { - _change_node_state(p_node, true); - } - nodes.erase(p_node); -} - -void VisibilityEnabler3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_enabler", "enabler", "enabled"), &VisibilityEnabler3D::set_enabler); - ClassDB::bind_method(D_METHOD("is_enabler_enabled", "enabler"), &VisibilityEnabler3D::is_enabler_enabled); - - 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); - - BIND_ENUM_CONSTANT(ENABLER_PAUSE_ANIMATIONS); - BIND_ENUM_CONSTANT(ENABLER_FREEZE_BODIES); - BIND_ENUM_CONSTANT(ENABLER_MAX); -} - -void VisibilityEnabler3D::set_enabler(Enabler p_enabler, bool p_enable) { - ERR_FAIL_INDEX(p_enabler, ENABLER_MAX); - enabler[p_enabler] = p_enable; -} - -bool VisibilityEnabler3D::is_enabler_enabled(Enabler p_enabler) const { - ERR_FAIL_INDEX_V(p_enabler, ENABLER_MAX, false); - return enabler[p_enabler]; -} - -VisibilityEnabler3D::VisibilityEnabler3D() { - for (int i = 0; i < ENABLER_MAX; i++) { - enabler[i] = true; - } -} diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp new file mode 100644 index 0000000000..682bcec449 --- /dev/null +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* visible_on_screen_notifier_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "visible_on_screen_notifier_3d.h" + +#include "core/config/engine.h" +#include "scene/3d/camera_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/scene_string_names.h" + +void VisibleOnScreenNotifier3D::_visibility_enter() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = true; + emit_signal(SceneStringNames::get_singleton()->screen_entered); + _screen_enter(); +} +void VisibleOnScreenNotifier3D::_visibility_exit() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = false; + emit_signal(SceneStringNames::get_singleton()->screen_exited); + _screen_exit(); +} + +void VisibleOnScreenNotifier3D::set_aabb(const AABB &p_aabb) { + if (aabb == p_aabb) { + return; + } + aabb = p_aabb; + + RS::get_singleton()->visibility_notifier_set_aabb(get_base(), aabb); + + update_gizmo(); +} + +AABB VisibleOnScreenNotifier3D::get_aabb() const { + return aabb; +} + +bool VisibleOnScreenNotifier3D::is_on_screen() const { + return on_screen; +} + +void VisibleOnScreenNotifier3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_EXIT_TREE) { + on_screen = false; + } +} + +void VisibleOnScreenNotifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibleOnScreenNotifier3D::set_aabb); + ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibleOnScreenNotifier3D::is_on_screen); + + ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb"); + + ADD_SIGNAL(MethodInfo("screen_entered")); + ADD_SIGNAL(MethodInfo("screen_exited")); +} + +Vector<Face3> VisibleOnScreenNotifier3D::get_faces(uint32_t p_usage_flags) const { + return Vector<Face3>(); +} + +VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() { + RID notifier = RS::get_singleton()->visibility_notifier_create(); + RS::get_singleton()->visibility_notifier_set_aabb(notifier, aabb); + RS::get_singleton()->visibility_notifier_set_callbacks(notifier, callable_mp(this, &VisibleOnScreenNotifier3D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier3D::_visibility_exit)); + set_base(notifier); +} +VisibleOnScreenNotifier3D::~VisibleOnScreenNotifier3D() { + RID base = get_base(); + set_base(RID()); + RS::get_singleton()->free(base); +} + +////////////////////////////////////// + +void VisibleOnScreenEnabler3D::_screen_enter() { + _update_enable_mode(true); +} + +void VisibleOnScreenEnabler3D::_screen_exit() { + _update_enable_mode(false); +} + +void VisibleOnScreenEnabler3D::set_enable_mode(EnableMode p_mode) { + enable_mode = p_mode; + if (is_inside_tree()) { + _update_enable_mode(is_on_screen()); + } +} +VisibleOnScreenEnabler3D::EnableMode VisibleOnScreenEnabler3D::get_enable_mode() { + return enable_mode; +} + +void VisibleOnScreenEnabler3D::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 VisibleOnScreenEnabler3D::get_enable_node_path() { + return enable_node_path; +} + +void VisibleOnScreenEnabler3D::_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 VisibleOnScreenEnabler3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + + 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) { + node_id = ObjectID(); + } +} + +void VisibleOnScreenEnabler3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enable_mode", "mode"), &VisibleOnScreenEnabler3D::set_enable_mode); + ClassDB::bind_method(D_METHOD("get_enable_mode"), &VisibleOnScreenEnabler3D::get_enable_mode); + + ClassDB::bind_method(D_METHOD("set_enable_node_path", "path"), &VisibleOnScreenEnabler3D::set_enable_node_path); + ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler3D::get_enable_node_path); + + 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"); + + BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT); + BIND_ENUM_CONSTANT(ENABLE_MODE_ALWAYS); + BIND_ENUM_CONSTANT(ENABLE_MODE_WHEN_PAUSED); +} + +VisibleOnScreenEnabler3D::VisibleOnScreenEnabler3D() { +} diff --git a/scene/3d/visibility_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h index 9f7705067f..fb7137c4f0 100644 --- a/scene/3d/visibility_notifier_3d.h +++ b/scene/3d/visible_on_screen_notifier_3d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* visibility_notifier_3d.h */ +/* visible_on_screen_notifier_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,74 +28,74 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISIBILITY_NOTIFIER_H -#define VISIBILITY_NOTIFIER_H +#ifndef VISIBLE_ON_SCREEN_NOTIFIER_3D_H +#define VISIBLE_ON_SCREEN_NOTIFIER_3D_H -#include "scene/3d/node_3d.h" +#include "scene/3d/visual_instance_3d.h" class World3D; class Camera3D; -class VisibilityNotifier3D : public Node3D { - GDCLASS(VisibilityNotifier3D, Node3D); - - Ref<World3D> world; - Set<Camera3D *> cameras; +class VisibleOnScreenNotifier3D : public VisualInstance3D { + GDCLASS(VisibleOnScreenNotifier3D, VisualInstance3D); AABB aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); +private: + bool on_screen = false; + void _visibility_enter(); + void _visibility_exit(); + protected: virtual void _screen_enter() {} virtual void _screen_exit() {} void _notification(int p_what); static void _bind_methods(); - friend struct SpatialIndexer; - - void _enter_camera(Camera3D *p_camera); - void _exit_camera(Camera3D *p_camera); public: void set_aabb(const AABB &p_aabb); - AABB get_aabb() const; + virtual AABB get_aabb() const override; bool is_on_screen() const; - VisibilityNotifier3D(); + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; + + VisibleOnScreenNotifier3D(); + ~VisibleOnScreenNotifier3D(); }; -class VisibilityEnabler3D : public VisibilityNotifier3D { - GDCLASS(VisibilityEnabler3D, VisibilityNotifier3D); +class VisibleOnScreenEnabler3D : public VisibleOnScreenNotifier3D { + GDCLASS(VisibleOnScreenEnabler3D, VisibleOnScreenNotifier3D); public: - enum Enabler { - ENABLER_PAUSE_ANIMATIONS, - ENABLER_FREEZE_BODIES, - ENABLER_MAX + enum EnableMode { + ENABLE_MODE_INHERIT, + ENABLE_MODE_ALWAYS, + ENABLE_MODE_WHEN_PAUSED, }; protected: + ObjectID node_id; virtual void _screen_enter() override; virtual void _screen_exit() override; - bool visible = false; - - void _find_nodes(Node *p_node); - - Map<Node *, Variant> nodes; - void _node_removed(Node *p_node); - bool enabler[ENABLER_MAX]; - - void _change_node_state(Node *p_node, bool p_enabled); + EnableMode enable_mode = ENABLE_MODE_INHERIT; + NodePath enable_node_path = NodePath(".."); void _notification(int p_what); static void _bind_methods(); + void _update_enable_mode(bool p_enable); + public: - void set_enabler(Enabler p_enabler, bool p_enable); - bool is_enabler_enabled(Enabler p_enabler) const; + void set_enable_mode(EnableMode p_mode); + EnableMode get_enable_mode(); + + void set_enable_node_path(NodePath p_path); + NodePath get_enable_node_path(); - VisibilityEnabler3D(); + VisibleOnScreenEnabler3D(); }; -VARIANT_ENUM_CAST(VisibilityEnabler3D::Enabler); +VARIANT_ENUM_CAST(VisibleOnScreenEnabler3D::EnableMode); #endif // VISIBILITY_NOTIFIER_H diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 6971c1ce2a..c16e3c2d78 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -150,40 +150,40 @@ Ref<Material> GeometryInstance3D::get_material_override() const { return material_override; } -void GeometryInstance3D::set_lod_min_distance(float p_dist) { - lod_min_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin(float p_dist) { + visibility_range_begin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_distance() const { - return lod_min_distance; +float GeometryInstance3D::get_visibility_range_begin() const { + return visibility_range_begin; } -void GeometryInstance3D::set_lod_max_distance(float p_dist) { - lod_max_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end(float p_dist) { + visibility_range_end = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_distance() const { - return lod_max_distance; +float GeometryInstance3D::get_visibility_range_end() const { + return visibility_range_end; } -void GeometryInstance3D::set_lod_min_hysteresis(float p_dist) { - lod_min_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) { + visibility_range_begin_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_hysteresis() const { - return lod_min_hysteresis; +float GeometryInstance3D::get_visibility_range_begin_margin() const { + return visibility_range_begin_margin; } -void GeometryInstance3D::set_lod_max_hysteresis(float p_dist) { - lod_max_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) { + visibility_range_end_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_hysteresis() const { - return lod_max_hysteresis; +float GeometryInstance3D::get_visibility_range_end_margin() const { + return visibility_range_end_margin; } void GeometryInstance3D::_notification(int p_what) { @@ -357,17 +357,17 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); - ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_end_margin"), &GeometryInstance3D::get_visibility_range_end_margin); - ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); - ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_end", "distance"), &GeometryInstance3D::set_visibility_range_end); + ClassDB::bind_method(D_METHOD("get_visibility_range_end"), &GeometryInstance3D::get_visibility_range_end); - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin_margin", "distance"), &GeometryInstance3D::set_visibility_range_begin_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin_margin"), &GeometryInstance3D::get_visibility_range_begin_margin); - ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance); - ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin", "distance"), &GeometryInstance3D::set_visibility_range_begin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin"), &GeometryInstance3D::get_visibility_range_begin); ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); @@ -398,11 +398,11 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); - ADD_GROUP("LOD", "lod_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis"); + ADD_GROUP("Visibility Range", "visibility_range_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); //ADD_SIGNAL( MethodInfo("visibility_changed")); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 68d29ef81e..2d5699859b 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -107,10 +107,13 @@ public: private: ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON; Ref<Material> material_override; - float lod_min_distance = 0.0; - float lod_max_distance = 0.0; - float lod_min_hysteresis = 0.0; - float lod_max_hysteresis = 0.0; + + float visibility_range_begin = 0.0; + float visibility_range_end = 0.0; + float visibility_range_begin_margin = 0.0; + float visibility_range_end_margin = 0.0; + + Vector<NodePath> visibility_range_children; float lod_bias = 1.0; @@ -136,17 +139,20 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; - void set_lod_min_distance(float p_dist); - float get_lod_min_distance() const; + void set_visibility_range_begin(float p_dist); + float get_visibility_range_begin() const; + + void set_visibility_range_end(float p_dist); + float get_visibility_range_end() const; - void set_lod_max_distance(float p_dist); - float get_lod_max_distance() const; + void set_visibility_range_begin_margin(float p_dist); + float get_visibility_range_begin_margin() const; - void set_lod_min_hysteresis(float p_dist); - float get_lod_min_hysteresis() const; + void set_visibility_range_end_margin(float p_dist); + float get_visibility_range_end_margin() const; - void set_lod_max_hysteresis(float p_dist); - float get_lod_max_hysteresis() const; + void set_visibility_range_parent(const Node *p_parent); + void clear_visibility_range_parent(); void set_material_override(const Ref<Material> &p_material); Ref<Material> get_material_override() const; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 5a4d9b5d7e..4f2c816934 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -520,9 +520,9 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const { } } - bool vr_enabled = GLOBAL_GET("rendering/vr/enabled"); - if (!vr_enabled) { - warnings.push_back(TTR("VR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); + bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); + if (!xr_enabled) { + warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); } return warnings; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index d5000e88d7..5b720945b8 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -218,6 +218,11 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventMouseButton> mb = p_gui_input; if (mb.is_valid()) { + /* Ignore mouse clicks in IME input mode. */ + if (has_ime_text()) { + return; + } + if (code_completion_active && code_completion_rect.has_point(mb->get_position())) { if (!mb->is_pressed()) { return; @@ -248,6 +253,30 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } cancel_code_completion(); set_code_hint(""); + + if (mb->is_pressed()) { + Vector2i mpos = mb->get_position(); + if (is_layout_rtl()) { + mpos.x = get_size().x - mpos.x; + } + + int line, col; + _get_mouse_pos(Point2i(mpos.x, mpos.y), line, col); + + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (is_line_folded(line)) { + int wrap_index = get_line_wrap_index_at_col(line, col); + if (wrap_index == times_line_wraps(line)) { + int eol_icon_width = cache.folded_eol_icon->get_width(); + int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); + if (mpos.x > left_margin && mpos.x <= left_margin + eol_icon_width + 3) { + unfold_line(line); + return; + } + } + } + } + } } Ref<InputEventKey> k = p_gui_input; @@ -356,6 +385,16 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { set_code_hint(""); } + /* Override input to unfold lines where needed. */ + if (!is_readonly()) { + if (k->is_action("ui_text_newline_above", true) || k->is_action("ui_text_newline_blank", true) || k->is_action("ui_text_newline", true)) { + unfold_line(cursor_get_line()); + } + if (cursor_get_line() > 0 && k->is_action("ui_text_backspace", true)) { + unfold_line(cursor_get_line() - 1); + } + } + /* Remove shift otherwise actions will not match. */ k = k->duplicate(); k->set_shift_pressed(false); @@ -380,6 +419,21 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || get_line_count() == 0))) { return CURSOR_ARROW; } + + int line, col; + _get_mouse_pos(p_pos, line, col); + + if (is_line_folded(line)) { + int wrap_index = get_line_wrap_index_at_col(line, col); + if (wrap_index == times_line_wraps(line)) { + int eol_icon_width = cache.folded_eol_icon->get_width(); + int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); + if (p_pos.x > left_margin && p_pos.x <= left_margin + eol_icon_width + 3) { + return CURSOR_POINTING_HAND; + } + } + } + return TextEdit::get_cursor_shape(p_pos); } @@ -581,7 +635,7 @@ bool CodeEdit::is_drawing_fold_gutter() const { } void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) { - if (!can_fold(p_line) && !is_folded(p_line)) { + if (!can_fold_line(p_line) && !is_line_folded(p_line)) { set_line_gutter_clickable(p_line, fold_gutter, false); return; } @@ -593,13 +647,193 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi p_region.position += Point2(horizontal_padding, vertical_padding); p_region.size -= Point2(horizontal_padding, vertical_padding) * 2; - if (can_fold(p_line)) { + if (can_fold_line(p_line)) { can_fold_icon->draw_rect(get_canvas_item(), p_region, false, folding_color); return; } folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color); } +/* Line Folding */ +void CodeEdit::set_line_folding_enabled(bool p_enabled) { + line_folding_enabled = p_enabled; + set_hiding_enabled(p_enabled); +} + +bool CodeEdit::is_line_folding_enabled() const { + return line_folding_enabled; +} + +bool CodeEdit::can_fold_line(int p_line) const { + ERR_FAIL_INDEX_V(p_line, get_line_count(), false); + if (!line_folding_enabled) { + return false; + } + + if (p_line + 1 >= get_line_count() || get_line(p_line).strip_edges().size() == 0) { + return false; + } + + if (is_line_hidden(p_line) || is_line_folded(p_line)) { + return false; + } + + /* Check for full multiline line or block strings / comments. */ + int in_comment = is_in_comment(p_line); + int in_string = (in_comment == -1) ? is_in_string(p_line) : -1; + if (in_string != -1 || in_comment != -1) { + if (get_delimiter_start_position(p_line, get_line(p_line).size() - 1).y != p_line) { + return false; + } + + int delimter_end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y; + /* No end line, therefore we have a multiline region over the rest of the file. */ + if (delimter_end_line == -1) { + return true; + } + /* End line is the same therefore we have a block. */ + if (delimter_end_line == p_line) { + /* Check we are the start of the block. */ + if (p_line - 1 >= 0) { + if ((in_string != -1 && is_in_string(p_line - 1) != -1) || (in_comment != -1 && is_in_comment(p_line - 1) != -1)) { + return false; + } + } + /* Check it continues for at least one line. */ + return ((in_string != -1 && is_in_string(p_line + 1) != -1) || (in_comment != -1 && is_in_comment(p_line + 1) != -1)); + } + return ((in_string != -1 && is_in_string(delimter_end_line) != -1) || (in_comment != -1 && is_in_comment(delimter_end_line) != -1)); + } + + /* Otherwise check indent levels. */ + int start_indent = get_indent_level(p_line); + for (int i = p_line + 1; i < get_line_count(); i++) { + if (is_in_string(i) != -1 || is_in_comment(i) != -1 || get_line(i).strip_edges().size() == 0) { + continue; + } + return (get_indent_level(i) > start_indent); + } + return false; +} + +void CodeEdit::fold_line(int p_line) { + ERR_FAIL_INDEX(p_line, get_line_count()); + if (!is_line_folding_enabled() || !can_fold_line(p_line)) { + return; + } + + /* Find the last line to be hidden. */ + int end_line = get_line_count(); + + int in_comment = is_in_comment(p_line); + int in_string = (in_comment == -1) ? is_in_string(p_line) : -1; + if (in_string != -1 || in_comment != -1) { + end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y; + /* End line is the same therefore we have a block. */ + if (end_line == p_line) { + for (int i = p_line + 1; i < get_line_count(); i++) { + if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) { + end_line = i - 1; + break; + } + } + } + } else { + int start_indent = get_indent_level(p_line); + for (int i = p_line + 1; i < get_line_count(); i++) { + if (get_line(p_line).strip_edges().size() == 0 || is_in_string(i) != -1 || is_in_comment(i) != -1) { + end_line = i; + continue; + } + + if (get_indent_level(i) <= start_indent && get_line(i).strip_edges().size() != 0) { + end_line = i - 1; + break; + } + } + } + + for (int i = p_line + 1; i <= end_line; i++) { + set_line_as_hidden(i, true); + } + + /* Fix selection. */ + if (is_selection_active()) { + if (is_line_hidden(get_selection_from_line()) && is_line_hidden(get_selection_to_line())) { + deselect(); + } else if (is_line_hidden(get_selection_from_line())) { + select(p_line, 9999, get_selection_to_line(), get_selection_to_column()); + } else if (is_line_hidden(get_selection_to_line())) { + select(get_selection_from_line(), get_selection_from_column(), p_line, 9999); + } + } + + /* Reset caret. */ + if (is_line_hidden(cursor_get_line())) { + cursor_set_line(p_line, false, false); + cursor_set_column(get_line(p_line).length(), false); + } + update(); +} + +void CodeEdit::unfold_line(int p_line) { + ERR_FAIL_INDEX(p_line, get_line_count()); + if (!is_line_folded(p_line) && !is_line_hidden(p_line)) { + return; + } + + int fold_start = p_line; + for (; fold_start > 0; fold_start--) { + if (is_line_folded(fold_start)) { + break; + } + } + fold_start = is_line_folded(fold_start) ? fold_start : p_line; + + for (int i = fold_start + 1; i < get_line_count(); i++) { + if (!is_line_hidden(i)) { + break; + } + set_line_as_hidden(i, false); + } + update(); +} + +void CodeEdit::fold_all_lines() { + for (int i = 0; i < get_line_count(); i++) { + fold_line(i); + } + update(); +} + +void CodeEdit::unfold_all_lines() { + unhide_all_lines(); +} + +void CodeEdit::toggle_foldable_line(int p_line) { + ERR_FAIL_INDEX(p_line, get_line_count()); + if (is_line_folded(p_line)) { + unfold_line(p_line); + return; + } + fold_line(p_line); +} + +bool CodeEdit::is_line_folded(int p_line) const { + ERR_FAIL_INDEX_V(p_line, get_line_count(), false); + return p_line + 1 < get_line_count() && !is_line_hidden(p_line) && is_line_hidden(p_line + 1); +} + +TypedArray<int> CodeEdit::get_folded_lines() const { + TypedArray<int> folded_lines; + for (int i = 0; i < get_line_count(); i++) { + if (is_line_folded(i)) { + folded_lines.push_back(i); + } + } + return folded_lines; +} + /* Delimiters */ // Strings void CodeEdit::add_string_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only) { @@ -1094,6 +1328,21 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter); ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter); + /* Line folding */ + ClassDB::bind_method(D_METHOD("set_line_folding_enabled", "enabled"), &CodeEdit::set_line_folding_enabled); + ClassDB::bind_method(D_METHOD("is_line_folding_enabled"), &CodeEdit::is_line_folding_enabled); + + ClassDB::bind_method(D_METHOD("can_fold_line", "line"), &CodeEdit::can_fold_line); + + ClassDB::bind_method(D_METHOD("fold_line", "line"), &CodeEdit::fold_line); + ClassDB::bind_method(D_METHOD("unfold_line", "line"), &CodeEdit::unfold_line); + ClassDB::bind_method(D_METHOD("fold_all_lines"), &CodeEdit::fold_all_lines); + ClassDB::bind_method(D_METHOD("unfold_all_lines"), &CodeEdit::unfold_all_lines); + ClassDB::bind_method(D_METHOD("toggle_foldable_line", "line"), &CodeEdit::toggle_foldable_line); + + ClassDB::bind_method(D_METHOD("is_line_folded", "line"), &CodeEdit::is_line_folded); + ClassDB::bind_method(D_METHOD("get_folded_lines"), &CodeEdit::get_folded_lines); + /* Delimiters */ // Strings ClassDB::bind_method(D_METHOD("add_string_delimiter", "start_key", "end_key", "line_only"), &CodeEdit::add_string_delimiter, DEFVAL(false)); @@ -1121,8 +1370,8 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_delimiter_start_key", "delimiter_index"), &CodeEdit::get_delimiter_start_key); ClassDB::bind_method(D_METHOD("get_delimiter_end_key", "delimiter_index"), &CodeEdit::get_delimiter_end_key); - ClassDB::bind_method(D_METHOD("get_delimiter_start_postion", "line", "column"), &CodeEdit::get_delimiter_start_position); - ClassDB::bind_method(D_METHOD("get_delimiter_end_postion", "line", "column"), &CodeEdit::get_delimiter_end_position); + ClassDB::bind_method(D_METHOD("get_delimiter_start_position", "line", "column"), &CodeEdit::get_delimiter_start_position); + ClassDB::bind_method(D_METHOD("get_delimiter_end_position", "line", "column"), &CodeEdit::get_delimiter_end_position); /* Code hint */ ClassDB::bind_method(D_METHOD("set_code_hint", "code_hint"), &CodeEdit::set_code_hint); @@ -1175,6 +1424,8 @@ void CodeEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled"); + ADD_GROUP("Delimiters", "delimiter_"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_strings"), "set_string_delimiters", "get_string_delimiters"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_comments"), "set_comment_delimiters", "get_comment_delimiters"); @@ -1205,9 +1456,9 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) { } if (p_gutter == fold_gutter) { - if (is_folded(p_line)) { + if (is_line_folded(p_line)) { unfold_line(p_line); - } else if (can_fold(p_line)) { + } else if (can_fold_line(p_line)) { fold_line(p_line); } return; @@ -1526,6 +1777,9 @@ void CodeEdit::_clear_delimiters(DelimiterType p_type) { } } delimiter_cache.clear(); + if (!setting_delimiters) { + _update_delimiter_cache(); + } } TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const { diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 6305eacf83..52b3f52a03 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -98,6 +98,9 @@ private: void _gutter_clicked(int p_line, int p_gutter); void _update_gutter_indexes(); + /* Line Folding */ + bool line_folding_enabled = true; + /* Delimiters */ enum DelimiterType { TYPE_STRING, @@ -241,6 +244,21 @@ public: void set_draw_fold_gutter(bool p_draw); bool is_drawing_fold_gutter() const; + /* Line Folding */ + void set_line_folding_enabled(bool p_enabled); + bool is_line_folding_enabled() const; + + bool can_fold_line(int p_line) const; + + void fold_line(int p_line); + void unfold_line(int p_line); + void fold_all_lines(); + void unfold_all_lines(); + void toggle_foldable_line(int p_line); + + bool is_line_folded(int p_line) const; + TypedArray<int> get_folded_lines() const; + /* Delimiters */ void add_string_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only = false); void remove_string_delimiter(const String &p_start_key); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index c0b4563615..f394b9e3a5 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -289,7 +289,7 @@ void ColorPicker::_value_changed(double) { emit_signal("color_changed", color); } -void ColorPicker::_html_entered(const String &p_html) { +void ColorPicker::_html_submitted(const String &p_html) { if (updating || text_is_constructor || !c_text->is_visible()) { return; } @@ -1041,7 +1041,7 @@ void ColorPicker::_html_focus_exit() { if (c_text->get_menu()->is_visible()) { return; } - _html_entered(c_text->get_text()); + _html_submitted(c_text->get_text()); _focus_exit(); } @@ -1204,7 +1204,7 @@ ColorPicker::ColorPicker() : hhb->add_child(c_text); c_text->set_h_size_flags(SIZE_EXPAND_FILL); - c_text->connect("text_entered", callable_mp(this, &ColorPicker::_html_entered)); + c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted)); c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter)); c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 14113467d0..3bd2ff9375 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -104,7 +104,7 @@ private: float v = 0.0; Color last_hsv; - void _html_entered(const String &p_html); + void _html_submitted(const String &p_html); void _value_changed(double); void _update_controls(); void _update_color(bool p_update_sliders = true); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 7cae091c57..f63ae7569f 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -97,7 +97,7 @@ void AcceptDialog::_notification(int p_what) { } } -void AcceptDialog::_text_entered(const String &p_text) { +void AcceptDialog::_text_submitted(const String &p_text) { _ok_pressed(); } @@ -159,7 +159,7 @@ void AcceptDialog::register_text_enter(Control *p_line_edit) { ERR_FAIL_NULL(p_line_edit); LineEdit *line_edit = Object::cast_to<LineEdit>(p_line_edit); if (line_edit) { - line_edit->connect("text_entered", callable_mp(this, &AcceptDialog::_text_entered)); + line_edit->connect("text_submitted", callable_mp(this, &AcceptDialog::_text_submitted)); } } diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 69035001c0..d389806fff 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -69,7 +69,7 @@ protected: virtual void custom_action(const String &) {} // Not private since used by derived classes signal. - void _text_entered(const String &p_text); + void _text_submitted(const String &p_text); void _ok_pressed(); void _cancel_pressed(); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 806039d7ac..f8cee6daec 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -116,7 +116,7 @@ void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) { invalidate(); } break; case KEY_BACKSPACE: { - _dir_entered(".."); + _dir_submitted(".."); } break; default: { handled = false; @@ -156,7 +156,7 @@ void FileDialog::update_dir() { deselect_all(); } -void FileDialog::_dir_entered(String p_dir) { +void FileDialog::_dir_submitted(String p_dir) { dir_access->change_dir(p_dir); file->set_text(""); invalidate(); @@ -164,7 +164,7 @@ void FileDialog::_dir_entered(String p_dir) { _push_history(); } -void FileDialog::_file_entered(const String &p_file) { +void FileDialog::_file_submitted(const String &p_file) { _action_pressed(); } @@ -1020,8 +1020,8 @@ FileDialog::FileDialog() { tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), varray(), CONNECT_DEFERRED); tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated), varray()); tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all)); - dir->connect("text_entered", callable_mp(this, &FileDialog::_dir_entered)); - file->connect("text_entered", callable_mp(this, &FileDialog::_file_entered)); + dir->connect("text_submitted", callable_mp(this, &FileDialog::_dir_submitted)); + file->connect("text_submitted", callable_mp(this, &FileDialog::_file_submitted)); filter->connect("item_selected", callable_mp(this, &FileDialog::_filter_selected)); confirm_save = memnew(ConfirmationDialog); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 55774b488c..7fbafc4bb4 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -118,8 +118,8 @@ private: void _select_drive(int p_idx); void _tree_item_activated(); - void _dir_entered(String p_dir); - void _file_entered(const String &p_file); + void _dir_submitted(String p_dir); + void _file_submitted(const String &p_file); void _action_pressed(); void _save_confirm_pressed(); void _cancel_pressed(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 78ab30699a..5ef89e38f0 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -40,13 +40,8 @@ #include "editor/editor_scale.h" #endif -#define ZOOM_SCALE 1.2 - -#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE) -#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE) - -#define MINIMAP_OFFSET 12 -#define MINIMAP_PADDING 5 +constexpr int MINIMAP_OFFSET = 12; +constexpr int MINIMAP_PADDING = 5; bool GraphEditFilter::has_point(const Point2 &p_point) const { return ge->_filter_input(p_point); @@ -1072,8 +1067,9 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseMotion> mm = p_ev; if (mm.is_valid() && (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { - h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); - v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); + Vector2i relative = Input::get_singleton()->warp_mouse_motion(mm, get_global_rect()); + h_scroll->set_value(h_scroll->get_value() - relative.x); + v_scroll->set_value(v_scroll->get_value() - relative.y); } if (mm.is_valid() && dragging) { @@ -1322,18 +1318,20 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { minimap->update(); } - if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_CTRL)) { - set_zoom_custom(zoom * ZOOM_SCALE, b->get_position()); - } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_CTRL)) { - set_zoom_custom(zoom / ZOOM_SCALE, b->get_position()); - } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8); - } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8); - } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * b->get_factor() / 8); - } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) { - h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * b->get_factor() / 8); + int scroll_direction = (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) - (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP); + if (scroll_direction != 0) { + if (b->is_ctrl_pressed()) { + if (b->is_shift_pressed()) { + // Horizontal scrolling. + h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * b->get_factor() / 8) * scroll_direction); + } else { + // Vertical scrolling. + v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * b->get_factor() / 8) * scroll_direction); + } + } else { + // Zooming. + set_zoom_custom(scroll_direction < 0 ? zoom * zoom_step : zoom / zoom_step, b->get_position()); + } } } @@ -1392,19 +1390,19 @@ void GraphEdit::set_zoom(float p_zoom) { } void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { - p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); + p_zoom = CLAMP(p_zoom, zoom_min, zoom_max); if (zoom == p_zoom) { return; } - zoom_minus->set_disabled(zoom == MIN_ZOOM); - zoom_plus->set_disabled(zoom == MAX_ZOOM); - Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + p_center) / zoom; zoom = p_zoom; top_layer->update(); + zoom_minus->set_disabled(zoom == zoom_min); + zoom_plus->set_disabled(zoom == zoom_max); + _update_scroll(); minimap->update(); connections_layer->update(); @@ -1415,6 +1413,7 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { v_scroll->set_value(ofs.y); } + _update_zoom_label(); update(); } @@ -1422,6 +1421,61 @@ float GraphEdit::get_zoom() const { return zoom; } +void GraphEdit::set_zoom_step(float p_zoom_step) { + p_zoom_step = abs(p_zoom_step); + if (zoom_step == p_zoom_step) { + return; + } + + zoom_step = p_zoom_step; +} + +float GraphEdit::get_zoom_step() const { + return zoom_step; +} + +void GraphEdit::set_zoom_min(float p_zoom_min) { + ERR_FAIL_COND_MSG(p_zoom_min > zoom_max, "Cannot set min zoom level greater than max zoom level."); + + if (zoom_min == p_zoom_min) { + return; + } + + zoom_min = p_zoom_min; + set_zoom(zoom); +} + +float GraphEdit::get_zoom_min() const { + return zoom_min; +} + +void GraphEdit::set_zoom_max(float p_zoom_max) { + ERR_FAIL_COND_MSG(p_zoom_max < zoom_min, "Cannot set max zoom level lesser than min zoom level."); + + if (zoom_max == p_zoom_max) { + return; + } + + zoom_max = p_zoom_max; + set_zoom(zoom); +} + +float GraphEdit::get_zoom_max() const { + return zoom_max; +} + +void GraphEdit::set_show_zoom_label(bool p_enable) { + if (zoom_label->is_visible() == p_enable) { + return; + } + + zoom_label->set_visible(p_enable); +} + +bool GraphEdit::is_showing_zoom_label() const { + return zoom_label->is_visible(); +} + void GraphEdit::set_right_disconnects(bool p_enable) { right_disconnects = p_enable; } @@ -1462,7 +1516,7 @@ Array GraphEdit::_get_connection_list() const { } void GraphEdit::_zoom_minus() { - set_zoom(zoom / ZOOM_SCALE); + set_zoom(zoom / zoom_step); } void GraphEdit::_zoom_reset() { @@ -1470,7 +1524,13 @@ void GraphEdit::_zoom_reset() { } void GraphEdit::_zoom_plus() { - set_zoom(zoom * ZOOM_SCALE); + set_zoom(zoom * zoom_step); +} + +void GraphEdit::_update_zoom_label() { + int zoom_percent = static_cast<int>(Math::round(zoom * 100)); + String zoom_text = itos(zoom_percent) + "%"; + zoom_label->set_text(zoom_text); } void GraphEdit::add_valid_connection_type(int p_type, int p_with_type) { @@ -1611,6 +1671,18 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom); ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom); + ClassDB::bind_method(D_METHOD("set_zoom_min", "zoom_min"), &GraphEdit::set_zoom_min); + ClassDB::bind_method(D_METHOD("get_zoom_min"), &GraphEdit::get_zoom_min); + + ClassDB::bind_method(D_METHOD("set_zoom_max", "zoom_max"), &GraphEdit::set_zoom_max); + ClassDB::bind_method(D_METHOD("get_zoom_max"), &GraphEdit::get_zoom_max); + + ClassDB::bind_method(D_METHOD("set_zoom_step", "zoom_step"), &GraphEdit::set_zoom_step); + ClassDB::bind_method(D_METHOD("get_zoom_step"), &GraphEdit::get_zoom_step); + + ClassDB::bind_method(D_METHOD("set_show_zoom_label", "enable"), &GraphEdit::set_show_zoom_label); + ClassDB::bind_method(D_METHOD("is_showing_zoom_label"), &GraphEdit::is_showing_zoom_label); + ClassDB::bind_method(D_METHOD("set_snap", "pixels"), &GraphEdit::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &GraphEdit::get_snap); @@ -1645,9 +1717,18 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset"), "set_scroll_ofs", "get_scroll_ofs"); ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom"); + + ADD_GROUP("Connection Lines", "connection_lines"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased"); + + ADD_GROUP("Zoom", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_min"), "set_zoom_min", "get_zoom_min"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_max"), "set_zoom_max", "get_zoom_max"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_step"), "set_zoom_step", "get_zoom_step"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_label"), "set_show_zoom_label", "is_showing_zoom_label"); + ADD_GROUP("Minimap", "minimap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size"), "set_minimap_size", "get_minimap_size"); @@ -1672,6 +1753,13 @@ void GraphEdit::_bind_methods() { GraphEdit::GraphEdit() { set_focus_mode(FOCUS_ALL); + // Allow dezooming 8 times from the default zoom level. + // At low zoom levels, text is unreadable due to its small size and poor filtering, + // but this is still useful for previewing and navigation. + zoom_min = (1 / Math::pow(zoom_step, 8)); + // Allow zooming 4 times from the default zoom level. + zoom_max = (1 * Math::pow(zoom_step, 4)); + top_layer = memnew(GraphEditFilter(this)); add_child(top_layer); top_layer->set_mouse_filter(MOUSE_FILTER_PASS); @@ -1708,6 +1796,18 @@ GraphEdit::GraphEdit() { top_layer->add_child(zoom_hb); zoom_hb->set_position(Vector2(10, 10)); + zoom_label = memnew(Label); + zoom_hb->add_child(zoom_label); + zoom_label->set_visible(false); + zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + zoom_label->set_align(Label::ALIGN_CENTER); +#ifdef TOOLS_ENABLED + zoom_label->set_custom_minimum_size(Size2(48, 0) * EDSCALE); +#else + zoom_label->set_custom_minimum_size(Size2(48, 0)); +#endif + _update_zoom_label(); + zoom_minus = memnew(Button); zoom_minus->set_flat(true); zoom_hb->add_child(zoom_minus); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 8a51bcb11e..e8300f901c 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -34,6 +34,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/graph_node.h" +#include "scene/gui/label.h" #include "scene/gui/scroll_bar.h" #include "scene/gui/slider.h" #include "scene/gui/spin_box.h" @@ -105,6 +106,7 @@ public: }; private: + Label *zoom_label; Button *zoom_minus; Button *zoom_reset; Button *zoom_plus; @@ -114,10 +116,6 @@ private: Button *minimap_button; - void _zoom_minus(); - void _zoom_reset(); - void _zoom_plus(); - HScrollBar *h_scroll; VScrollBar *v_scroll; @@ -144,6 +142,14 @@ private: Vector2 drag_accum; float zoom = 1.0; + float zoom_step = 1.2; + float zoom_min; + float zoom_max; + + void _zoom_minus(); + void _zoom_reset(); + void _zoom_plus(); + void _update_zoom_label(); bool box_selecting = false; bool box_selection_mode_additive = false; @@ -247,6 +253,18 @@ public: void set_zoom_custom(float p_zoom, const Vector2 &p_center); float get_zoom() const; + void set_zoom_min(float p_zoom_min); + float get_zoom_min() const; + + void set_zoom_max(float p_zoom_max); + float get_zoom_max() const; + + void set_zoom_step(float p_zoom_step); + float get_zoom_step() const; + + void set_show_zoom_label(bool p_enable); + bool is_showing_zoom_label() const; + void set_minimap_size(Vector2 p_size); Vector2 get_minimap_size() const; void set_minimap_opacity(float p_opacity); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index de0ee626b9..0ce0130ad5 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -217,6 +217,7 @@ void Label::_notification(int p_what) { for (int64_t i = lines_skipped; i < last_line; i++) { total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing; } + total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); int vbegin = 0, vsep = 0; if (lines_visible > 0) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index c2ed9c1a3c..089893e63b 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -357,9 +357,9 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } - // Default is ENTER, KP_ENTER. Cannot use ui_accept as default includes SPACE - if (k->is_action("ui_text_newline", true)) { - emit_signal("text_entered", text); + // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE + if (k->is_action("ui_text_submit", false)) { + emit_signal("text_submitted", text); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } @@ -2159,7 +2159,7 @@ void LineEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_change_rejected")); - ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); + ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text"))); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a2c3b4ed8a..987d05bf71 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -3452,7 +3452,30 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { } } -bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to) { +bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) { + List<Item *>::Element *E = p_from; + while (E != nullptr) { + ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E->get()); + if (p_reverse_search) { + for (int i = frame->lines.size() - 1; i >= 0; i--) { + if (_search_line(frame, i, p_string, -1, p_reverse_search)) { + return true; + } + } + } else { + for (int i = 0; i < frame->lines.size(); i++) { + if (_search_line(frame, i, p_string, 0, p_reverse_search)) { + return true; + } + } + } + E = p_reverse_search ? E->prev() : E->next(); + } + return false; +} + +bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search) { ERR_FAIL_COND_V(p_frame == nullptr, false); ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false); @@ -3474,24 +3497,23 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - int idx = 0; - for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) { - ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. - ItemFrame *frame = static_cast<ItemFrame *>(E->get()); - - for (int i = 0; i < frame->lines.size(); i++) { - if (_search_line(frame, i, p_string, p_from, p_to)) { - return true; - } - } - idx++; + List<Item *>::Element *E = p_reverse_search ? table->subitems.back() : table->subitems.front(); + if (_search_table(table, E, p_string, p_reverse_search)) { + return true; } } break; default: break; } } - int sp = text.findn(p_string, 0); + + int sp = -1; + if (p_reverse_search) { + sp = text.rfindn(p_string, p_char_idx); + } else { + sp = text.findn(p_string, p_char_idx); + } + if (sp != -1) { selection.from_frame = p_frame; selection.from_line = p_line; @@ -3499,8 +3521,8 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p selection.from_char = sp; selection.to_frame = p_frame; selection.to_line = p_line; - selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length() - 1); - selection.to_char = sp + p_string.length() - 1; + selection.to_item = _get_item_at_pos(l.from, it_to, sp + p_string.length()); + selection.to_char = sp + p_string.length(); selection.active = true; return true; } @@ -3511,23 +3533,81 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) { ERR_FAIL_COND_V(!selection.enabled, false); + if (p_string.size() == 0) { + selection.active = false; + return false; + } + + int char_idx = p_search_previous ? -1 : 0; + int current_line = 0; + int ending_line = main->lines.size() - 1; if (p_from_selection && selection.active) { - for (int i = 0; i < main->lines.size(); i++) { - if (_search_line(main, i, p_string, selection.from_item, selection.to_item)) { - update(); - return true; - } + // First check to see if other results exist in current line + char_idx = p_search_previous ? selection.from_char - 1 : selection.to_char; + if (!(p_search_previous && char_idx < 0) && + _search_line(selection.from_frame, selection.from_line, p_string, char_idx, p_search_previous)) { + scroll_to_line(selection.from_frame->line + selection.from_line); + update(); + return true; } - } else { - for (int i = 0; i < main->lines.size(); i++) { - if (_search_line(main, i, p_string, nullptr, nullptr)) { - update(); - return true; + char_idx = p_search_previous ? -1 : 0; + + // Next, check to see if the current search result is in a table + if (selection.from_frame->parent != nullptr && selection.from_frame->parent->type == ITEM_TABLE) { + // Find last search result in table + ItemTable *parent_table = static_cast<ItemTable *>(selection.from_frame->parent); + List<Item *>::Element *parent_element = p_search_previous ? parent_table->subitems.back() : parent_table->subitems.front(); + + while (parent_element->get() != selection.from_frame) { + parent_element = p_search_previous ? parent_element->prev() : parent_element->next(); + ERR_FAIL_COND_V(parent_element == nullptr, false); + } + + // Search remainder of table + if (!(p_search_previous && parent_element == parent_table->subitems.front()) && + parent_element != parent_table->subitems.back()) { + parent_element = p_search_previous ? parent_element->prev() : parent_element->next(); // Don't want to search current item + ERR_FAIL_COND_V(parent_element == nullptr, false); + + // Search for next element + if (_search_table(parent_table, parent_element, p_string, p_search_previous)) { + scroll_to_line(selection.from_frame->line + selection.from_line); + update(); + return true; + } } } + + ending_line = selection.from_frame->line + selection.from_line; + current_line = p_search_previous ? ending_line - 1 : ending_line + 1; + } else if (p_search_previous) { + current_line = ending_line; + ending_line = 0; } - return false; + // Search remainder of the file + while (current_line != ending_line) { + // Wrap around + if (current_line < 0) { + current_line = main->lines.size() - 1; + } else if (current_line >= main->lines.size()) { + current_line = 0; + } + + if (_search_line(main, current_line, p_string, char_idx, p_search_previous)) { + scroll_to_line(current_line); + update(); + return true; + } + p_search_previous ? current_line-- : current_line++; + } + + if (p_from_selection && selection.active) { + // Check contents of selection + return _search_line(main, current_line, p_string, char_idx, p_search_previous); + } else { + return false; + } } String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index e3e457d1f2..9cf94b0cd7 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -392,7 +392,8 @@ private: void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const; - bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to); + bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search); + bool _search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search); void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index a407ef21cb..5947f3b99e 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -172,7 +172,7 @@ void Slider::_notification(int p_what) { int widget_width = style->get_minimum_size().width + style->get_center_size().width; float areasize = size.height - grabber->get_size().height; style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height))); - grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().width / 2))); + grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2))); if (ticks > 1) { int grabber_offset = (grabber->get_size().height / 2 - tick->get_height() / 2); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 9dc2afdb2d..4ac73ae6b5 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -50,7 +50,7 @@ void SpinBox::_value_changed(double) { line_edit->set_text(value); } -void SpinBox::_text_entered(const String &p_string) { +void SpinBox::_text_submitted(const String &p_string) { Ref<Expression> expr; expr.instance(); @@ -172,7 +172,7 @@ void SpinBox::_line_edit_focus_exit() { return; } - _text_entered(line_edit->get_text()); + _text_submitted(line_edit->get_text()); } inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { @@ -251,7 +251,7 @@ bool SpinBox::is_editable() const { } void SpinBox::apply() { - _text_entered(line_edit->get_text()); + _text_submitted(line_edit->get_text()); } void SpinBox::_bind_methods() { @@ -283,7 +283,7 @@ SpinBox::SpinBox() { line_edit->set_align(LineEdit::ALIGN_LEFT); //connect("value_changed",this,"_value_changed"); - line_edit->connect("text_entered", callable_mp(this, &SpinBox::_text_entered), Vector<Variant>(), CONNECT_DEFERRED); + line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED); line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input)); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index e116adb64c..fb10379296 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -45,7 +45,7 @@ class SpinBox : public Range { void _range_click_timeout(); void _release_mouse(); - void _text_entered(const String &p_string); + void _text_submitted(const String &p_string); virtual void _value_changed(double) override; String prefix; String suffix; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 07ccad70b1..6f78d586f1 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -137,8 +137,11 @@ void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) { draw_control_chars = p_draw_control_chars; } -int TextEdit::Text::get_line_width(int p_line) const { +int TextEdit::Text::get_line_width(int p_line, int p_wrap_index) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); + if (p_wrap_index != -1) { + return text[p_line].data_buf->get_line_width(p_wrap_index); + } return text[p_line].data_buf->get_size().x; } @@ -1354,7 +1357,8 @@ void TextEdit::_notification(int p_what) { } } - if (line_wrap_index == line_wrap_amount && is_folded(line)) { + // is_line_folded + if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && is_line_hidden(line + 1)) { int xofs = char_ofs + char_margin + ofs_x + (cache.folded_eol_icon->get_width() / 2); if (xofs >= xmargin_beg && xofs < xmargin_end) { int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); @@ -1943,10 +1947,6 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) { } } - if (is_folded(cursor.line)) { - unfold_line(cursor.line); - } - bool brace_indent = false; // No need to indent if we are going upwards. @@ -2361,10 +2361,6 @@ void TextEdit::_backspace(bool p_word, bool p_all_to_left) { cursor_set_line(line, false); cursor_set_column(column); } else { - // One character. - if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) { - unfold_line(cursor.line - 1); - } backspace_at_cursor(); } } @@ -2692,15 +2688,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { left_margin += gutters[i].width; } - // Unfold on folded icon click. - if (is_folded(row)) { - left_margin += gutter_padding + text.get_line_width(row) - cursor.x_ofs; - if (mpos.x > left_margin && mpos.x <= left_margin + cache.folded_eol_icon->get_width() + 3) { - unfold_line(row); - return; - } - } - // minimap if (draw_minimap) { _update_minimap_click(); @@ -3703,10 +3690,6 @@ void TextEdit::center_viewport_to_cursor() { scrolling = false; minimap_clicked = false; - if (is_line_hidden(cursor.line)) { - unfold_line(cursor.line); - } - set_line_as_center_visible(cursor.line, get_cursor_wrap_index()); int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width; if (v_scroll->is_visible_in_tree()) { @@ -4124,15 +4107,6 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) { return CURSOR_ARROW; } - - // EOL fold icon. - if (is_folded(row)) { - gutter += gutter_padding + text.get_line_width(row) - cursor.x_ofs; - if (p_pos.x > gutter - 3 && p_pos.x <= gutter + cache.folded_eol_icon->get_width() + 3) { - return CURSOR_POINTING_HAND; - } - } - return get_default_cursor_shape(); } @@ -4318,6 +4292,10 @@ String TextEdit::get_line(int line) const { return text[line]; }; +bool TextEdit::has_ime_text() const { + return !ime_text.is_empty(); +} + void TextEdit::_clear() { clear_undo_history(); text.clear(); @@ -4404,7 +4382,7 @@ void TextEdit::_update_caches() { cache.selection_color = get_theme_color("selection_color"); cache.current_line_color = get_theme_color("current_line_color"); cache.line_length_guideline_color = get_theme_color("line_length_guideline_color"); - cache.code_folding_color = get_theme_color("code_folding_color"); + cache.code_folding_color = get_theme_color("code_folding_color", "CodeEdit"); cache.brace_mismatch_color = get_theme_color("brace_mismatch_color"); cache.word_highlighted_color = get_theme_color("word_highlighted_color"); cache.search_result_color = get_theme_color("search_result_color"); @@ -4417,7 +4395,7 @@ void TextEdit::_update_caches() { #endif cache.tab_icon = get_theme_icon("tab"); cache.space_icon = get_theme_icon("space"); - cache.folded_eol_icon = get_theme_icon("GuiEllipsis", "EditorIcons"); + cache.folded_eol_icon = get_theme_icon("folded_eol_icon", "CodeEdit"); TextServer::Direction dir; if (text_direction == Control::TEXT_DIRECTION_INHERITED) { @@ -4526,6 +4504,10 @@ int TextEdit::get_gutter_width(int p_gutter) const { return gutters[p_gutter].width; } +int TextEdit::get_total_gutter_width() const { + return gutters_width + gutter_padding; +} + void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) { ERR_FAIL_INDEX(p_gutter, gutters.size()); gutters.write[p_gutter].draw = p_draw; @@ -5107,14 +5089,6 @@ bool TextEdit::is_line_hidden(int p_line) const { return text.is_hidden(p_line); } -void TextEdit::fold_all_lines() { - for (int i = 0; i < text.size(); i++) { - fold_line(i); - } - _update_scrollbars(); - update(); -} - void TextEdit::unhide_all_lines() { for (int i = 0; i < text.size(); i++) { text.set_hidden(i, false); @@ -5262,151 +5236,14 @@ bool TextEdit::is_line_comment(int p_line) const { return false; } -bool TextEdit::can_fold(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), false); - if (!is_hiding_enabled()) { - return false; - } - if (p_line + 1 >= text.size()) { - return false; - } - if (text[p_line].strip_edges().size() == 0) { - return false; - } - if (is_folded(p_line)) { - return false; - } - if (is_line_hidden(p_line)) { - return false; - } - if (is_line_comment(p_line)) { - return false; - } - - int start_indent = get_indent_level(p_line); - - for (int i = p_line + 1; i < text.size(); i++) { - if (text[i].strip_edges().size() == 0) { - continue; - } - int next_indent = get_indent_level(i); - if (is_line_comment(i)) { - continue; - } else if (next_indent > start_indent) { - return true; - } else { - return false; - } - } - - return false; -} - -bool TextEdit::is_folded(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), false); - if (p_line + 1 >= text.size()) { - return false; - } - return !is_line_hidden(p_line) && is_line_hidden(p_line + 1); -} - -Vector<int> TextEdit::get_folded_lines() const { - Vector<int> folded_lines; - - for (int i = 0; i < text.size(); i++) { - if (is_folded(i)) { - folded_lines.push_back(i); - } - } - return folded_lines; -} - -void TextEdit::fold_line(int p_line) { - ERR_FAIL_INDEX(p_line, text.size()); - if (!is_hiding_enabled()) { - return; - } - if (!can_fold(p_line)) { - return; - } - - // Hide lines below this one. - int start_indent = get_indent_level(p_line); - int last_line = start_indent; - for (int i = p_line + 1; i < text.size(); i++) { - if (text[i].strip_edges().size() != 0) { - if (is_line_comment(i)) { - continue; - } else if (get_indent_level(i) > start_indent) { - last_line = i; - } else { - break; - } - } - } - for (int i = p_line + 1; i <= last_line; i++) { - set_line_as_hidden(i, true); - } - - // Fix selection. - if (is_selection_active()) { - if (is_line_hidden(selection.from_line) && is_line_hidden(selection.to_line)) { - deselect(); - } else if (is_line_hidden(selection.from_line)) { - select(p_line, 9999, selection.to_line, selection.to_column); - } else if (is_line_hidden(selection.to_line)) { - select(selection.from_line, selection.from_column, p_line, 9999); - } - } - - // Reset cursor. - if (is_line_hidden(cursor.line)) { - cursor_set_line(p_line, false, false); - cursor_set_column(get_line(p_line).length(), false); - } - _update_scrollbars(); - update(); -} - -void TextEdit::unfold_line(int p_line) { - ERR_FAIL_INDEX(p_line, text.size()); - - if (!is_folded(p_line) && !is_line_hidden(p_line)) { - return; - } - int fold_start; - for (fold_start = p_line; fold_start > 0; fold_start--) { - if (is_folded(fold_start)) { - break; - } - } - fold_start = is_folded(fold_start) ? fold_start : p_line; - - for (int i = fold_start + 1; i < text.size(); i++) { - if (is_line_hidden(i)) { - set_line_as_hidden(i, false); - } else { - break; - } - } - _update_scrollbars(); - update(); -} - -void TextEdit::toggle_fold_line(int p_line) { - ERR_FAIL_INDEX(p_line, text.size()); - - if (!is_folded(p_line)) { - fold_line(p_line); - } else { - unfold_line(p_line); - } -} - int TextEdit::get_line_count() const { return text.size(); } +int TextEdit::get_line_width(int p_line, int p_wrap_offset) const { + return text.get_line_width(p_line, p_wrap_offset); +} + void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) { ERR_FAIL_COND(p_op.type == TextOperation::TYPE_NONE); @@ -6270,18 +6107,6 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); - ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled); - ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled); - ClassDB::bind_method(D_METHOD("set_line_as_hidden", "line", "enable"), &TextEdit::set_line_as_hidden); - ClassDB::bind_method(D_METHOD("is_line_hidden", "line"), &TextEdit::is_line_hidden); - ClassDB::bind_method(D_METHOD("fold_all_lines"), &TextEdit::fold_all_lines); - ClassDB::bind_method(D_METHOD("unhide_all_lines"), &TextEdit::unhide_all_lines); - ClassDB::bind_method(D_METHOD("fold_line", "line"), &TextEdit::fold_line); - ClassDB::bind_method(D_METHOD("unfold_line", "line"), &TextEdit::unfold_line); - ClassDB::bind_method(D_METHOD("toggle_fold_line", "line"), &TextEdit::toggle_fold_line); - ClassDB::bind_method(D_METHOD("can_fold", "line"), &TextEdit::can_fold); - ClassDB::bind_method(D_METHOD("is_folded", "line"), &TextEdit::is_folded); - ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); @@ -6365,7 +6190,6 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index f963e664d1..c04d758abb 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -124,7 +124,7 @@ private: void set_draw_control_chars(bool p_draw_control_chars); int get_line_height(int p_line, int p_wrap_index) const; - int get_line_width(int p_line) const; + int get_line_width(int p_line, int p_wrap_index = -1) const; int get_max_width(bool p_exclude_hidden = false) const; void set_width(float p_width); @@ -349,11 +349,8 @@ private: void update_cursor_wrap_offset(); void _update_wrap_at(bool p_force = false); - bool line_wraps(int line) const; - int times_line_wraps(int line) const; Vector<String> get_wrap_rows_text(int p_line) const; int get_cursor_wrap_index() const; - int get_line_wrap_index_at_col(int p_line, int p_column) const; int get_char_count(); double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const; @@ -514,6 +511,7 @@ public: void set_gutter_width(int p_gutter, int p_width); int get_gutter_width(int p_gutter) const; + int get_total_gutter_width() const; void set_gutter_draw(int p_gutter, bool p_draw); bool is_gutter_drawn(int p_gutter) const; @@ -622,24 +620,19 @@ public: void insert_text_at_cursor(const String &p_text); void insert_at(const String &p_text, int at); int get_line_count() const; + int get_line_width(int p_line, int p_wrap_offset = -1) const; + int get_line_wrap_index_at_col(int p_line, int p_column) const; void set_line_as_hidden(int p_line, bool p_hidden); bool is_line_hidden(int p_line) const; - void fold_all_lines(); void unhide_all_lines(); int num_lines_from(int p_line_from, int visible_amount) const; int num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const; int get_last_unhidden_line() const; - bool can_fold(int p_line) const; - bool is_folded(int p_line) const; - Vector<int> get_folded_lines() const; - void fold_line(int p_line); - void unfold_line(int p_line); - void toggle_fold_line(int p_line); - String get_text(); String get_line(int line) const; + bool has_ime_text() const; void set_line(int line, String new_text); int get_row_height() const; void backspace_at_cursor(); @@ -701,6 +694,8 @@ public: void set_wrap_enabled(bool p_wrap_enabled); bool is_wrap_enabled() const; + bool line_wraps(int line) const; + int times_line_wraps(int line) const; void clear(); diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index f43e3d1a9d..8659ea06a2 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -295,11 +295,13 @@ void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) { void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) { pressed = p_pressed; update(); + minimum_size_changed(); } void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) { hover = p_hover; update(); + minimum_size_changed(); } void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) { @@ -310,6 +312,7 @@ void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) { void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) { click_mask = p_click_mask; update(); + minimum_size_changed(); } Ref<Texture2D> TextureButton::get_normal_texture() const { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f66cc13af5..26d881955b 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2451,10 +2451,10 @@ void Tree::_text_editor_modal_close() { return; } - _text_editor_enter(text_editor->get_text()); + _text_editor_submit(text_editor->get_text()); } -void Tree::_text_editor_enter(String p_text) { +void Tree::_text_editor_submit(String p_text) { popup_editor->hide(); if (!popup_edited_item) { @@ -4554,7 +4554,7 @@ Tree::Tree() { h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); - text_editor->connect("text_entered", callable_mp(this, &Tree::_text_editor_enter)); + text_editor->connect("text_submitted", callable_mp(this, &Tree::_text_editor_submit)); popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_modal_close)); popup_menu->connect("id_pressed", callable_mp(this, &Tree::popup_select)); value_editor->connect("value_changed", callable_mp(this, &Tree::value_editor_changed)); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 5176d01497..0571a605a5 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -449,7 +449,7 @@ private: int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod); - void _text_editor_enter(String p_text); + void _text_editor_submit(String p_text); void _text_editor_modal_close(); void value_editor_changed(double p_value); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 181fe606c8..a4393d682f 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -876,6 +876,17 @@ void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) { RenderingServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix); } +void CanvasItem::draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset) { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + RenderingServer::get_singleton()->canvas_item_add_animation_slice(canvas_item, p_animation_length, p_slice_begin, p_slice_end, p_offset); +} + +void CanvasItem::draw_end_animation() { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + + RenderingServer::get_singleton()->canvas_item_add_animation_slice(canvas_item, 1, 0, 2, 0); +} void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -1159,6 +1170,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform, DEFVAL(0.0), DEFVAL(Size2(1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix); + ClassDB::bind_method(D_METHOD("draw_animation_slice", "animation_length", "slice_begin", "slice_end", "offset"), &CanvasItem::draw_animation_slice, DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("draw_end_animation"), &CanvasItem::draw_end_animation); ClassDB::bind_method(D_METHOD("get_transform"), &CanvasItem::get_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &CanvasItem::get_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform_with_canvas"), &CanvasItem::get_global_transform_with_canvas); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 1c64cafab8..afdd18d76b 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -356,6 +356,8 @@ public: void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); void draw_set_transform_matrix(const Transform2D &p_matrix); + void draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset = 0); + void draw_end_animation(); static CanvasItem *get_current_item_drawn(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index c39b8005e4..622c271935 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -292,7 +292,7 @@ void Node::_propagate_exit_tree() { void Node::move_child(Node *p_child, int p_pos) { ERR_FAIL_NULL(p_child); - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, "Invalid new child position: " + itos(p_pos) + "."); + ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos)); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); @@ -1037,8 +1037,11 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { void Node::add_child(Node *p_child, bool p_legible_unique_name) { ERR_FAIL_NULL(p_child); - ERR_FAIL_COND_MSG(p_child == this, "Can't add child '" + p_child->get_name() + "' to itself."); // adding to itself! - ERR_FAIL_COND_MSG(p_child->data.parent, "Can't add child '" + p_child->get_name() + "' to '" + get_name() + "', already has a parent '" + p_child->data.parent->get_name() + "'."); //Fail if node has a parent + ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! + ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_child->is_a_parent_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); +#endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); /* Validate name */ @@ -1049,7 +1052,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) { void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { ERR_FAIL_NULL(p_sibling); - ERR_FAIL_COND_MSG(p_sibling == this, "Can't add sibling '" + p_sibling->get_name() + "' to itself."); // adding to itself! + ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead."); get_parent()->add_child(p_sibling, p_legible_unique_name); @@ -1104,7 +1107,7 @@ void Node::remove_child(Node *p_child) { } } - ERR_FAIL_COND_MSG(idx == -1, "Cannot remove child node " + p_child->get_name() + " as it is not a child of this node."); + ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name())); //ERR_FAIL_COND( p_child->data.blocked > 0 ); //if (data.scene) { does not matter @@ -2188,7 +2191,7 @@ void Node::_replace_connections_target(Node *p_new_target) { if (c.flags & CONNECT_PERSIST) { c.signal.get_object()->disconnect(c.signal.get_name(), Callable(this, c.callable.get_method())); bool valid = p_new_target->has_method(c.callable.get_method()) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.callable.get_method()); - ERR_CONTINUE_MSG(!valid, "Attempt to connect signal '" + c.signal.get_object()->get_class() + "." + c.signal.get_name() + "' to nonexistent method '" + c.callable.get_object()->get_class() + "." + c.callable.get_method() + "'."); + ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", c.signal.get_object()->get_class(), c.signal.get_name(), c.callable.get_object()->get_class(), c.callable.get_method())); c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.binds, c.flags); } } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index e4ba93feec..f588e000f9 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -413,7 +413,6 @@ bool SceneTree::physics_process(float p_time) { _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack flush_transform_notifications(); - call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds"); root_lock--; _flush_delete_queue(); @@ -445,7 +444,6 @@ bool SceneTree::process(float p_time) { _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications - call_group_flags(GROUP_CALL_REALTIME, "_viewports", "update_worlds"); root_lock--; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5369792194..59ec0e6345 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -183,19 +183,6 @@ public: ///////////////////////////////////// -void Viewport::update_worlds() { - if (!is_inside_tree()) { - 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()); -} - void Viewport::_collision_object_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { Transform3D object_transform = p_object->get_global_transform(); Transform3D camera_transform = p_camera->get_global_transform(); @@ -441,8 +428,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 +484,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 +1138,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 +1153,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()); } } @@ -3525,8 +3505,6 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("input", "event", "in_local_coords"), &Viewport::input, DEFVAL(false)); ClassDB::bind_method(D_METHOD("unhandled_input", "event", "in_local_coords"), &Viewport::unhandled_input, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("update_worlds"), &Viewport::update_worlds); - ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d); ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 2d7f5101c2..d905e44a82 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -397,8 +397,6 @@ private: void _gui_input_event(Ref<InputEvent> p_event); - void update_worlds(); - _FORCE_INLINE_ Transform2D _get_input_pre_xform() const; Ref<InputEvent> _make_input_local(const Ref<InputEvent> &ev); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 332976a18d..2a97ae3acf 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -65,7 +65,7 @@ #include "scene/2d/sprite_2d.h" #include "scene/2d/tile_map.h" #include "scene/2d/touch_screen_button.h" -#include "scene/2d/visibility_notifier_2d.h" +#include "scene/2d/visible_on_screen_notifier_2d.h" #include "scene/animation/animation_blend_space_1d.h" #include "scene/animation/animation_blend_space_2d.h" #include "scene/animation/animation_blend_tree.h" @@ -229,7 +229,7 @@ #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body_3d.h" -#include "scene/3d/visibility_notifier_3d.h" +#include "scene/3d/visible_on_screen_notifier_3d.h" #include "scene/3d/voxel_gi.h" #include "scene/3d/world_environment.h" #include "scene/3d/xr_nodes.h" @@ -513,8 +513,8 @@ void register_scene_types() { ClassDB::register_class<Curve3D>(); ClassDB::register_class<Path3D>(); ClassDB::register_class<PathFollow3D>(); - ClassDB::register_class<VisibilityNotifier3D>(); - ClassDB::register_class<VisibilityEnabler3D>(); + ClassDB::register_class<VisibleOnScreenNotifier3D>(); + ClassDB::register_class<VisibleOnScreenEnabler3D>(); ClassDB::register_class<WorldEnvironment>(); ClassDB::register_class<RemoteTransform3D>(); @@ -655,8 +655,8 @@ void register_scene_types() { ClassDB::register_class<CollisionShape2D>(); ClassDB::register_class<CollisionPolygon2D>(); ClassDB::register_class<RayCast2D>(); - ClassDB::register_class<VisibilityNotifier2D>(); - ClassDB::register_class<VisibilityEnabler2D>(); + ClassDB::register_class<VisibleOnScreenNotifier2D>(); + ClassDB::register_class<VisibleOnScreenEnabler2D>(); ClassDB::register_class<Polygon2D>(); ClassDB::register_class<Skeleton2D>(); ClassDB::register_class<Bone2D>(); @@ -966,8 +966,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("VehicleWheel", "VehicleWheel3D"); ClassDB::add_compatibility_class("ViewportContainer", "SubViewportContainer"); ClassDB::add_compatibility_class("Viewport", "SubViewport"); - ClassDB::add_compatibility_class("VisibilityEnabler", "VisibilityEnabler3D"); - ClassDB::add_compatibility_class("VisibilityNotifier", "VisibilityNotifier3D"); + ClassDB::add_compatibility_class("VisibilityEnabler", "VisibleOnScreenEnabler3D"); + ClassDB::add_compatibility_class("VisibilityNotifier", "VisibleOnScreenNotifier3D"); ClassDB::add_compatibility_class("VisualServer", "RenderingServer"); ClassDB::add_compatibility_class("VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant"); ClassDB::add_compatibility_class("VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc"); @@ -986,6 +986,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("World", "World3D"); ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D"); ClassDB::add_compatibility_class("Light2D", "PointLight2D"); + ClassDB::add_compatibility_class("VisibilityNotifier2D", "VisibleOnScreenNotifier2D"); + ClassDB::add_compatibility_class("VisibilityNotifier3D", "VisibleOnScreenNotifier3D"); #endif /* DISABLE_DEPRECATED */ diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b91a5c0b7f..a4228d48b4 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -445,7 +445,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_readonly_color", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f)); theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1)); theme->set_color("selection_color", "TextEdit", control_selection_color); - theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8)); theme->set_color("caret_color", "TextEdit", control_font_color); theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0)); @@ -469,6 +468,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png)); theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png)); theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png)); + theme->set_icon("folded_eol_icon", "CodeEdit", make_icon(ellipsis_png)); theme->set_font("font", "CodeEdit", Ref<Font>()); theme->set_font_size("font_size", "CodeEdit", -1); @@ -487,8 +487,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8)); theme->set_color("breakpoint_color", "CodeEdit", Color(0.9, 0.29, 0.3)); theme->set_color("executing_line_color", "CodeEdit", Color(0.98, 0.89, 0.27)); - theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("current_line_color", "CodeEdit", Color(0.25, 0.25, 0.26, 0.8)); + theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8)); theme->set_color("caret_color", "CodeEdit", control_font_color); theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0)); theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2)); diff --git a/scene/resources/default_theme/ellipsis.png b/scene/resources/default_theme/ellipsis.png Binary files differnew file mode 100644 index 0000000000..c949e2c95b --- /dev/null +++ b/scene/resources/default_theme/ellipsis.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index 190f2a03d9..7d747e3c9e 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -74,6 +74,10 @@ static const unsigned char dropdown_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char ellipsis_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc9, 0x11, 0xce, 0xcc, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x78, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x95, 0xd1, 0x31, 0xa, 0xc2, 0x50, 0x10, 0x4, 0xd0, 0xb7, 0x1f, 0xf, 0x60, 0x67, 0xa, 0xf, 0x12, 0x6f, 0x60, 0xe9, 0x51, 0x5, 0x3d, 0x44, 0xee, 0x61, 0xa1, 0xa5, 0xf6, 0x81, 0xb5, 0xc8, 0x47, 0x82, 0x84, 0x4f, 0xb2, 0xdd, 0xb0, 0x33, 0xb3, 0xb3, 0x4c, 0x40, 0x66, 0xee, 0x71, 0xc2, 0x1, 0x3b, 0xcb, 0x33, 0xe2, 0x85, 0x21, 0x22, 0xde, 0x51, 0x45, 0x97, 0x86, 0x60, 0xc9, 0xe0, 0x5a, 0xea, 0xa5, 0xb5, 0x22, 0x95, 0xdb, 0x97, 0x1a, 0xf, 0x6e, 0xb8, 0xcf, 0x8, 0x2d, 0xdc, 0x95, 0xd9, 0x22, 0xfe, 0x9c, 0x9b, 0x38, 0x32, 0xf3, 0x8c, 0xe3, 0x86, 0xa8, 0xf0, 0x28, 0x18, 0x4c, 0xf, 0xaf, 0x9d, 0x11, 0x43, 0xf0, 0xab, 0xa3, 0x47, 0xa7, 0x5d, 0xc7, 0xd3, 0x54, 0xc7, 0xe7, 0xb, 0xb9, 0xce, 0x1f, 0xc6, 0x2d, 0x99, 0x55, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char error_icon_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x0, 0x1, 0x0, 0x2, 0x10, 0x0, 0x1, 0x14, 0xc2, 0xc0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 2a60f54fdd..27f0c50a79 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -575,6 +575,7 @@ Error ResourceLoaderText::load() { int_resources[id] = res; //always assign int resources if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + res->set_subindex(id); } if (progress && resources_total > 0) { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 1e78561bec..fee9f92ad7 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -537,12 +537,16 @@ Array SurfaceTool::commit_to_arrays() { int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; Vector<int> array; array.resize(varr_len * count); + array.fill(0); int *w = array.ptrw(); for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - ERR_CONTINUE(v.bones.size() != count); + if (v.bones.size() > count) { + ERR_PRINT_ONCE(vformat("Invalid bones size %d vs count %d", v.bones.size(), count)); + continue; + } for (int j = 0; j < count; j++) { w[idx * count + j] = v.bones[j]; @@ -557,12 +561,16 @@ Array SurfaceTool::commit_to_arrays() { int count = skin_weights == SKIN_8_WEIGHTS ? 8 : 4; array.resize(varr_len * count); + array.fill(0.0f); float *w = array.ptrw(); for (uint32_t idx = 0; idx < vertex_array.size(); idx++) { const Vertex &v = vertex_array[idx]; - ERR_CONTINUE(v.weights.size() != count); + if (v.weights.size() > count) { + ERR_PRINT_ONCE(vformat("Invalid weight size %d vs count %d", v.weights.size(), count)); + continue; + } for (int j = 0; j < count; j++) { w[idx * count + j] = v.weights[j]; diff --git a/scene/resources/theme.h b/scene/resources/theme.h index cc4b716aa2..fe64fd7290 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -44,6 +44,7 @@ class Theme : public Resource { #ifdef TOOLS_ENABLED friend class ThemeItemImportTree; friend class ThemeItemEditorDialog; + friend class ThemeTypeEditor; #endif public: diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 0a0742753f..8af4deda83 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -32,290 +32,12 @@ #include "core/config/project_settings.h" #include "scene/2d/camera_2d.h" -#include "scene/2d/visibility_notifier_2d.h" +#include "scene/2d/visible_on_screen_notifier_2d.h" #include "scene/main/window.h" #include "servers/navigation_server_2d.h" #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); } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 38abf3d7ad..65f89c8f64 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -35,7 +35,7 @@ #include "core/io/resource.h" #include "servers/physics_server_2d.h" -class VisibilityNotifier2D; +class VisibleOnScreenNotifier2D; class Viewport; struct SpatialIndexer2D; @@ -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(); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index e811cbf57a..8d9b1fd6ec 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -33,210 +33,19 @@ #include "core/math/camera_matrix.h" #include "core/math/octree.h" #include "scene/3d/camera_3d.h" -#include "scene/3d/visibility_notifier_3d.h" +#include "scene/3d/visible_on_screen_notifier_3d.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" -struct SpatialIndexer { - Octree<VisibilityNotifier3D> octree; - - struct NotifierData { - AABB aabb; - OctreeElementID id; - }; - - Map<VisibilityNotifier3D *, NotifierData> notifiers; - struct CameraData { - Map<VisibilityNotifier3D *, uint64_t> notifiers; - }; - - Map<Camera3D *, CameraData> cameras; - - enum { - VISIBILITY_CULL_MAX = 32768 - }; - - Vector<VisibilityNotifier3D *> cull; - - bool changed; - uint64_t pass; - uint64_t last_frame; - - void _notifier_add(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - ERR_FAIL_COND(notifiers.has(p_notifier)); - notifiers[p_notifier].aabb = p_rect; - notifiers[p_notifier].id = octree.create(p_notifier, p_rect); - changed = true; - } - - void _notifier_update(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { - Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - if (E->get().aabb == p_rect) { - return; - } - - E->get().aabb = p_rect; - octree.move(E->get().id, E->get().aabb); - changed = true; - } - - void _notifier_remove(VisibilityNotifier3D *p_notifier) { - Map<VisibilityNotifier3D *, NotifierData>::Element *E = notifiers.find(p_notifier); - ERR_FAIL_COND(!E); - - octree.erase(E->get().id); - notifiers.erase(p_notifier); - - List<Camera3D *> removed; - for (Map<Camera3D *, CameraData>::Element *F = cameras.front(); F; F = F->next()) { - Map<VisibilityNotifier3D *, uint64_t>::Element *G = F->get().notifiers.find(p_notifier); - - if (G) { - F->get().notifiers.erase(G); - removed.push_back(F->key()); - } - } - - while (!removed.is_empty()) { - p_notifier->_exit_camera(removed.front()->get()); - removed.pop_front(); - } - - changed = true; - } - - void _add_camera(Camera3D *p_camera) { - ERR_FAIL_COND(cameras.has(p_camera)); - CameraData vd; - cameras[p_camera] = vd; - changed = true; - } - - void _update_camera(Camera3D *p_camera) { - Map<Camera3D *, CameraData>::Element *E = cameras.find(p_camera); - ERR_FAIL_COND(!E); - changed = true; - } - - void _remove_camera(Camera3D *p_camera) { - ERR_FAIL_COND(!cameras.has(p_camera)); - List<VisibilityNotifier3D *> removed; - for (Map<VisibilityNotifier3D *, uint64_t>::Element *E = cameras[p_camera].notifiers.front(); E; E = E->next()) { - removed.push_back(E->key()); - } - - while (!removed.is_empty()) { - removed.front()->get()->_exit_camera(p_camera); - removed.pop_front(); - } - - cameras.erase(p_camera); - } - - void _update(uint64_t p_frame) { - if (p_frame == last_frame) { - return; - } - last_frame = p_frame; - - if (!changed) { - return; - } - - for (Map<Camera3D *, CameraData>::Element *E = cameras.front(); E; E = E->next()) { - pass++; - - Camera3D *c = E->key(); - - Vector<Plane> planes = c->get_frustum(); - - int culled = octree.cull_convex(planes, cull.ptrw(), cull.size()); - - VisibilityNotifier3D **ptr = cull.ptrw(); - - List<VisibilityNotifier3D *> added; - List<VisibilityNotifier3D *> removed; - - for (int i = 0; i < culled; i++) { - //notifiers in frustum - - Map<VisibilityNotifier3D *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]); - if (!H) { - E->get().notifiers.insert(ptr[i], pass); - added.push_back(ptr[i]); - } else { - H->get() = pass; - } - } - - for (Map<VisibilityNotifier3D *, uint64_t>::Element *F = E->get().notifiers.front(); F; F = F->next()) { - if (F->get() != pass) { - removed.push_back(F->key()); - } - } - - while (!added.is_empty()) { - added.front()->get()->_enter_camera(E->key()); - added.pop_front(); - } - - while (!removed.is_empty()) { - E->get().notifiers.erase(removed.front()->get()); - removed.front()->get()->_exit_camera(E->key()); - removed.pop_front(); - } - } - changed = false; - } - - SpatialIndexer() { - pass = 0; - last_frame = 0; - changed = false; - cull.resize(VISIBILITY_CULL_MAX); - } -}; - void World3D::_register_camera(Camera3D *p_camera) { #ifndef _3D_DISABLED - indexer->_add_camera(p_camera); -#endif -} - -void World3D::_update_camera(Camera3D *p_camera) { -#ifndef _3D_DISABLED - indexer->_update_camera(p_camera); + cameras.insert(p_camera); #endif } void World3D::_remove_camera(Camera3D *p_camera) { #ifndef _3D_DISABLED - indexer->_remove_camera(p_camera); -#endif -} - -void World3D::_register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { -#ifndef _3D_DISABLED - indexer->_notifier_add(p_notifier, p_rect); -#endif -} - -void World3D::_update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect) { -#ifndef _3D_DISABLED - indexer->_notifier_update(p_notifier, p_rect); -#endif -} - -void World3D::_remove_notifier(VisibilityNotifier3D *p_notifier) { -#ifndef _3D_DISABLED - indexer->_notifier_remove(p_notifier); -#endif -} - -void World3D::_update(uint64_t p_frame) { -#ifndef _3D_DISABLED - indexer->_update(p_frame); + cameras.erase(p_camera); #endif } @@ -307,12 +116,6 @@ PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { return PhysicsServer3D::get_singleton()->space_get_direct_state(space); } -void World3D::get_camera_list(List<Camera3D *> *r_cameras) { - for (Map<Camera3D *, SpatialIndexer::CameraData>::Element *E = indexer->cameras.front(); E; E = E->next()) { - r_cameras->push_back(E->key()); - } -} - void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_space"), &World3D::get_space); ClassDB::bind_method(D_METHOD("get_navigation_map"), &World3D::get_navigation_map); @@ -349,20 +152,10 @@ World3D::World3D() { NavigationServer3D::get_singleton()->map_set_active(navigation_map, true); NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/3d/default_cell_size", 0.3)); NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/3d/default_edge_connection_margin", 0.3)); - -#ifdef _3D_DISABLED - indexer = nullptr; -#else - indexer = memnew(SpatialIndexer); -#endif } World3D::~World3D() { PhysicsServer3D::get_singleton()->free(space); RenderingServer::get_singleton()->free(scenario); NavigationServer3D::get_singleton()->free(navigation_map); - -#ifndef _3D_DISABLED - memdelete(indexer); -#endif } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 4e2717a2bb..2c5be35609 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -38,7 +38,7 @@ #include "servers/rendering_server.h" class Camera3D; -class VisibilityNotifier3D; +class VisibleOnScreenNotifier3D; struct SpatialIndexer; class World3D : public Resource { @@ -48,27 +48,21 @@ private: RID space; RID navigation_map; RID scenario; - SpatialIndexer *indexer; + Ref<Environment> environment; Ref<Environment> fallback_environment; Ref<CameraEffects> camera_effects; + Set<Camera3D *> cameras; + protected: static void _bind_methods(); friend class Camera3D; - friend class VisibilityNotifier3D; void _register_camera(Camera3D *p_camera); - void _update_camera(Camera3D *p_camera); void _remove_camera(Camera3D *p_camera); - void _register_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _update_notifier(VisibilityNotifier3D *p_notifier, const AABB &p_rect); - void _remove_notifier(VisibilityNotifier3D *p_notifier); - friend class Viewport; - void _update(uint64_t p_frame); - public: RID get_space() const; RID get_navigation_map() const; @@ -83,7 +77,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - void get_camera_list(List<Camera3D *> *r_cameras); + _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; } PhysicsDirectSpaceState3D *get_direct_space_state(); |