Skip to main content

gizmo_scene/
scene.rs

1use gizmo_core::{EntityName, World};
2use gizmo_core::component::{MeshSource, MaterialSource};
3use serde::{Deserialize, Serialize};
4use std::fs;
5
6use gizmo_core::component::{Children, Parent};
7use std::collections::HashMap;
8
9/// Tam sahne verisi — tüm entity'ler ve bileşenleri
10#[derive(Serialize, Deserialize, Clone)]
11pub struct SceneData {
12    pub entities: Vec<EntityData>,
13    #[serde(default)]
14    pub joints: Vec<gizmo_physics_rigid::joints::Joint>,
15}
16
17/// Prefab verisi — Tıpkı SceneData gibi ama kök entity'si var
18#[derive(Serialize, Deserialize, Clone)]
19pub struct PrefabData {
20    pub root_id: u32,
21    pub entities: Vec<EntityData>,
22    #[serde(default)]
23    pub joints: Vec<gizmo_physics_rigid::joints::Joint>,
24}
25
26/// Tek bir entity'nin serileştirilebilir verisi
27#[derive(Serialize, Deserialize, Clone)]
28pub struct EntityData {
29    pub original_id: u32,
30    pub name: Option<String>,
31    pub mesh_source: Option<String>,
32    pub material_source: Option<MaterialData>,
33    #[serde(default)]
34    pub parent_id: Option<u32>,
35    #[serde(default)]
36    pub components: std::collections::BTreeMap<String, String>,
37}
38
39/// Material serileştirme verisi
40#[derive(Serialize, Deserialize, Clone)]
41pub struct MaterialData {
42    pub albedo: [f32; 4],
43    pub roughness: f32,
44    pub metallic: f32,
45    pub unlit: f32,
46    pub texture_source: Option<String>,
47}
48
49impl SceneData {
50    /// Mevcut World durumunu JSON dosyası olarak diske kaydeder
51    pub fn save(
52        world: &World,
53        file_path: &str,
54        registry: &crate::registry::SceneRegistry,
55    ) -> Result<(), String> {
56        if let Some(parent) = std::path::Path::new(file_path).parent() {
57            let _ = fs::create_dir_all(parent);
58        }
59        let entities_data = Self::serialize_entities(
60            world,
61            world
62                .iter_alive_entities()
63                .into_iter()
64                .map(|e| e.id())
65                .collect(),
66            registry,
67        );
68
69        let mut joints = Vec::new();
70        if let Ok(physics_world) = world.try_get_resource::<gizmo_physics_rigid::world::PhysicsWorld>() {
71            joints = physics_world.joints.clone();
72        }
73
74        let scene = SceneData {
75            entities: entities_data,
76            joints,
77        };
78
79        let string_data = ron::ser::to_string_pretty(&scene, ron::ser::PrettyConfig::default())
80            .map_err(|e| format!("[SceneData::save] Serileştirme hatası: {}", e))?;
81
82        fs::write(file_path, string_data)
83            .map_err(|e| format!("[SceneData::save] Dosya yazma hatası: {}", e))?;
84
85        tracing::info!("✅ Sahne kaydedildi → {}", file_path);
86        Ok(())
87    }
88
89    /// Belirtilen entity ID'lerini serileştirir
90    pub fn serialize_entities(
91        world: &World,
92        entity_ids: Vec<u32>,
93        registry: &crate::registry::SceneRegistry,
94    ) -> Vec<EntityData> {
95        let mut entities_data = Vec::new();
96        let names = world.borrow::<EntityName>();
97        let meshes = world.borrow::<MeshSource>();
98        let materials = world.borrow::<MaterialSource>();
99        let parents = world.borrow::<Parent>();
100
101        for &id in &entity_ids {
102            let name = names.get(id).map(|n| n.0.clone());
103
104            // Gizmo Studio'nun içsel araçlarını kaydetme
105            if let Some(ref n) = name {
106                if n.starts_with("Editor ") || n == "Highlight Box" {
107                    continue;
108                }
109            }
110
111            let mesh_source = meshes.get(id).map(|m| m.0.clone());
112            let material_source = materials.get(id).map(|m| MaterialData {
113                albedo: m.albedo,
114                roughness: m.roughness,
115                metallic: m.metallic,
116                unlit: m.unlit,
117                texture_source: m.texture_source.clone(),
118            });
119            let parent_id = parents.get(id).map(|p| p.0);
120
121            let mut dynamic_components = std::collections::BTreeMap::new();
122            for comp_name in registry.all_components() {
123                if let Some(serializer) = registry.get_serializer(comp_name) {
124                    if let Some(comp_value) = serializer(world, id) {
125                        dynamic_components.insert(comp_name.clone(), comp_value);
126                    }
127                }
128            }
129
130            if name.is_some()
131                || mesh_source.is_some()
132                || material_source.is_some()
133                || parent_id.is_some()
134                || !dynamic_components.is_empty()
135            {
136                entities_data.push(EntityData {
137                    original_id: id,
138                    name,
139                    mesh_source,
140                    material_source,
141                    parent_id,
142                    components: dynamic_components,
143                });
144            }
145        }
146        
147        tracing::info!(">>> serialize_entities: total entities checked: {}, serialized: {}", entity_ids.len(), entities_data.len());
148        entities_data
149    }
150
151    /// JSON sahne dosyasını okuyup World'e entity olarak yükler
152    pub fn load_into(
153        file_path: &str,
154        world: &mut World,
155        registry: &crate::registry::SceneRegistry,
156    ) -> bool {
157        let string_data = match fs::read_to_string(file_path) {
158            Ok(content) => content,
159            Err(_) => return false,
160        };
161
162        let scene: SceneData = match ron::from_str(&string_data) {
163            Ok(s) => s,
164            Err(e) => {
165                tracing::error!("❌ Sahne dosyası geçersiz ({}): {}", file_path, e);
166                return false;
167            }
168        };
169
170        let entities = scene.entities;
171        tracing::info!(">>> load_into: Read {} entities from file", entities.len());
172
173        let id_map = Self::instantiate_entities(
174            entities,
175            None,
176            world,
177            registry,
178        );
179
180        if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics_rigid::world::PhysicsWorld>() {
181            for mut joint in scene.joints {
182                if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
183                    joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
184                    joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
185                    physics_world.joints.push(joint);
186                }
187            }
188        }
189
190        tracing::info!("✅ Sahne yüklendi ← {}", file_path);
191        true
192    }
193
194    /// Verilen entity listesini instantiate eder
195    pub fn instantiate_entities(
196        entities: Vec<EntityData>,
197        root_parent: Option<u32>,
198        world: &mut World,
199        registry: &crate::registry::SceneRegistry,
200    ) -> HashMap<u32, u32> {
201        let mut id_map = HashMap::new(); 
202        let mut entity_structs = HashMap::new();
203
204        for data in &entities {
205            let root_ent = world.spawn();
206            id_map.insert(data.original_id, root_ent.id());
207            entity_structs.insert(root_ent.id(), root_ent);
208        }
209
210        let mut children_map: HashMap<u32, Vec<u32>> = HashMap::new();
211
212        for data in entities {
213            let new_id = id_map[&data.original_id];
214            let entity = entity_structs[&new_id];
215
216            if let Some(n) = data.name {
217                world.add_component(entity, EntityName::new(&n));
218            }
219            
220            for (comp_name, comp_val) in &data.components {
221                if let Some(deserializer) = registry.get_deserializer(comp_name) {
222                    deserializer(world, new_id, comp_val);
223                }
224            }
225
226            if let Some(mesh_src) = data.mesh_source {
227                world.add_component(entity, MeshSource(mesh_src));
228            }
229
230            if let Some(mat_data) = data.material_source {
231                world.add_component(entity, MaterialSource {
232                    albedo: mat_data.albedo,
233                    roughness: mat_data.roughness,
234                    metallic: mat_data.metallic,
235                    unlit: mat_data.unlit,
236                    texture_source: mat_data.texture_source,
237                });
238            }
239
240            let mut resolved_parent = None;
241            if let Some(orig_parent) = data.parent_id {
242                if let Some(&p_id) = id_map.get(&orig_parent) {
243                    resolved_parent = Some(p_id);
244                }
245            } else {
246                resolved_parent = root_parent;
247            }
248
249            if let Some(p_id) = resolved_parent {
250                world.add_component(entity, Parent(p_id));
251                children_map.entry(p_id).or_default().push(new_id);
252            }
253        }
254
255        for (p_id, mut c_list) in children_map {
256            if let Some(&p_ent) = entity_structs.get(&p_id) {
257                world.add_component(p_ent, Children(c_list));
258            } else if let Some(p_ent) = world.get_entity(p_id) {
259                let existing: Vec<u32> = world
260                    .borrow::<Children>()
261                    .get(p_id)
262                    .map(|c| c.0.clone())
263                    .unwrap_or_default();
264                let mut merged = existing;
265                merged.append(&mut c_list);
266                world.add_component(p_ent, Children(merged));
267            }
268        }
269
270        id_map
271    }
272
273    /// Prefab kaydet
274    pub fn save_prefab(
275        world: &World,
276        root_entity_id: u32,
277        file_path: &str,
278        registry: &crate::registry::SceneRegistry,
279    ) -> Result<(), String> {
280        if let Some(parent) = std::path::Path::new(file_path).parent() {
281            let _ = fs::create_dir_all(parent);
282        }
283
284        let mut ids_to_save = vec![root_entity_id];
285        let children_storage = world.borrow::<Children>();
286
287        let mut i = 0;
288        while i < ids_to_save.len() {
289            let current = ids_to_save[i];
290            if let Some(children_comp) = children_storage.get(current) {
291                for &child_id in &children_comp.0 {
292                    ids_to_save.push(child_id);
293                }
294            }
295            i += 1;
296        }
297
298        let mut entities_data = Self::serialize_entities(world, ids_to_save.clone(), registry);
299
300        if let Some(root_data) = entities_data
301            .iter_mut()
302            .find(|d| d.original_id == root_entity_id)
303        {
304            root_data.parent_id = None;
305        }
306
307        let mut joints = Vec::new();
308        if let Ok(physics_world) = world.try_get_resource::<gizmo_physics_rigid::world::PhysicsWorld>() {
309            for joint in &physics_world.joints {
310                if ids_to_save.contains(&joint.entity_a.id()) && ids_to_save.contains(&joint.entity_b.id()) {
311                    joints.push(joint.clone());
312                }
313            }
314        }
315
316        let prefab = PrefabData {
317            root_id: root_entity_id,
318            entities: entities_data,
319            joints,
320        };
321
322        let string_data = ron::ser::to_string_pretty(&prefab, ron::ser::PrettyConfig::default())
323            .map_err(|e| format!("[SceneData::save_prefab] Serileştirme hatası: {}", e))?;
324
325        fs::write(file_path, string_data)
326            .map_err(|e| format!("[SceneData::save_prefab] Dosya yazma hatası: {}", e))?;
327
328        tracing::info!("✅ Prefab kaydedildi → {}", file_path);
329        Ok(())
330    }
331
332    /// Prefab yükle
333    pub fn load_prefab(
334        file_path: &str,
335        parent_entity: Option<u32>,
336        world: &mut World,
337        registry: &crate::registry::SceneRegistry,
338    ) -> Option<u32> {
339        let string_data = match fs::read_to_string(file_path) {
340            Ok(content) => content,
341            Err(_) => return None,
342        };
343
344        let prefab: PrefabData = match ron::from_str(&string_data) {
345            Ok(p) => p,
346            Err(e) => {
347                tracing::info!("❌ Prefab dosyası geçersiz ({}): {}", file_path, e);
348                return None;
349            }
350        };
351
352        let id_map = Self::instantiate_entities(
353            prefab.entities,
354            parent_entity,
355            world,
356            registry,
357        );
358
359        let new_root_id = id_map.get(&prefab.root_id).copied();
360
361        if let (Some(new_r), Some(p_id)) = (new_root_id, parent_entity) {
362            if let Some(p_ent) = world.get_entity(p_id) {
363                let mut children_list = world
364                    .borrow::<Children>()
365                    .get(p_id)
366                    .map(|c| c.0.clone())
367                    .unwrap_or_default();
368                children_list.push(new_r);
369                world.add_component(p_ent, Children(children_list));
370            }
371        }
372
373        if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics_rigid::world::PhysicsWorld>() {
374            for mut joint in prefab.joints {
375                if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
376                    joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
377                    joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
378                    physics_world.joints.push(joint);
379                }
380            }
381        }
382
383        tracing::info!("✅ Prefab yüklendi ← {}", file_path);
384        new_root_id
385    }
386
387    /// Entity listesini döndürür (Lua API'si için)
388    pub fn get_entity_names(world: &World) -> Vec<(u32, String)> {
389        let mut result = Vec::new();
390        let names = world.borrow::<EntityName>();
391        for (entity_id, _) in names.iter() {
392            if let Some(name) = names.get(entity_id) {
393                result.push((entity_id, name.0.clone()));
394            }
395        }
396        result
397    }
398
399    /// İsme göre entity bul
400    pub fn find_entity_by_name(world: &World, target_name: &str) -> Option<u32> {
401        let names = world.borrow::<EntityName>();
402        for (entity_id, _) in names.iter() {
403            if let Some(name) = names.get(entity_id) {
404                if name.0 == target_name {
405                    return Some(entity_id);
406                }
407            }
408        }
409        None
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use super::*;
416    use gizmo_core::World;
417    use gizmo_physics_rigid::joints::data::{Joint, JointType};
418
419    #[test]
420    fn test_prefab_joint_serialization() {
421        let mut world = World::new();
422        let ent1 = world.spawn();
423        let ent2 = world.spawn();
424
425        let joint = Joint {
426            entity_a: ent1,
427            entity_b: ent2,
428            local_anchor_a: gizmo_math::Vec3::ZERO,
429            local_anchor_b: gizmo_math::Vec3::ZERO,
430            break_force: 1000.0,
431            break_torque: 1000.0,
432            is_broken: false,
433            collision_enabled: false,
434            data: gizmo_physics_rigid::joints::data::JointData::Fixed,
435        };
436
437        let prefab_data = PrefabData {
438            root_id: ent1.id(),
439            entities: vec![],
440            joints: vec![joint.clone()],
441        };
442
443        let serialized = ron::ser::to_string(&prefab_data).unwrap();
444        assert!(serialized.contains("Fixed"));
445
446        let deserialized: PrefabData = ron::from_str(&serialized).unwrap();
447        assert_eq!(deserialized.joints.len(), 1);
448        assert!(matches!(deserialized.joints[0].data, gizmo_physics_rigid::joints::data::JointData::Fixed));
449    }
450}