Skip to main content

World

Struct World 

Source
pub struct World {
    pub objects: HashMap<usize, Object>,
    pub roots: Vec<usize>,
    pub name_handles: HashMap<String, usize>,
    pub on_scene_graph_modified: Option<SceneGraphCallback>,
    /* private fields */
}

Fields§

§objects: HashMap<usize, Object>§roots: Vec<usize>§name_handles: HashMap<String, usize>§on_scene_graph_modified: Option<SceneGraphCallback>

Optional callback invoked after every structural scene-graph change.

Implementations§

Source§

impl World

Source

pub fn new() -> Self

Examples found in repository?
examples/vtr_roundtrip.rs (line 37)
30fn main() {
31    // ── 1. Build a scene in memory ────────────────────────────────────────────
32
33    let camera = Camera::new()
34        .with_position([0.0, 5.0, -12.0])
35        .with_rotation(90.0, -20.0);
36
37    let mut world = World::new();
38
39    // Root object
40    let root_id = world.spawn_object(
41        Object {
42            name: "Root".to_string(),
43            str_id: "root".to_string(),
44            geometry: Some(Geometry::Cube { size: 1.0 }),
45            color: [1.0, 0.4, 0.4, 1.0],
46            transform: Transform::from_position(0.0, 0.0, 0.0),
47            ..Default::default()
48        },
49        None,
50    );
51
52    // Child of root
53    let child_id = world.spawn_object(
54        Object {
55            name: "Child".to_string(),
56            str_id: "child".to_string(),
57            geometry: Some(Geometry::Sphere {
58                radius: 0.5,
59                subdivisions: 16,
60            }),
61            color: [0.4, 0.8, 0.4, 1.0],
62            transform: Transform::from_position(3.0, 0.0, 0.0),
63            ..Default::default()
64        },
65        Some(root_id),
66    );
67
68    // Grandchild
69    world.spawn_object(
70        Object {
71            name: "Grandchild".to_string(),
72            str_id: "grandchild".to_string(),
73            geometry: Some(Geometry::Pyramid {
74                base_size: 0.8,
75                height: 1.2,
76            }),
77            color: [0.4, 0.4, 1.0, 1.0],
78            transform: Transform::from_position(2.0, 0.0, 0.0),
79            ..Default::default()
80        },
81        Some(child_id),
82    );
83
84    println!("Original scene: {} object(s)", world.objects.len());
85
86    // ── 2. Serialize ──────────────────────────────────────────────────────────
87
88    let mut buf: Vec<u8> = Vec::new();
89    vtr::write(&mut buf, &camera, &world).expect("serialization failed");
90    println!("Serialized to {} byte(s)", buf.len());
91
92    // ── 3. Peek at the header ─────────────────────────────────────────────────
93
94    let header = vtr::read_header(&mut Cursor::new(&buf)).expect("header read failed");
95    println!(
96        "Header: format v{}, engine v{}, {} object(s)",
97        header.format_version,
98        header.engine_version_string(),
99        header.object_count,
100    );
101
102    assert_eq!(header.object_count as usize, world.objects.len());
103
104    // ── 4. Deserialize ────────────────────────────────────────────────────────
105
106    let loaded = vtr::read(&mut Cursor::new(&buf)).expect("deserialization failed");
107    println!("Loaded scene:  {} object(s)", loaded.world.objects.len());
108
109    // ── 5. Verify ─────────────────────────────────────────────────────────────
110
111    assert_eq!(
112        loaded.world.objects.len(),
113        world.objects.len(),
114        "object count mismatch"
115    );
116
117    // Check that hierarchy is preserved.
118    for str_id in ["root", "child", "grandchild"] {
119        let orig_id = world.get_id(str_id).expect("str_id missing in original");
120        let load_id = loaded
121            .world
122            .get_id(str_id)
123            .expect("str_id missing after round-trip");
124
125        let orig = &world.objects[&orig_id];
126        let load = &loaded.world.objects[&load_id];
127
128        assert_eq!(orig.name, load.name, "name mismatch for {str_id}");
129        assert_eq!(orig.color, load.color, "color mismatch for {str_id}");
130        assert_eq!(
131            orig.transform.position, load.transform.position,
132            "position mismatch for {str_id}"
133        );
134        assert_eq!(
135            orig.geometry, load.geometry,
136            "geometry mismatch for {str_id}"
137        );
138
139        println!("  ✓  {str_id:12}  name={:?}", load.name);
140    }
141
142    // Camera round-trip
143    assert_eq!(camera.eye, loaded.camera.eye, "camera eye mismatch");
144    assert_eq!(camera.fov, loaded.camera.fov, "camera fov mismatch");
145    println!("  ✓  camera");
146
147    println!("\nRound-trip verified ✓");
148}
Source

