Skip to main content

solar_system/
solar_system.rs

1//! # Solar System
2//!
3//! A three-level parent-child hierarchy: **Sun → Planet → Moon**.
4//! The Sun drives the entire system — rotating the Sun automatically
5//! carries the Planet (and Moon) around it, demonstrating how Vertra
6//! propagates world transforms down the object tree.
7//!
8//! Editor mode is enabled on startup so you can inspect and manipulate
9//! any object with the gizmo.  The `on_editor_event` callback logs every
10//! gizmo mode change and axis drag to stdout.
11//!
12//! **Run:**
13//! ```sh
14//! cargo run --example solar_system
15//! ```
16//!
17//! **Editor keybinds:**
18//! | Key        | Action                               |
19//! |------------|--------------------------------------|
20//! | `T`        | Switch to Translate gizmo            |
21//! | `R`        | Switch to Rotate gizmo               |
22//! | `E`        | Switch to Scale gizmo                |
23//! | `F`        | Frame / focus on selected object     |
24//! | `Escape`   | Exit editor mode (enter play mode)   |
25//! | `RMB drag` | Orbit camera                         |
26//! | `MMB drag` | Pan camera                           |
27//! | Scroll     | Dolly in / out                       |
28
29use vertra::camera::Camera;
30use vertra::window::Window;
31use vertra::transform::Transform;
32use vertra::geometry::Geometry;
33use vertra::objects::Object;
34use vertra::editor::{EditorStateEvent, GizmoMode, DragAxis};
35
36struct AppState {
37    sun_id: Option<usize>,
38    earth_id: Option<usize>,
39    moon_id: Option<usize>,
40}
41
42fn main() {
43    let initial_state = AppState {
44        sun_id: None,
45        earth_id: None,
46        moon_id: None,
47    };
48
49    Window::new(initial_state)
50        .with_title("Simple Solar Simulation")
51        .with_camera(
52            Camera::new()
53                .with_position([0.0, 8.0, -12.0])
54                .with_rotation(90.0, -30.0)
55        )
56        .on_startup(|state, scene, _| {
57            // 1. The Sun (Center)
58            let sun = Object {
59                name: "Sun".to_string(),
60                str_id: "sun".to_string (),
61                transform: Transform::from_position(0.0, 0.0, 0.0),
62                geometry: Some(Geometry::Cube { size: 2.0 }),
63                color: [1.0, 0.9, 0.2, 1.0],
64                ..Default::default()
65            };
66            let sun_id = scene.spawn(sun, None);
67
68            // 2. The Planet (Child)
69            let planet = Object {
70                name: "Planet".to_string(),
71                str_id: "earth".to_string(),
72                transform: Transform::from_position(6.0, 0.0, 0.0),
73                geometry: Some(Geometry::Sphere { radius: 0.8, subdivisions: 24 }),
74                color: [0.2, 0.5, 1.0, 1.0],
75                ..Default::default()
76            };
77            let planet_id = scene.spawn(planet, Some(sun_id));
78
79            // 3. The Moon (Grandchild)
80            let moon = Object {
81                name: "Moon".to_string(),
82                str_id: "moon".to_string(),
83                transform: Transform::from_position(1.5, 0.0, 0.0),
84                geometry: Some(Geometry::Sphere { radius: 0.3, subdivisions: 16 }),
85                color: [0.7, 0.7, 0.7, 1.0],
86                ..Default::default()
87            };
88            scene.spawn(moon, Some(planet_id));
89
90            state.sun_id    = scene.world.get_id("sun");
91            state.earth_id  = scene.world.get_id("earth");
92            state.moon_id   = scene.world.get_id("moon");
93            scene.enable_editor_mode();
94        })
95        .on_update(|state, scene, ctx| {
96            // Rotate the Sun (the planet will orbit automatically)
97            if let Some(sun) = state.sun_id.and_then(|id| scene.world.get_mut(id)) {
98                sun.transform.rotation[1] += 30.0 * ctx.dt;
99            }
100
101            // Rotate the Planet (Earth)
102            if let Some(planet) = state.earth_id.and_then(|id| scene.world.get_mut(id)) {
103                planet.transform.rotation[1] += 100.0 * ctx.dt;
104            }
105        })
106        // on_editor_event fires whenever the editor's internal state changes:
107        //   • T / R / E keys  →  GizmoModeChanged
108        //   • Gizmo axis drag →  DragStart / DragEnd
109        // on_update is suppressed in editor mode, so game logic here is safe.
110        .on_editor_event(|_state, _scene, event, object| {
111            match event {
112                EditorStateEvent::GizmoModeChanged(mode) => {
113                    let label = match mode {
114                        GizmoMode::Translate => "Translate",
115                        GizmoMode::Rotate    => "Rotate",
116                        GizmoMode::Scale     => "Scale",
117                    };
118                    println!("[Editor] Gizmo mode → {label} {object:?}");
119                }
120                EditorStateEvent::DragStart { axis } => {
121                    let label = match axis {
122                        DragAxis::X => "X",
123                        DragAxis::Y => "Y",
124                        DragAxis::Z => "Z",
125                    };
126                    println!("[Editor] Drag started — axis: {label} {object:?}");
127                }
128                EditorStateEvent::DragEnd => {
129                    println!("[Editor] Drag ended {object:?}");
130                }
131                EditorStateEvent::SelectionChanged => {
132                    println!("[Editor] Selection changed {object:?}");
133                }
134            }
135        })
136        .create();
137}