156 lines
4.9 KiB
Rust
156 lines
4.9 KiB
Rust
//! Maps authoring [`EditorVisibility`] to runtime [`Visibility`].
|
|
|
|
use crate::{EditorVisibility, LevelObject};
|
|
use bevy::prelude::*;
|
|
|
|
/// Establishes the runtime visibility hierarchy synchronously for all authored actors.
|
|
///
|
|
/// Scene loading can occur between scheduled hydration sets, so callers that materialize a scene
|
|
/// must run this before generated render children can be attached in the same frame.
|
|
pub fn initialize_level_object_visibility_hierarchy(world: &mut World) {
|
|
let targets: Vec<(Entity, EditorVisibility)> = world
|
|
.query_filtered::<(Entity, Option<&EditorVisibility>), With<LevelObject>>()
|
|
.iter(world)
|
|
.map(|(entity, visibility)| (entity, visibility.copied().unwrap_or_default()))
|
|
.collect();
|
|
|
|
for (entity, editor_visibility) in targets {
|
|
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
|
entity_mut.insert((editor_visibility, visibility_from_editor(editor_visibility)));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[expect(
|
|
clippy::type_complexity,
|
|
reason = "the query mirrors Bevy's three visibility hierarchy components"
|
|
)]
|
|
pub fn ensure_level_object_visibility_hierarchy(
|
|
mut commands: Commands,
|
|
query: Query<
|
|
(
|
|
Entity,
|
|
&EditorVisibility,
|
|
Option<&Visibility>,
|
|
Option<&InheritedVisibility>,
|
|
Option<&ViewVisibility>,
|
|
),
|
|
With<LevelObject>,
|
|
>,
|
|
) {
|
|
for (entity, editor, visibility, inherited_visibility, view_visibility) in &query {
|
|
let mut entity_commands = commands.entity(entity);
|
|
if visibility.is_none() {
|
|
entity_commands.insert(visibility_from_editor(*editor));
|
|
}
|
|
if inherited_visibility.is_none() {
|
|
entity_commands.insert(InheritedVisibility::default());
|
|
}
|
|
if view_visibility.is_none() {
|
|
entity_commands.insert(ViewVisibility::default());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[expect(
|
|
clippy::type_complexity,
|
|
reason = "the query expresses Bevy change detection at the system boundary"
|
|
)]
|
|
pub fn sync_editor_visibility(
|
|
mut query: Query<
|
|
(&EditorVisibility, &mut Visibility),
|
|
(
|
|
With<LevelObject>,
|
|
Or<(Changed<EditorVisibility>, Added<EditorVisibility>)>,
|
|
),
|
|
>,
|
|
) {
|
|
for (editor, mut visibility) in query.iter_mut() {
|
|
*visibility = visibility_from_editor(*editor);
|
|
}
|
|
}
|
|
|
|
#[expect(
|
|
clippy::type_complexity,
|
|
reason = "the query expresses Bevy spawn detection at the system boundary"
|
|
)]
|
|
pub fn init_editor_visibility_on_spawn(
|
|
mut query: Query<
|
|
(&EditorVisibility, &mut Visibility),
|
|
(
|
|
With<LevelObject>,
|
|
Added<LevelObject>,
|
|
With<EditorVisibility>,
|
|
),
|
|
>,
|
|
) {
|
|
for (editor, mut visibility) in query.iter_mut() {
|
|
*visibility = visibility_from_editor(*editor);
|
|
}
|
|
}
|
|
|
|
pub fn visibility_from_editor(editor: EditorVisibility) -> Visibility {
|
|
if editor.visible {
|
|
Visibility::Visible
|
|
} else {
|
|
Visibility::Hidden
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{
|
|
ensure_level_object_visibility_hierarchy, initialize_level_object_visibility_hierarchy,
|
|
};
|
|
use crate::{EditorVisibility, LevelObject};
|
|
use bevy::prelude::*;
|
|
|
|
#[test]
|
|
fn level_object_visibility_hierarchy_is_inserted_for_generated_children() {
|
|
let mut app = App::new();
|
|
app.add_systems(Update, ensure_level_object_visibility_hierarchy);
|
|
|
|
let entity = app
|
|
.world_mut()
|
|
.spawn((LevelObject, EditorVisibility { visible: false }))
|
|
.id();
|
|
|
|
app.update();
|
|
|
|
let world = app.world();
|
|
assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Hidden));
|
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn immediate_initializer_establishes_hierarchy_before_scheduled_hydration() {
|
|
let mut world = World::new();
|
|
let entity = world
|
|
.spawn((LevelObject, EditorVisibility { visible: false }))
|
|
.id();
|
|
|
|
initialize_level_object_visibility_hierarchy(&mut world);
|
|
|
|
assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Hidden));
|
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn immediate_initializer_migrates_missing_authored_visibility() {
|
|
let mut world = World::new();
|
|
let entity = world.spawn(LevelObject).id();
|
|
|
|
initialize_level_object_visibility_hierarchy(&mut world);
|
|
|
|
assert_eq!(
|
|
world.get::<EditorVisibility>(entity),
|
|
Some(&EditorVisibility::default())
|
|
);
|
|
assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Visible));
|
|
assert!(world.get::<InheritedVisibility>(entity).is_some());
|
|
assert!(world.get::<ViewVisibility>(entity).is_some());
|
|
}
|
|
}
|