1use crate::components::*;
4use crate::hierarchy;
5use crate::registry::Registry;
6use glam::Vec3;
7use kvas_ecs::{Entity, World};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11use std::path::Path;
12
13#[derive(Clone, Serialize, Deserialize)]
15#[serde(default)]
16pub struct SceneSettings {
17 pub ambient: [f32; 3],
19 pub sky: [f32; 3],
21 pub gravity: Vec3,
23 pub toon_bands: u32,
25 pub saturation: f32,
27 pub contrast: f32,
29 pub exposure: f32,
31 pub psx: bool,
33 pub render_scale: f32,
35 pub atmosphere: crate::sky::SkySettings,
37 pub shadows: crate::fx::ShadowSettings,
39 pub post: crate::fx::PostSettings,
41 pub upscaler: crate::fx::Upscaler,
43 pub frame_generation: bool,
46 pub sharpness: f32,
48 pub view_distance: f32,
50 pub filmic: f32,
52 pub fog: f32,
54 pub environment_map: String,
56 pub environment_rotation: f32,
58 pub environment_intensity: f32,
60}
61
62impl Default for SceneSettings {
63 fn default() -> Self {
64 Self {
65 ambient: [0.25, 0.27, 0.32],
66 sky: [0.35, 0.5, 0.75],
67 gravity: Vec3::new(0.0, -9.81, 0.0),
68 toon_bands: 0,
69 saturation: 1.0,
70 contrast: 1.0,
71 exposure: 1.0,
72 psx: false,
73 render_scale: 1.0,
74 atmosphere: Default::default(),
75 shadows: Default::default(),
76 post: Default::default(),
77 upscaler: Default::default(),
78 frame_generation: false,
79 sharpness: 0.5,
80 view_distance: 0.0,
81 filmic: 0.7,
82 fog: 0.0,
83 environment_map: String::new(),
84 environment_rotation: 0.0,
85 environment_intensity: 1.0,
86 }
87 }
88}
89
90pub struct Scene {
95 pub world: World,
97 pub settings: SceneSettings,
99 pub signals: crate::signals::SignalBus,
101 pub audio: crate::audio::AudioBus,
103 pub globals: std::collections::BTreeMap<String, Value>,
105}
106
107impl Default for Scene {
108 fn default() -> Self {
109 let mut world = World::new();
110 register_all(&mut world);
111 Scene { world, settings: SceneSettings::default(), signals: Default::default(), audio: Default::default(), globals: Default::default() }
112 }
113}
114
115#[derive(Serialize, Deserialize)]
116struct ComponentData {
117 #[serde(rename = "type")]
118 kind: String,
119 data: Value,
120}
121
122#[derive(Serialize, Deserialize)]
123struct EntityData {
124 parent: Option<usize>,
125 components: Vec<ComponentData>,
126}
127
128#[derive(Serialize, Deserialize, Default)]
129struct SceneFile {
130 #[serde(default)]
131 settings: SceneSettings,
132 entities: Vec<EntityData>,
133}
134
135impl Scene {
136 pub fn to_json(&self, reg: &Registry) -> String {
139 self.try_to_json(reg).unwrap_or_default()
140 }
141
142 pub fn try_to_json(&self, reg: &Registry) -> Result<String, String> {
145 let entities: Vec<Entity> = self.world.entities().into_iter().filter(|&e| !self.world.has::<Transient>(e)).collect();
146 let settings_json = serde_json::to_value(&self.settings).map_err(|e| format!("scene settings: {e}"))?;
147 serde_json::from_value::<SceneSettings>(settings_json)
148 .map_err(|e| format!("scene settings cannot round-trip through JSON: {e}"))?;
149 for &entity in &entities {
150 let errors = reg.validate_entity(&self.world, entity);
151 if !errors.is_empty() {
152 return Err(format!("entity {}: {}", entity.index(), errors.join("; ")));
153 }
154 }
155 let index: HashMap<Entity, usize> = entities.iter().enumerate().map(|(i, &e)| (e, i)).collect();
156 let entities = entities
157 .iter()
158 .map(|&e| EntityData {
159 parent: hierarchy::parent_of(&self.world, e).and_then(|p| index.get(&p).copied()),
160 components: reg
161 .components
162 .iter()
163 .filter_map(|c| (c.to_json)(&self.world, e).map(|data| ComponentData { kind: c.name.into(), data }))
164 .collect(),
165 })
166 .collect();
167 serde_json::to_string_pretty(&SceneFile { settings: self.settings.clone(), entities })
168 .map_err(|e| format!("scene serialization error: {e}"))
169 }
170
171 pub fn from_json(reg: &Registry, text: &str) -> Result<(Scene, Vec<String>), String> {
173 let file: SceneFile = serde_json::from_str(text).map_err(|e| format!("scene parse error: {e}"))?;
174 let mut scene = Scene { settings: file.settings, ..Scene::default() };
175 reg.register_world(&mut scene.world);
176 let warnings = load_entities(&mut scene, reg, file.entities);
177 Ok((scene, warnings))
178 }
179
180 pub fn add_json(&mut self, reg: &Registry, text: &str) -> Result<Vec<String>, String> {
183 let file: SceneFile = serde_json::from_str(text).map_err(|e| format!("scene parse error: {e}"))?;
184 Ok(load_entities(self, reg, file.entities))
185 }
186
187
188 pub fn load(reg: &Registry, path: &Path) -> Result<(Scene, Vec<String>), String> {
190 let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
191 Self::from_json(reg, &text)
192 }
193
194 pub fn save(&self, reg: &Registry, path: &Path) -> Result<(), String> {
196 let text = self.try_to_json(reg)?;
197 crate::file_io::atomic_write(path, text.as_bytes()).map_err(|e| format!("{}: {e}", path.display()))
198 }
199
200 pub fn snapshot(&self, reg: &Registry) -> Result<(Scene, Vec<String>), String> {
203 Self::from_json(reg, &self.try_to_json(reg)?)
204 }
205
206 pub fn validate(&self, reg: &Registry) -> Vec<String> {
210 let mut issues = Vec::new();
211 if let Err(error) = serde_json::to_value(&self.settings).and_then(serde_json::from_value::<SceneSettings>) {
212 issues.push(format!("scene settings: {error}"));
213 }
214 for entity in self.world.iter_entities() {
215 if self.world.has::<Transient>(entity) {
216 continue;
217 }
218 issues.extend(reg.validate_entity(&self.world, entity).into_iter().map(|error| format!("entity {}: {error}", entity.index())));
219 if !self.world.has::<Transform>(entity) {
220 issues.push(format!("entity {} has no Transform", entity.index()));
221 }
222 let mut current = Some(entity);
223 let mut seen = std::collections::HashSet::new();
224 while let Some(e) = current {
225 if !self.world.is_alive(e) {
226 issues.push(format!("entity {} has a dangling parent", entity.index()));
227 break;
228 }
229 if !seen.insert(e) {
230 issues.push(format!("entity {} has a cyclic parent chain", entity.index()));
231 break;
232 }
233 current = self.world.get::<Parent>(e).map(|p| p.0);
234 }
235 }
236 issues
237 }
238
239 pub fn spawn(&mut self, name: &str) -> Entity {
241 hierarchy::spawn_named(&mut self.world, name)
242 }
243
244 pub fn find(&self, name: &str) -> Option<Entity> {
246 hierarchy::find_by_name(&self.world, name)
247 }
248
249 pub fn despawn(&mut self, entity: Entity) {
251 hierarchy::despawn_recursive(&mut self.world, entity);
252 }
253}
254
255fn load_entities(scene: &mut Scene, reg: &Registry, entities: Vec<EntityData>) -> Vec<String> {
257 let mut warnings = Vec::new();
258 let spawned: Vec<Entity> = entities.iter().map(|_| scene.world.spawn()).collect();
259 let mut parents = Vec::new();
260 for (data, &e) in entities.into_iter().zip(&spawned) {
261 for c in data.components {
262 match reg.get(&c.kind) {
263 Some(info) => {
264 if let Err(err) = (info.from_json)(&mut scene.world, e, c.data) {
265 warnings.push(err);
266 }
267 }
268 None => warnings.push(format!("unknown component '{}'", c.kind)),
269 }
270 }
271 parents.push((e, data.parent));
272 }
273 let mut groups: Vec<usize> = (0..spawned.len()).collect();
275 fn root(groups: &mut [usize], mut index: usize) -> usize {
276 while groups[index] != index {
277 groups[index] = groups[groups[index]];
278 index = groups[index];
279 }
280 index
281 }
282 for (child_index, (e, parent)) in parents.into_iter().enumerate() {
286 if let Some(index) = parent {
287 match spawned.get(index).copied() {
288 Some(p) => {
289 let child_root = root(&mut groups, child_index);
290 let parent_root = root(&mut groups, index);
291 if child_root == parent_root {
292 warnings.push(format!("entity {}: cyclic parent {index} ignored", e.index()));
293 } else {
294 groups[child_root] = parent_root;
295 scene.world.insert(e, Parent(p));
296 }
297 }
298 None => warnings.push(format!("entity {}: invalid parent index {index} ignored", e.index())),
299 }
300 }
301 }
302 hierarchy::update_globals(&mut scene.world);
303 warnings
304}
305
306
307pub fn duplicate(world: &mut World, reg: &Registry, e: Entity) -> Entity {
315 assert!(world.is_alive(e), "cannot duplicate a stale entity");
316 let mut originals = vec![e];
317 let mut cursor = 0;
318 let mut seen = std::collections::HashSet::from([e]);
319 while cursor < originals.len() {
320 let current = originals[cursor];
321 for child in hierarchy::children(world, current) {
322 if seen.insert(child) {
323 originals.push(child);
324 }
325 }
326 cursor += 1;
327 }
328 let mut copies = HashMap::new();
329 for &original in &originals {
330 let copy = world.spawn();
331 reg.copy_entity(world, original, copy);
332 copies.insert(original, copy);
333 }
334 let copy = copies[&e];
335 if let Some(n) = world.get_mut::<Name>(copy) {
336 n.0.push_str(" (Copy)");
337 }
338 for original in originals {
339 if let Some(parent) = hierarchy::parent_of(world, original) {
340 let target = if original == e { (!copies.contains_key(&parent)).then_some(parent) } else { copies.get(&parent).copied() };
341 if let Some(target) = target {
342 hierarchy::set_parent_local(world, copies[&original], Some(target));
343 }
344 }
345 }
346 copy
347}