1use crate::{
2 Arena, DirectionalLight, Material, Mesh, MeshId, ObjectId, Texture, TextureId, Transform,
3};
4
5#[derive(Clone, Debug)]
8pub struct RenderObject {
9 pub mesh: MeshId,
12 pub texture: Option<TextureId>,
15 pub transform: Transform,
18 pub material: Material,
21 pub visible: bool,
24}
25
26pub struct Scene {
32 pub meshes: Arena<Mesh>,
35 pub textures: Arena<Texture>,
38 pub objects: Arena<RenderObject>,
41 pub light: DirectionalLight,
44}
45
46impl Scene {
47 #[must_use]
68 pub fn new() -> Self {
69 Self {
70 meshes: Arena::new(),
71 textures: Arena::new(),
72 objects: Arena::new(),
73 light: DirectionalLight::default(),
74 }
75 }
76
77 pub fn spawn(&mut self, object: RenderObject) -> ObjectId {
80 self.objects.insert(object)
81 }
82}
83
84impl Default for Scene {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::{Material, Rgb, Transform};
94
95 #[test]
96 fn spawn_returns_reachable_object() {
97 let mut scene = Scene::new();
98 let mesh = scene.meshes.insert(Mesh::triangle(1.0, 1.0).unwrap());
99 let object = scene.spawn(RenderObject {
100 mesh,
101 texture: None,
102 transform: Transform::default(),
103 material: Material::new(Rgb::new(1.0, 0.5, 0.25)),
104 visible: true,
105 });
106 let fetched = scene.objects.get(object).unwrap();
107 assert_eq!(fetched.mesh, mesh);
108 assert!(fetched.visible);
109 assert_eq!(fetched.material.tint, Rgb::new(1.0, 0.5, 0.25));
110 }
111
112 #[test]
113 fn removed_object_does_not_leak_into_scene() {
114 let mut scene = Scene::new();
115 let mesh = scene.meshes.insert(Mesh::plane(1.0, 1.0).unwrap());
116 let object = scene.spawn(RenderObject {
117 mesh,
118 texture: None,
119 transform: Transform::default(),
120 material: Material::default(),
121 visible: true,
122 });
123 assert_eq!(scene.objects.len(), 1);
124 let _ = scene.objects.remove(object);
125 assert!(scene.objects.is_empty());
126 }
127}