pub fn from_parts( objects: HashMap<usize, Object>, roots: Vec<usize>, next_id: usize, ) -> Self

Reconstruct a World directly from its constituent parts.

Used by the VTR deserializer to rebuild a world without going through spawn_object, so that the original IDs and hierarchy are preserved exactly.

next_id should be set to max(existing_ids) + 1 so that future calls to spawn_object never collide with the loaded objects.

Source

pub fn spawn_object( &mut self, object: Object, parent_id: Option<usize>, ) -> usize

Examples found in repository?
examples/vtr_roundtrip.rs (lines 40-50)
30fn main() {
31    // ── 1. Build a scene in memory ────────────────────────────────────────────
32
33    let camera = Camera::new()
34        .with_position([0.0, 5.0, -12.0])
35        .with_rotation(90.0, -20.0);
36
37    let mut world = World::new();
38
39    // Root object
40    let root_id = world.spawn_object(
41        Object {
42            name: "Root".to_string(),
43            str_id: "root".to_string(),
44            geometry: Some(Geometry::Cube { size: 1.0 }),
45            color: [1.0, 0.4, 0.4, 1.0],
46            transform: Transform::from_position(0.0, 0.0, 0.0),
47            ..Default::default()
48        },
49        None,
50    );
51
52    // Child of root
53    let child_id = world.spawn_object(
54        Object {
55            name: "Child".to_string(),
56            str_id: "child".to_string(),
57            geometry: Some(Geometry::Sphere {
58                radius: 0.5,
59                subdivisions: 16,
60            }),
61            color: [0.4, 0.8, 0.4, 1.0],
62            transform: Transform::from_position(3.0, 0.0, 0.0),
63            ..Default::default()
64        },
65        Some(root_id),
66    );
67
68    // Grandchild
69    world.spawn_object(
70        Object {
71            name: "Grandchild".to_string(),
72            str_id: "grandchild".to_string(),
73            geometry: Some(Geometry::Pyramid {
74                base_size: 0.8,
75                height: 1.2,
76            }),
77            color: [0.4, 0.4, 1.0, 1.0],
78            transform: Transform::from_position(2.0, 0.0, 0.0),
79            ..Default::default()
80        },
81        Some(child_id),
82    );
83
84    println!("Original scene: {} object(s)", world.objects.len());
85
86    // ── 2. Serialize ──────────────────────────────────────────────────────────
87
88    let mut buf: Vec<u8> = Vec::new();
89    vtr::write(&mut buf, &camera, &world).expect("serialization failed");
90    println!("Serialized to {} byte(s)", buf.len());
91
92    // ── 3. Peek at the header ─────────────────────────────────────────────────
93
94    let header = vtr::read_header(&mut Cursor::new(&buf)).expect("header read failed");
95    println!(
96        "Header: format v{}, engine v{}, {} object(s)",
97        header.format_version,
98        header.engine_version_string(),
99        header.object_count,
100    );
101
102    assert_eq!(header.object_count as usize, world.objects.len());
103
104    // ── 4. Deserialize ────────────────────────────────────────────────────────
105
106    let loaded = vtr::read(&mut Cursor::new(&buf)).expect("deserialization failed");
107    println!("Loaded scene:  {} object(s)", loaded.world.objects.len());
108
109    // ── 5. Verify ─────────────────────────────────────────────────────────────
110
111    assert_eq!(
112        loaded.world.objects.len(),
113        world.objects.len(),
114        "object count mismatch"
115    );
116
117    // Check that hierarchy is preserved.
118    for str_id in ["root", "child", "grandchild"] {
119        let orig_id = world.get_id(str_id).expect("str_id missing in original");
120        let load_id = loaded
121            .world
122            .get_id(str_id)
123            .expect("str_id missing after round-trip");
124
125        let orig = &world.objects[&orig_id];
126        let load = &loaded.world.objects[&load_id];
127
128        assert_eq!(orig.name, load.name, "name mismatch for {str_id}");
129        assert_eq!(orig.color, load.color, "color mismatch for {str_id}");
130        assert_eq!(
131            orig.transform.position, load.transform.position,
132            "position mismatch for {str_id}"
133        );
134        assert_eq!(
135            orig.geometry, load.geometry,
136            "geometry mismatch for {str_id}"
137        );
138
139        println!("  ✓  {str_id:12}  name={:?}", load.name);
140    }
141
142    // Camera round-trip
143    assert_eq!(camera.eye, loaded.camera.eye, "camera eye mismatch");
144    assert_eq!(camera.fov, loaded.camera.fov, "camera fov mismatch");
145    println!("  ✓  camera");
146
147    println!("\nRound-trip verified ✓");
148}
Source

