//! `scene_basic` — a runnable tour of the Stage 3 scene graph. //! //! Run with: //! ```sh //! cargo run -p oxide-examples --bin scene_basic //! ``` //! //! This example has no window or GPU dependency. It builds a small entity //! hierarchy, prints each node's local and resolved world transform, reparents //! a node, and round-trips the whole scene through RON — so the Stage 3 scene //! API can be reviewed by eye. use oxide_engine::prelude::*; use oxide_engine::scene::DespawnPolicy; fn main() { env_logger::init(); println!("Oxide scene demo — Stage 3 scene graph\n"); let mut scene = Scene::new(); // A little solar-system-ish hierarchy: sun → planet → moon, plus a probe. let sun = scene.spawn("sun", Transform::from_translation(Vec3::new(0.0, 0.0, 0.0))); let planet = scene.spawn_child( sun, "planet", Transform::from_trs( Vec3::new(10.0, 0.0, 0.0), Quat::from_rotation_y(90_f32.to_radians()), Vec3::ONE, ), ); let moon = scene.spawn_child( planet, "moon", Transform::from_translation(Vec3::new(0.0, 0.0, 2.0)), ); let probe = scene.spawn_child(planet, "probe", Transform::from_translation(Vec3::Y)); println!("== Hierarchy & world transforms =="); print_tree(&scene); // World transforms compose down the chain: the moon inherits the planet's // rotation, so its local +Z offset lands along the world axes accordingly. let moon_world = scene.world_transform(moon).unwrap(); println!( "\nmoon local pos : {}", scene.local_transform(moon).unwrap().translation ); println!("moon world pos : {}", moon_world.translation); // Reparent the probe directly under the sun and observe its world transform // change (its local transform is preserved). println!("\n== Reparent probe: planet → sun =="); scene.set_parent(probe, Some(sun)).unwrap(); println!( "probe world pos: {}", scene.world_transform(probe).unwrap().translation ); // Disable a node (later systems will skip disabled subtrees). scene.set_enabled(moon, false); println!( "\nmoon enabled? : {}", scene.is_enabled(moon).unwrap_or(true) ); // Serialize → deserialize round-trip. println!("\n== RON serialization =="); let ron = scene.to_ron().expect("serialize"); println!("{ron}"); let restored = Scene::from_ron(&ron).expect("deserialize"); println!( "restored {} entities, {} roots", restored.len(), restored.roots().len() ); // Despawn the planet, detaching its children up to its parent. println!("\n== Despawn planet (detach children) =="); scene.despawn(planet, DespawnPolicy::DetachChildren); print_tree(&scene); } /// Prints the scene as an indented tree with each node's world position. fn print_tree(scene: &Scene) { let worlds = scene.world_transforms(); for &root in scene.roots() { print_node(scene, root, 0, &worlds); } } fn print_node( scene: &Scene, entity: oxide_engine::scene::Entity, depth: usize, worlds: &std::collections::HashMap, ) { let indent = " ".repeat(depth); let name = scene.name(entity).unwrap_or_default(); let enabled = scene.is_enabled(entity).unwrap_or(true); let world = worlds .get(&entity) .map(|t| t.translation) .unwrap_or(Vec3::ZERO); let tag = if enabled { "" } else { " (disabled)" }; println!("{indent}- {name}{tag} world={world}"); for &child in scene.children(entity) { print_node(scene, child, depth + 1, worlds); } }