diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 5fc28cb083f..4524ef6c92b 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -158,6 +158,7 @@ void Spatial::_notification(int p_what) { } else { data.C = nullptr; } + _update_visible_in_tree(); if (data.toplevel && !Engine::get_singleton()->is_editor_hint()) { if (data.parent) { @@ -217,6 +218,8 @@ void Spatial::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; data.toplevel_active = false; + + _update_visible_in_tree(); _disable_client_physics_interpolation(); } break; case NOTIFICATION_ENTER_WORLD: { @@ -818,6 +821,36 @@ Ref Spatial::get_world() const { return data.viewport->find_world(); } +void Spatial::_update_visible_in_tree() { + Spatial *parent = get_parent_spatial(); + + bool propagate_visible = parent ? parent->data.visible_in_tree : true; + + // Only propagate visible when entering tree if we are visible. + propagate_visible &= is_visible(); + + _propagate_visible_in_tree(propagate_visible); +} + +void Spatial::_propagate_visible_in_tree(bool p_visible_in_tree) { + // If any node is invisible, the propagation changes to invisible below. + p_visible_in_tree &= is_visible(); + + // No change. + if (data.visible_in_tree == p_visible_in_tree) { + return; + } + + data.visible_in_tree = p_visible_in_tree; + + for (int32_t n = 0; n < get_child_count(); n++) { + Spatial *s = Object::cast_to(get_child(n)); + if (s) { + s->_propagate_visible_in_tree(p_visible_in_tree); + } + } +} + void Spatial::_propagate_visibility_changed() { notification(NOTIFICATION_VISIBILITY_CHANGED); emit_signal(SceneStringNames::get_singleton()->visibility_changed); @@ -877,6 +910,10 @@ void Spatial::show() { return; } + bool parent_visible = get_parent_spatial() ? get_parent_spatial()->data.visible_in_tree : true; + if (parent_visible) { + _propagate_visible_in_tree(true); + } _propagate_visibility_changed(); } @@ -891,10 +928,14 @@ void Spatial::hide() { return; } + bool parent_visible = get_parent_spatial() ? get_parent_spatial()->data.visible_in_tree : true; + if (parent_visible) { + _propagate_visible_in_tree(false); + } _propagate_visibility_changed(); } -bool Spatial::is_visible_in_tree() const { +bool Spatial::_is_visible_in_tree_reference() const { const Spatial *s = this; while (s) { @@ -1207,6 +1248,7 @@ Spatial::Spatial() : data.viewport = nullptr; data.inside_world = false; data.visible = true; + data.visible_in_tree = true; data.disable_scale = false; data.vi_visible = true; data.merging_allowed = true; diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index 2ecbae25a56..b48aa78514b 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -121,6 +121,7 @@ private: bool notify_transform : 1; bool visible : 1; + bool visible_in_tree : 1; bool disable_scale : 1; // Scene tree interpolation @@ -155,6 +156,9 @@ private: void _notify_dirty(); void _propagate_transform_changed(Spatial *p_origin); + void _update_visible_in_tree(); + bool _is_visible_in_tree_reference() const; + void _propagate_visible_in_tree(bool p_visible_in_tree); void _propagate_visibility_changed(); void _propagate_merging_allowed(bool p_merging_allowed); @@ -293,7 +297,15 @@ public: bool is_visible() const; void show(); void hide(); - bool is_visible_in_tree() const; + bool is_visible_in_tree() const { +#if DEV_ENABLED + // As this is newly introduced, regression test the old method against the new in DEV builds. + // If no regressions, this can be removed after a beta. + bool visible = _is_visible_in_tree_reference(); + ERR_FAIL_COND_V_MSG(data.visible_in_tree != visible, visible, "is_visible_in_tree regression detected, recovering."); +#endif + return data.visible_in_tree; + } void force_update_transform();