pub fn get_id(&self, str_id: &str) -> Option<usize>

Returns the unique integer ID associated with a given string identifier (str_id).

This method performs a lookup in the internal handle cache. While the lookup is technically $O(1)$ on average, it involves hashing the input string and searching a HashMap.

§Performance Warning

Do not use this method inside on_update or other high-frequency loops.

Calling this every frame for multiple objects will cause significant performance degradation due to repeated string hashing and cache misses. Instead, “memoize” the ID: call this method once during on_startup, store the resulting usize in your application state, and use that integer ID for direct access during updates.

§Examples
// Correct: Resolve once during initialization
let sun_id = scene.get_id("sun_center").expect("Sun not found in scene!");
state.sun_id = Some(sun_id);
Examples found in repository?
examples/solar_system.rs (line 90)
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}
More examples
Hide additional examples
examples/vtr_roundtrip.rs (line 119)
30fn main() {
31    // ── 1. Build a scene in memory ────────────────────────────────────────────
32
33    let camera = Camera::new()
34        .with_position([0.0, 5.0, -12.0])
35        .with_rotation(90.0, -20.0);
36
37    let mut world = World::new();
38
39    // Root object
40    let root_id = world.spawn_object(
41        Object {
42            name: "Root".to_string(),
43            str_id: "root".to_string(),
44            geometry: Some(Geometry::Cube { size: 1.0 }),
45            color: [1.0, 0.4, 0.4, 1.0],
46            transform: Transform::from_position(0.0, 0.0, 0.0),
47            ..Default::default()
48        },
49        None,
50    );
51
52    // Child of root
53    let child_id = world.spawn_object(
54        Object {
55            name: "Child".to_string(),
56            str_id: "child".to_string(),
57            geometry: Some(Geometry::Sphere {
58                radius: 0.5,
59                subdivisions: 16,
60            }),
61            color: [0.4, 0.8, 0.4, 1.0],
62            transform: Transform::from_position(3.0, 0.0, 0.0),
63            ..Default::default()
64        },
65        Some(root_id),
66    );
67
68    // Grandchild
69    world.spawn_object(
70        Object {
71            name: "Grandchild".to_string(),
72            str_id: "grandchild".to_string(),
73            geometry: Some(Geometry::Pyramid {
74                base_size: 0.8,
75                height: 1.2,
76            }),
77            color: [0.4, 0.4, 1.0, 1.0],
78            transform: Transform::from_position(2.0, 0.0, 0.0),
79            ..Default::default()
80        },
81        Some(child_id),
82    );
83
84    println!("Original scene: {} object(s)", world.objects.len());
85
86    // ── 2. Serialize ──────────────────────────────────────────────────────────
87
88    let mut buf: Vec<u8> = Vec::new();
89    vtr::write(&mut buf, &camera, &world).expect("serialization failed");
90    println!("Serialized to {} byte(s)", buf.len());
91
92    // ── 3. Peek at the header ─────────────────────────────────────────────────
93
94    let header = vtr::read_header(&mut Cursor::new(&buf)).expect("header read failed");
95    println!(
96        "Header: format v{}, engine v{}, {} object(s)",
97        header.format_version,
98        header.engine_version_string(),
99        header.object_count,
100    );
101
102    assert_eq!(header.object_count as usize, world.objects.len());
103
104    // ── 4. Deserialize ────────────────────────────────────────────────────────
105
106    let loaded = vtr::read(&mut Cursor::new(&buf)).expect("deserialization failed");
107    println!("Loaded scene:  {} object(s)", loaded.world.objects.len());
108
109    // ── 5. Verify ─────────────────────────────────────────────────────────────
110
111    assert_eq!(
112        loaded.world.objects.len(),
113        world.objects.len(),
114        "object count mismatch"
115    );
116
117    // Check that hierarchy is preserved.
118    for str_id in ["root", "child", "grandchild"] {
119        let orig_id = world.get_id(str_id).expect("str_id missing in original");
120        let load_id = loaded
121            .world
122            .get_id(str_id)
123            .expect("str_id missing after round-trip");
124
125        let orig = &world.objects[&orig_id];
126        let load = &loaded.world.objects[&load_id];
127
128        assert_eq!(orig.name, load.name, "name mismatch for {str_id}");
129        assert_eq!(orig.color, load.color, "color mismatch for {str_id}");
130        assert_eq!(
131            orig.transform.position, load.transform.position,
132            "position mismatch for {str_id}"
133        );
134        assert_eq!(
135            orig.geometry, load.geometry,
136            "geometry mismatch for {str_id}"
137        );
138
139        println!("  ✓  {str_id:12}  name={:?}", load.name);
140    }
141
142    // Camera round-trip
143    assert_eq!(camera.eye, loaded.camera.eye, "camera eye mismatch");
144    assert_eq!(camera.fov, loaded.camera.fov, "camera fov mismatch");
145    println!("  ✓  camera");
146
147    println!("\nRound-trip verified ✓");
148}
Source

