engine/game_objects/
scene_builder.rs1use core::{
6 any::TypeId,
7 sync::atomic::{AtomicU32, Ordering},
8};
9
10use arrayvec::ArrayVec;
11
12use crate::{allocators::LinearAllocator, collections::FixedVec};
13
14use super::{ComponentColumn, ComponentInfo, ComponentVec, GameObject, GameObjectTable, Scene};
15
16struct GameObjectInfo {
17 component_infos: ComponentVec<ComponentInfo>,
18 game_object_type: TypeId,
19 game_object_count: usize,
20}
21
22#[allow(clippy::large_enum_variant)]
23enum GameObjectInfoLinkedList<'a> {
24 End,
25 Element {
26 next: &'a GameObjectInfoLinkedList<'a>,
27 info: GameObjectInfo,
28 },
29}
30
31impl<'a> IntoIterator for &'a GameObjectInfoLinkedList<'a> {
32 type Item = &'a GameObjectInfo;
33 type IntoIter = GameObjectInfoLinkedListIterator<'a>;
34
35 fn into_iter(self) -> Self::IntoIter {
36 GameObjectInfoLinkedListIterator { next: self }
37 }
38}
39
40struct GameObjectInfoLinkedListIterator<'a> {
41 next: &'a GameObjectInfoLinkedList<'a>,
42}
43
44impl<'a> Iterator for GameObjectInfoLinkedListIterator<'a> {
45 type Item = &'a GameObjectInfo;
46
47 fn next(&mut self) -> Option<Self::Item> {
48 match self.next {
49 GameObjectInfoLinkedList::End => None,
50 GameObjectInfoLinkedList::Element { next, info } => {
51 self.next = next;
52 Some(info)
53 }
54 }
55 }
56}
57
58pub struct SceneBuilder<'a> {
60 game_object_infos: GameObjectInfoLinkedList<'a>,
61}
62
63impl<'a> SceneBuilder<'a> {
64 pub fn with_game_object_type<G: GameObject>(&'a mut self, count: usize) -> SceneBuilder<'a> {
67 SceneBuilder {
68 game_object_infos: GameObjectInfoLinkedList::Element {
69 next: &self.game_object_infos,
70 info: GameObjectInfo {
71 component_infos: G::component_infos(),
72 game_object_type: TypeId::of::<G>(),
73 game_object_count: count,
74 },
75 },
76 }
77 }
78}
79
80impl Scene<'_> {
81 pub fn builder<'a>() -> SceneBuilder<'a> {
83 SceneBuilder {
84 game_object_infos: GameObjectInfoLinkedList::End,
85 }
86 }
87}
88
89impl SceneBuilder<'_> {
90 pub fn build<'a>(
103 self,
104 arena: &'a LinearAllocator,
105 temp_arena: &LinearAllocator,
106 ) -> Option<Scene<'a>> {
107 profiling::function_scope!();
108
109 let mut distinct_components = 0;
111 for (i, infos) in (self.game_object_infos.into_iter())
112 .enumerate()
113 .map(|(i, info)| (i, &info.component_infos))
114 {
115 for (j, component) in infos.iter().enumerate() {
116 let mut already_seen = false;
117 'find_prev: for previous_infos in (self.game_object_infos.into_iter())
118 .take(i + 1)
119 .map(|info| &info.component_infos)
120 {
121 for previous_component in previous_infos.iter().take(j) {
122 if component.type_id == previous_component.type_id {
123 already_seen = true;
124 break 'find_prev;
125 }
126 }
127 }
128 if !already_seen {
129 distinct_components += 1;
130 }
131 }
132 }
133
134 let mut component_alloc_counts =
136 FixedVec::<(&ComponentInfo, usize)>::new(temp_arena, distinct_components)?;
137 for game_object_info in &self.game_object_infos {
138 for component_info in &game_object_info.component_infos {
139 let count = 'count: {
140 for (existing_info, count) in &mut *component_alloc_counts {
141 if component_info.type_id == existing_info.type_id {
142 break 'count count;
143 }
144 }
145 let i = component_alloc_counts.len();
146 component_alloc_counts
147 .push((component_info, 0))
148 .ok()
149 .unwrap();
150 &mut component_alloc_counts[i].1
151 };
152
153 *count += game_object_info.game_object_count;
154 }
155 }
156
157 let mut component_datas_by_type = FixedVec::new(temp_arena, distinct_components)?;
159 for (component_info, total_count) in &*component_alloc_counts {
160 let data: FixedVec<u8> = FixedVec::with_alignment(
161 arena,
162 component_info.size * *total_count,
163 component_info.alignment,
164 )?;
165 component_datas_by_type
166 .push((component_info.type_id, data))
167 .unwrap();
168 }
169 component_datas_by_type.sort_unstable_by_key(|(type_id, _)| *type_id);
170
171 let game_object_table_count = self.game_object_infos.into_iter().count();
173 let mut game_object_tables = FixedVec::new(arena, game_object_table_count)?;
174 for GameObjectInfo {
175 component_infos,
176 game_object_type,
177 game_object_count,
178 } in &self.game_object_infos
179 {
180 let mut columns = ArrayVec::new();
181 for component_info in component_infos {
182 let alloc_for_type = {
183 let i = component_datas_by_type
184 .binary_search_by_key(&component_info.type_id, |(t, _)| *t)
185 .unwrap();
186 &mut component_datas_by_type[i].1
187 };
188 let data_size = *game_object_count * component_info.size;
189
190 columns.push(ComponentColumn {
191 component_info: *component_info,
192 data: alloc_for_type.split_off_head(data_size).unwrap(),
193 });
194 }
195
196 let table = GameObjectTable {
197 game_object_type: *game_object_type,
198 columns,
199 };
200 game_object_tables.push(table).ok().unwrap();
201 }
202 game_object_tables.sort_unstable_by_key(|table| table.game_object_type);
203
204 static SCENE_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
206 let prev_id = SCENE_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
207 let scene_id = prev_id.checked_add(1).unwrap();
208
209 Some(Scene {
210 id: scene_id,
211 generation: 0,
212 game_object_tables,
213 })
214 }
215}