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: &gizmo_core::registry::ComponentRegistry,
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    pub fn serialize_entities(
90        world: &World,
91        entity_ids: Vec<u32>,
92        registry: &gizmo_core::registry::ComponentRegistry,
93    ) -> Vec<EntityData> {
94        let mut entities_data = Vec::new();
95        let names = world.borrow::<EntityName>();
96        let meshes = world.borrow::<MeshSource>();
97        let materials = world.borrow::<MaterialSource>();
98        let parents = world.borrow::<Parent>();
99
100        for &id in &entity_ids {
101            let entity = gizmo_core::entity::Entity::new(id, 0); // Using generation 0 for lookup
102
103            let name = names.get(id).map(|n| n.0.clone());
104
105            // Gizmo Studio'nun içsel araçlarını kaydetme
106            if let Some(ref n) = name {
107                if n.starts_with("Editor ") || n == "Highlight Box" {
108                    continue;
109                }
110            }
111
112            let mesh_source = meshes.get(id).map(|m| m.0.clone());
113            let material_source = materials.get(id).map(|m| MaterialData {
114                albedo: m.albedo,
115                roughness: m.roughness,
116                metallic: m.metallic,
117                unlit: m.unlit,
118                texture_source: m.texture_source.clone(),
119            });
120            let parent_id = parents.get(id).map(|p| p.0);
121
122            let mut dynamic_components = std::collections::BTreeMap::new();
123            
124            let types = world.get_entity_component_types(entity);
125            for type_id in types {
126                if let Some(reg) = registry.get_registration(type_id) {
127                    if let (Some(ptr), Some(get_reflect_ptr)) = (world.get_component_ptr(entity, type_id), reg.get_reflect_ptr_fn) {
128                        let reflect_ptr = get_reflect_ptr(ptr);
129                        let reflect_val = unsafe { &*reflect_ptr };
130                        
131                        let type_reg = registry.reflect_registry.get(type_id);
132                        if let Some(_type_registration) = type_reg {
133                            let serializer = bevy_reflect::serde::TypedReflectSerializer::new(reflect_val, &registry.reflect_registry);
134                            if let Ok(string_repr) = ron::ser::to_string(&serializer) {
135                                dynamic_components.insert(reg.name.clone(), string_repr);
136                            }
137                        } else if let Some(ser_fn) = reg.serialize_fn {
138                            // Fallback to legacy serialization
139                            if let Ok(string_repr) = ser_fn(ptr) {
140                                dynamic_components.insert(reg.name.clone(), string_repr);
141                            }
142                        }
143                    }
144                }
145            }
146
147            if name.is_some()
148                || mesh_source.is_some()
149                || material_source.is_some()
150                || parent_id.is_some()
151                || !dynamic_components.is_empty()
152            {
153                entities_data.push(EntityData {
154                    original_id: id,
155                    name,
156                    mesh_source,
157                    material_source,
158                    parent_id,
159                    components: dynamic_components,
160                });
161            }
162        }
163        
164        tracing::info!(">>> serialize_entities: total entities checked: {}, serialized: {}", entity_ids.len(), entities_data.len());
165        entities_data
166    }
167
168    /// JSON sahne dosyasını okuyup World'e entity olarak yükler
169    pub fn load_into(
170        file_path: &str,
171        world: &mut World,
172        registry: &gizmo_core::registry::ComponentRegistry,
173    ) -> bool {
174        let string_data = match fs::read_to_string(file_path) {
175            Ok(content) => content,
176            Err(_) => return false,
177        };
178
179        let scene: SceneData = match ron::from_str(&string_data) {
180            Ok(s) => s,
181            Err(e) => {
182                tracing::error!("❌ Sahne dosyası geçersiz ({}): {}", file_path, e);
183                return false;
184            }
185        };
186
187        let entities = scene.entities;
188        tracing::info!(">>> load_into: Read {} entities from file", entities.len());
189
190        let id_map = Self::instantiate_entities(
191            entities,
192            None,
193            world,
194            registry,
195        );
196
197        if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics_rigid::world::PhysicsWorld>() {
198            for mut joint in scene.joints {
199                if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
200                    joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
201                    joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
202                    physics_world.joints.push(joint);
203                }
204            }
205        }
206
207        tracing::info!("✅ Sahne yüklendi ← {}", file_path);
208        true
209    }
210
211    pub fn instantiate_entities(
212        entities: Vec<EntityData>,
213        root_parent: Option<u32>,
214        world: &mut World,
215        registry: &gizmo_core::registry::ComponentRegistry,
216    ) -> HashMap<u32, u32> {
217        let mut id_map = HashMap::new(); 
218        let mut entity_structs = HashMap::new();
219
220        for data in &entities {
221            let root_ent = world.spawn();
222            id_map.insert(data.original_id, root_ent.id());
223            entity_structs.insert(root_ent.id(), root_ent);
224        }
225
226        let mut children_map: HashMap<u32, Vec<u32>> = HashMap::new();
227
228        for data in entities {
229            let new_id = id_map[&data.original_id];
230            let entity = entity_structs[&new_id];
231
232            if let Some(n) = data.name {
233                world.add_component(entity, EntityName::new(&n));
234            }
235            
236            for (comp_name, comp_val) in &data.components {
237                if let Some(type_id) = registry.get_type_id(comp_name) {
238                    if let Some(reg) = registry.get_registration(type_id) {
239                        if let Some(type_reg) = registry.reflect_registry.get(type_id) {
240                            // Use Bevy Reflect deserialization
241                            let deserializer = bevy_reflect::serde::TypedReflectDeserializer::new(type_reg, &registry.reflect_registry);
242                            if let Ok(mut de) = ron::de::Deserializer::from_str(comp_val) {
243                                if let Ok(reflect_val) = serde::de::DeserializeSeed::deserialize(deserializer, &mut de) {
244                                    if let Some(insert_fn) = reg.insert_reflect_fn {
245                                        if let Err(e) = insert_fn(world, entity, &*reflect_val) {
246                                            tracing::error!("Failed to insert reflect component {}: {}", comp_name, e);
247                                        }
248                                    }
249                                }
250                            }
251                        } else if let Some(deserialize_fn) = reg.deserialize_fn {
252                            // Fallback to legacy deserialization
253                            if let Err(e) = deserialize_fn(world, entity, comp_val) {
254                                tracing::error!("Failed to deserialize component {}: {}", comp_name, e);
255                            }
256                        }
257                    }
258                }
259            }
260
261            if let Some(mesh_src) = data.mesh_source {
262                world.add_component(entity, MeshSource(mesh_src));
263            }
264
265            if let Some(mat_data) = data.material_source {
266                world.add_component(entity, MaterialSource {
267                    albedo: mat_data.albedo,
268                    roughness: mat_data.roughness,
269                    metallic: mat_data.metallic,
270                    unlit: mat_data.unlit,
271                    texture_source: mat_data.texture_source,
272                });
273            }
274
275            let mut resolved_parent = None;
276            if let Some(orig_parent) = data.parent_id {
277                if let Some(&p_id) = id_map.get(&orig_parent) {
278                    resolved_parent = Some(p_id);
279                }
280            } else {
281                resolved_parent = root_parent;
282            }
283
284            if let Some(p_id) = resolved_parent {
285                world.add_component(entity, Parent(p_id));
286                children_map.entry(p_id).or_default().push(new_id);
287            }
288        }
289
290        for (p_id, mut c_list) in children_map {
291            if let Some(&p_ent) = entity_structs.get(&p_id) {
292                world.add_component(p_ent, Children(c_list));
293            } else if let Some(p_ent) = world.get_entity(p_id) {
294                let existing: Vec<u32> = world
295                    .borrow::<Children>()
296                    .get(p_id)
297                    .map(|c| c.0.clone())
298                    .unwrap_or_default();
299                let mut merged = existing;
300                merged.append(&mut c_list);
301                world.add_component(p_ent, Children(merged));
302            }
303        }
304
305        id_map
306    }
307
308    /// Prefab kaydet
309    pub fn save_prefab(
310        world: &World,
311        root_entity_id: u32,
312        file_path: &str,
313        registry: &gizmo_core::registry::ComponentRegistry,
314    ) -> Result<(), String> {
315        if let Some(parent) = std::path::Path::new(file_path).parent() {
316            let _ = fs::create_dir_all(parent);
317        }
318
319        let mut ids_to_save = vec![root_entity_id];
320        let children_storage = world.borrow::<Children>();
321
322        let mut i = 0;
323        while i < ids_to_save.len() {
324            let current = ids_to_save[i];
325            if let Some(children_comp) = children_storage.get(current) {
326                for &child_id in &children_comp.0 {
327                    ids_to_save.push(child_id);
328                }
329            }
330            i += 1;
331        }
332
333        let mut entities_data = Self::serialize_entities(world, ids_to_save.clone(), registry);
334
335        if let Some(root_data) = entities_data
336            .iter_mut()
337            .find(|d| d.original_id == root_entity_id)
338        {
339            root_data.parent_id = None;
340        }
341
342        let mut joints = Vec::new();
343        if let Ok(physics_world) = world.try_get_resource::<gizmo_physics_rigid::world::PhysicsWorld>() {
344            for joint in &physics_world.joints {
345                if ids_to_save.contains(&joint.entity_a.id()) && ids_to_save.contains(&joint.entity_b.id()) {
346                    joints.push(joint.clone());
347                }
348            }
349        }
350
351        let prefab = PrefabData {
352            root_id: root_entity_id,
353            entities: entities_data,
354            joints,
355        };
356
357        let string_data = ron::ser::to_string_pretty(&prefab, ron::ser::PrettyConfig::default())
358            .map_err(|e| format!("[SceneData::save_prefab] Serileştirme hatası: {}", e))?;
359
360        fs::write(file_path, string_data)
361            .map_err(|e| format!("[SceneData::save_prefab] Dosya yazma hatası: {}", e))?;
362
363        tracing::info!("✅ Prefab kaydedildi → {}", file_path);
364        Ok(())
365    }
366
367    /// Prefab yükle
368    pub fn load_prefab(
369        file_path: &str,
370        parent_entity: Option<u32>,
371        world: &mut World,
372        registry: &gizmo_core::registry::ComponentRegistry,
373    ) -> Option<u32> {
374        let string_data = match fs::read_to_string(file_path) {
375            Ok(content) => content,
376            Err(_) => return None,
377        };
378
379        let prefab: PrefabData = match ron::from_str(&string_data) {
380            Ok(p) => p,
381            Err(e) => {
382                tracing::info!("❌ Prefab dosyası geçersiz ({}): {}", file_path, e);
383                return None;
384            }
385        };
386
387        let id_map = Self::instantiate_entities(
388            prefab.entities,
389            parent_entity,
390            world,
391            registry,
392        );
393
394        let new_root_id = id_map.get(&prefab.root_id).copied();
395
396        if let (Some(new_r), Some(p_id)) = (new_root_id, parent_entity) {
397            if let Some(p_ent) = world.get_entity(p_id) {
398                let mut children_list = world
399                    .borrow::<Children>()
400                    .get(p_id)
401                    .map(|c| c.0.clone())
402                    .unwrap_or_default();
403                children_list.push(new_r);
404                world.add_component(p_ent, Children(children_list));
405            }
406        }
407
408        if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics_rigid::world::PhysicsWorld>() {
409            for mut joint in prefab.joints {
410                if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
411                    joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
412                    joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
413                    physics_world.joints.push(joint);
414                }
415            }
416        }
417
418        tracing::info!("✅ Prefab yüklendi ← {}", file_path);
419        new_root_id
420    }
421
422    /// Entity listesini döndürür (Lua API'si için)
423    pub fn get_entity_names(world: &World) -> Vec<(u32, String)> {
424        let mut result = Vec::new();
425        let names = world.borrow::<EntityName>();
426        for (entity_id, _) in names.iter() {
427            if let Some(name) = names.get(entity_id) {
428                result.push((entity_id, name.0.clone()));
429            }
430        }
431        result
432    }
433
434    /// İsme göre entity bul
435    pub fn find_entity_by_name(world: &World, target_name: &str) -> Option<u32> {
436        let names = world.borrow::<EntityName>();
437        for (entity_id, _) in names.iter() {
438            if let Some(name) = names.get(entity_id) {
439                if name.0 == target_name {
440                    return Some(entity_id);
441                }
442            }
443        }
444        None
445    }
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    use gizmo_core::World;
452    use gizmo_physics_rigid::joints::data::{Joint, JointType};
453
454    #[test]
455    fn test_prefab_joint_serialization() {
456        let mut world = World::new();
457        let ent1 = world.spawn();
458        let ent2 = world.spawn();
459
460        let joint = Joint {
461            entity_a: ent1,
462            entity_b: ent2,
463            local_anchor_a: gizmo_math::Vec3::ZERO,
464            local_anchor_b: gizmo_math::Vec3::ZERO,
465            break_force: 1000.0,
466            break_torque: 1000.0,
467            is_broken: false,
468            collision_enabled: false,
469            data: gizmo_physics_rigid::joints::data::JointData::Fixed,
470        };
471
472        let prefab_data = PrefabData {
473            root_id: ent1.id(),
474            entities: vec![],
475            joints: vec![joint.clone()],
476        };
477
478        let serialized = ron::ser::to_string(&prefab_data).unwrap();
479        assert!(serialized.contains("Fixed"));
480
481        let deserialized: PrefabData = ron::from_str(&serialized).unwrap();
482        assert_eq!(deserialized.joints.len(), 1);
483        assert!(matches!(deserialized.joints[0].data, gizmo_physics_rigid::joints::data::JointData::Fixed));
484    }
485}