pub fn get_mut(&mut self, id: usize) -> Option<&mut Object>

Examples found in repository?
examples/hello_cube.rs (line 52)
26fn main() {
27    Window::new(AppState { cube_id: None })
28        .with_title("Hello, Cube!")
29        .with_camera(
30            Camera::new()
31                .with_position([0.0, 2.0, -5.0])
32                .with_rotation(90.0, -15.0),
33        )
34        .on_startup(|state, scene, _| {
35            // Spawn a single cube at the world origin.
36            let id = scene.spawn(
37                Object {
38                    name: "Cube".to_string(),
39                    str_id: "cube".to_string(),
40                    geometry: Some(Geometry::Cube { size: 1.5 }),
41                    color: [0.9, 0.5, 0.2, 1.0], // warm orange
42                    transform: Transform::default(),
43                    ..Default::default()
44                },
45                None, // no parent — this is a root object
46            );
47            // Cache the ID so on_update can find it cheaply.
48            state.cube_id = Some(id);
49        })
50        .on_update(|state, scene, ctx| {
51            // Rotate 45° per second around the Y-axis.
52            if let Some(cube) = state.cube_id.and_then(|id| scene.world.get_mut(id)) {
53                cube.transform.rotation[1] += 45.0 * ctx.dt;
54            }
55        })
56        .create();
57}
More examples
Hide additional examples
examples/textured_cube.rs (line 80)
40fn main() {
41    Window::new(AppState { cube_id: None })
42        .with_title("Textured Cube — Vertra")
43        .with_camera(
44            Camera::new()
45                .with_position([0.0, 2.5, -6.0])
46                .with_rotation(90.0, -15.0),
47        )
48        .on_startup(|state, scene, _| {
49            // ----------------------------------------------------------------
50            // 1.  Load the texture from disk.
51            //     The key must match the `texture_path` set on the object.
52            // ----------------------------------------------------------------
53            #[cfg(not(target_arch = "wasm32"))]
54            match scene.load_texture(TEXTURE_PATH) {
55                Ok(()) => println!("[info] Texture loaded: {TEXTURE_PATH}"),
56                Err(e) => eprintln!("[warn] {e}  – cube will render with vertex colour"),
57            }
58
59            // ----------------------------------------------------------------
60            // 2.  Textured cube (white vertex colour so the image shows cleanly)
61            // ----------------------------------------------------------------
62            let cube_id = scene.spawn(
63                Object {
64                    name: "Textured Cube".to_string(),
65                    str_id: "cube".to_string(),
66                    geometry: Some(Geometry::Cube { size: 2.0 }),
67                    // White vertex colour = texture displayed without tint.
68                    // Change this to tint the texture (e.g. [1.0, 0.5, 0.5, 1.0] = reddish).
69                    color: [1.0, 1.0, 1.0, 1.0],
70                    transform: Transform::from_position(0.0, 1.0, 0.0),
71                    texture_path: Some(TEXTURE_PATH.to_string()),
72                    ..Default::default()
73                },
74                None,
75            );
76            state.cube_id = Some(cube_id);
77        })
78        .on_update(|state, scene, ctx| {
79            // Slowly rotate the cube so all faces of the texture are visible.
80            if let Some(cube) = state.cube_id.and_then(|id| scene.world.get_mut(id)) {
81                cube.transform.rotation[1] += 40.0 * ctx.dt; // 40°/s around Y
82                cube.transform.rotation[0] += 15.0 * ctx.dt; // 15°/s around X
83            }
84        })
85        .create();
86}
examples/fixed_update.rs (line 112)
44fn main() {
45    Window::new(AppState {
46        ball_id: None,
47        cube_id: None,
48        ball_vy: 8.0, // initial upward kick
49    })
50    .with_title("Fixed Update — Bouncing Ball")
51    .with_camera(
52        Camera::new()
53            .with_position([0.0, 3.0, -8.0])
54            .with_rotation(90.0, -15.0),
55    )
56    .on_startup(|state, scene, _| {
57        // Bouncing sphere
58        let ball_id = scene.spawn(
59            Object {
60                name: "Ball".to_string(),
61                str_id: "ball".to_string(),
62                geometry: Some(Geometry::Sphere {
63                    radius: 0.5,
64                    subdivisions: 20,
65                }),
66                color: [0.3, 0.7, 1.0, 1.0],
67                transform: Transform::from_position(0.0, 4.0, 0.0),
68                ..Default::default()
69            },
70            None,
71        );
72
73        // Ground plane
74        scene.spawn(
75            Object {
76                name: "Ground".to_string(),
77                str_id: "ground".to_string(),
78                geometry: Some(Geometry::Plane { size: 12.0 }),
79                color: [0.3, 0.6, 0.3, 1.0],
80                transform: Transform::from_position(0.0, 0.0, 0.0),
81                ..Default::default()
82            },
83            None,
84        );
85
86        // Spinning cube for on_update comparison
87        let cube_id = scene.spawn(
88            Object {
89                name: "Spinner".to_string(),
90                str_id: "spinner".to_string(),
91                geometry: Some(Geometry::Cube { size: 1.0 }),
92                color: [1.0, 0.5, 0.2, 1.0],
93                transform: Transform::from_position(3.5, 1.0, 0.0),
94                ..Default::default()
95            },
96            None,
97        );
98
99        state.ball_id = Some(ball_id);
100        state.cube_id = Some(cube_id);
101    })
102    // ── Physics tick (fixed 60 Hz) ────────────────────────────────────────────
103    .on_fixed_update(|state, scene, ctx| {
104        const GRAVITY: f32 = -12.0;       // m/s²
105        const RESTITUTION: f32 = 0.78;    // energy retained on bounce (0–1)
106        const GROUND_Y: f32 = 0.5;        // ball radius — lowest allowed centre
107
108        if let Some(id) = state.ball_id {
109            // Integrate gravity.
110            state.ball_vy += GRAVITY * ctx.dt;
111
112            if let Some(ball) = scene.world.get_mut(id) {
113                ball.transform.position[1] += state.ball_vy * ctx.dt;
114
115                // Ground collision: reflect and damp.
116                if ball.transform.position[1] < GROUND_Y {
117                    ball.transform.position[1] = GROUND_Y;
118                    state.ball_vy = state.ball_vy.abs() * RESTITUTION;
119
120                    // Stop micro-bounces that would never settle.
121                    if state.ball_vy < 0.2 {
122                        state.ball_vy = 0.0;
123                    }
124                }
125            }
126        }
127    })
128    // ── Visual update (variable dt) ───────────────────────────────────────────
129    .on_update(|state, scene, ctx| {
130        // Spin the reference cube at 90 °/s — purely cosmetic.
131        if let Some(spinner) = state.cube_id.and_then(|id| scene.world.get_mut(id)) {
132            spinner.transform.rotation[1] += 90.0 * ctx.dt;
133            spinner.transform.rotation[0] += 45.0 * ctx.dt;
134        }
135    })
136    .create();
137}
examples/solar_system.rs (line 97)
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}
Source

pub fn rename_str_id(&mut self, id: usize, new_str_id: String) -> bool

Rename the stable string identifier of a live object and keep the internal name_handles cache in sync.

Always prefer this over writing to object.str_id directly when the object is already inside a World. Direct field assignment bypasses the cache and will silently break every subsequent World::get_id call for the old or new identifier.

Returns false (no-op) when id does not exist.

Source

pub fn delete(&mut self, id: usize)

Source

pub fn reparent(&mut self, id: usize, new_parent: Option<usize>) -> bool

Move id to a new parent (or to the scene root when new_parent is None).

§No-op conditions
  • id does not exist in the world.
  • new_parent is the same as the current parent.
  • new_parent == Some(id) (self-parenting).
  • new_parent does not exist in the world (guards against dangling links).
  • new_parent is a descendant of id (would create a cycle).

The object’s children are carried along unchanged.

Trait Implementations§

Source§

impl Debug for World

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for World

§

impl !RefUnwindSafe for World

§

impl !Send for World

§

impl !Sync for World

§

impl Unpin for World

§

impl UnsafeUnpin for World

§

impl !UnwindSafe for World

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>