Skip to main content

gizmo_scene/
scene.rs

1use gizmo_core::{EntityName, World};
2use gizmo_math::Vec4;
3use gizmo_renderer::asset::AssetManager;
4use gizmo_renderer::components::{Material, Mesh, MeshRenderer};
5use serde::{Deserialize, Serialize};
6use std::fs;
7use std::sync::Arc;
8
9use gizmo_core::component::{Children, Parent};
10use std::collections::HashMap;
11
12/// Tam sahne verisi — tüm entity'ler ve bileşenleri
13#[derive(Serialize, Deserialize, Clone)]
14pub struct SceneData {
15    pub entities: Vec<EntityData>,
16}
17
18/// Prefab verisi — Tıpkı SceneData gibi ama kök entity'si var
19#[derive(Serialize, Deserialize, Clone)]
20pub struct PrefabData {
21    pub root_id: u32,
22    pub entities: Vec<EntityData>,
23}
24
25/// Tek bir entity'nin serileştirilebilir verisi
26#[derive(Serialize, Deserialize, Clone)]
27pub struct EntityData {
28    pub original_id: u32,
29    pub name: Option<String>,
30    pub mesh_source: Option<String>,
31    pub material_source: Option<MaterialData>,
32    #[serde(default)]
33    pub parent_id: Option<u32>,
34    #[serde(default)]
35    pub components: std::collections::BTreeMap<String, ron::Value>,
36}
37
38/// Material serileştirme verisi (GPU bind group'u diske yazılamaz)
39#[derive(Serialize, Deserialize, Clone)]
40pub struct MaterialData {
41    pub albedo: Vec4,
42    pub roughness: f32,
43    pub metallic: f32,
44    pub unlit: f32,
45    pub texture_source: Option<String>,
46}
47
48impl SceneData {
49    /// Mevcut World durumunu JSON dosyası olarak diske kaydeder
50    pub fn save(
51        world: &World,
52        file_path: &str,
53        registry: &crate::registry::SceneRegistry,
54    ) -> Result<(), String> {
55        if let Some(parent) = std::path::Path::new(file_path).parent() {
56            let _ = fs::create_dir_all(parent);
57        }
58        let entities_data = Self::serialize_entities(
59            world,
60            world
61                .iter_alive_entities()
62                .into_iter()
63                .map(|e| e.id())
64                .collect(),
65            registry,
66        );
67
68        let scene = SceneData {
69            entities: entities_data,
70        };
71
72        let string_data = ron::ser::to_string_pretty(&scene, ron::ser::PrettyConfig::default())
73            .map_err(|e| format!("[SceneData::save] Serileştirme hatası: {}", e))?;
74
75        fs::write(file_path, string_data)
76            .map_err(|e| format!("[SceneData::save] Dosya yazma hatası: {}", e))?;
77
78        println!("✅ Sahne kaydedildi → {}", file_path);
79        Ok(())
80    }
81
82    /// Belirtilen entity ID'lerini serileştirir
83    pub fn serialize_entities(
84        world: &World,
85        entity_ids: Vec<u32>,
86        registry: &crate::registry::SceneRegistry,
87    ) -> Vec<EntityData> {
88        let mut entities_data = Vec::new();
89        let names = world.borrow::<EntityName>();
90        let meshes = world.borrow::<Mesh>();
91        let materials = world.borrow::<Material>();
92        let parents = world.borrow::<Parent>();
93
94        for &id in &entity_ids {
95            let name = names.get(id).map(|n| n.0.clone());
96
97            // Gizmo Studio'nun içsel araçlarını kaydetme (Gizmo kurguları, Highlight box, grid vs.)
98            if let Some(ref n) = name {
99                if n.starts_with("Editor ") || n == "Highlight Box" {
100                    continue;
101                }
102            }
103
104            let mesh_source = meshes.get(id).map(|m| m.source.clone());
105            let material_source = materials.get(id).map(|m| MaterialData {
106                albedo: m.albedo,
107                roughness: m.roughness,
108                metallic: m.metallic,
109                unlit: match m.material_type {
110                    gizmo_renderer::components::MaterialType::Skybox => 2.0,
111                    gizmo_renderer::components::MaterialType::Unlit => 1.0,
112                    _ => 0.0,
113                },
114                texture_source: m.texture_source.clone(),
115            });
116            let parent_id = parents.get(id).map(|p| p.0);
117
118            // Dinamik bileşenleri Registry üzerinden tarayarak JSON AST'sine (ron::Value) dönüştür
119            let mut dynamic_components = std::collections::BTreeMap::new();
120            for comp_name in registry.all_components() {
121                if let Some(serializer) = registry.get_serializer(comp_name) {
122                    if let Some(comp_value) = serializer(world, id) {
123                        dynamic_components.insert(comp_name.clone(), comp_value);
124                    }
125                }
126            }
127
128            if name.is_some()
129                || mesh_source.is_some()
130                || material_source.is_some()
131                || parent_id.is_some()
132                || !dynamic_components.is_empty()
133            {
134                entities_data.push(EntityData {
135                    original_id: id,
136                    name,
137                    mesh_source,
138                    material_source,
139                    parent_id,
140                    components: dynamic_components,
141                });
142            }
143        }
144        entities_data
145    }
146
147    /// JSON sahne dosyasını okuyup World'e entity olarak yükler
148    pub fn load_into(
149        file_path: &str,
150        world: &mut World,
151        device: &wgpu::Device,
152        queue: &wgpu::Queue,
153        texture_bind_group_layout: &wgpu::BindGroupLayout,
154        asset_manager: &mut AssetManager,
155        default_texture_bind_group: Arc<wgpu::BindGroup>,
156        registry: &crate::registry::SceneRegistry,
157    ) -> bool {
158        let string_data = match fs::read_to_string(file_path) {
159            Ok(content) => content,
160            Err(_) => return false,
161        };
162
163        let scene: SceneData = match ron::from_str(&string_data) {
164            Ok(s) => s,
165            Err(e) => {
166                println!("❌ Sahne dosyası geçersiz ({}): {}", file_path, e);
167                return false;
168            }
169        };
170
171        Self::instantiate_entities(
172            scene.entities,
173            None,
174            world,
175            device,
176            queue,
177            texture_bind_group_layout,
178            asset_manager,
179            &default_texture_bind_group,
180            registry,
181        );
182
183        println!("✅ Sahne yüklendi ← {}", file_path);
184        true
185    }
186
187    /// Verilen entity listesini instantiate eder, id eşleştirmelerini yapar ve gerekirse root bir parent'a bağlar
188    pub fn instantiate_entities(
189        entities: Vec<EntityData>,
190        root_parent: Option<u32>,
191        world: &mut World,
192        device: &wgpu::Device,
193        queue: &wgpu::Queue,
194        texture_bind_group_layout: &wgpu::BindGroupLayout,
195        asset_manager: &mut AssetManager,
196        default_texture_bind_group: &Arc<wgpu::BindGroup>,
197        registry: &crate::registry::SceneRegistry,
198    ) -> HashMap<u32, u32> {
199        let mut id_map = HashMap::new(); // original_id -> new_entity_id
200        let mut entity_structs = HashMap::new();
201
202        // Entity'leri oluştur ve id haritasını çıkar
203        for data in &entities {
204            let root_ent = world.spawn();
205            id_map.insert(data.original_id, root_ent.id());
206            entity_structs.insert(root_ent.id(), root_ent);
207        }
208
209        // Parent-Child ilişkilerini toplayacağımız geçici yapı (new_parent -> [new_children])
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            // Dinamik bileşenleri Registry üzerinden yükle
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                let mesh = if mesh_src == "inverted_cube" {
228                    AssetManager::create_inverted_cube(device)
229                } else if mesh_src == "plane" {
230                    AssetManager::create_plane(device, 200.0)
231                } else if mesh_src == "standard_cube" {
232                    AssetManager::create_cube(device)
233                } else if mesh_src == "sphere" {
234                    AssetManager::create_sphere(device, 1.0, 16, 16)
235                } else if mesh_src == "sprite_quad" {
236                    AssetManager::create_sprite_quad(device, 1.0, 1.0)
237                } else if mesh_src.starts_with("gltf_mesh_") {
238                    if let Some(cached) = asset_manager.get_cached_mesh(&mesh_src) {
239                        cached
240                    } else {
241                        // Eğer GLTF RAM'de yoksa, path'i çıkar ve gizlice parse edip Cache'e yükle.
242                        let file_path = if let Some(idx) = mesh_src.find(".glb") {
243                            Some(&mesh_src["gltf_mesh_".len()..idx + 4])
244                        } else {
245                            mesh_src
246                                .find(".gltf")
247                                .map(|idx| &mesh_src["gltf_mesh_".len()..idx + 5])
248                        };
249
250                        if let Some(path) = file_path {
251                            let _ = asset_manager.load_gltf_scene(
252                                device,
253                                queue,
254                                texture_bind_group_layout,
255                                default_texture_bind_group.clone(),
256                                path,
257                            );
258                            if let Some(cached) = asset_manager.get_cached_mesh(&mesh_src) {
259                                cached
260                            } else {
261                                asset_manager.loading_placeholder_mesh(device)
262                            }
263                        } else {
264                            asset_manager.loading_placeholder_mesh(device)
265                        }
266                    }
267                } else if mesh_src.starts_with("obj:") {
268                    let path = mesh_src.trim_start_matches("obj:");
269                    asset_manager.load_obj(device, path)
270                } else {
271                    // Fail-safe obj loading
272                    asset_manager.load_obj(device, &mesh_src)
273                };
274                world.add_component(entity, mesh);
275            }
276
277            if let Some(mat_data) = data.material_source {
278                let bind_group = if let Some(tex_path) = &mat_data.texture_source {
279                    asset_manager
280                        .load_material_texture(device, queue, texture_bind_group_layout, tex_path)
281                        .unwrap_or_else(|e| {
282                            println!("Scene Texture error: {}", e);
283                            default_texture_bind_group.clone()
284                        })
285                } else {
286                    default_texture_bind_group.clone()
287                };
288
289                let mut mat = Material::new(bind_group);
290                mat.albedo = mat_data.albedo;
291                mat.roughness = mat_data.roughness;
292                mat.metallic = mat_data.metallic;
293                mat.material_type = if mat_data.unlit > 1.5 {
294                    gizmo_renderer::components::MaterialType::Skybox
295                } else if mat_data.unlit > 0.5 {
296                    gizmo_renderer::components::MaterialType::Unlit
297                } else {
298                    gizmo_renderer::components::MaterialType::Pbr
299                };
300                mat.texture_source = mat_data.texture_source;
301                world.add_component(entity, mat);
302                world.add_component(entity, MeshRenderer::new());
303            }
304
305            // Hiyerarşi Bağlantıları
306            let mut resolved_parent = None;
307            if let Some(orig_parent) = data.parent_id {
308                // Kendi içerisinde (bu sahnede/prefabda) kaydedilmiş bir parent varsa ona bağla
309                if let Some(&p_id) = id_map.get(&orig_parent) {
310                    resolved_parent = Some(p_id);
311                }
312            } else {
313                // Eğer entity'nin kendi parent'ı yoksa, root_parent verilmişse ona tak
314                resolved_parent = root_parent;
315            }
316
317            if let Some(p_id) = resolved_parent {
318                world.add_component(entity, Parent(p_id));
319                children_map.entry(p_id).or_default().push(new_id);
320            }
321        }
322
323        // Tüm Children bileşenlerini ilgili parent'lara ekle
324        for (p_id, mut c_list) in children_map {
325            if let Some(&p_ent) = entity_structs.get(&p_id) {
326                world.add_component(p_ent, Children(c_list));
327            } else if let Some(p_ent) = world.get_entity(p_id) {
328                // External parent (e.g. root_parent passed in) — merge with existing children.
329                let existing: Vec<u32> = world
330                    .borrow::<Children>()
331                    .get(p_id)
332                    .map(|c| c.0.clone())
333                    .unwrap_or_default();
334                let mut merged = existing;
335                merged.append(&mut c_list);
336                world.add_component(p_ent, Children(merged));
337            }
338        }
339
340        id_map
341    }
342
343    /// Prefab kaydet (Verilen entity ve tüm alt çocukları)
344    pub fn save_prefab(
345        world: &World,
346        root_entity_id: u32,
347        file_path: &str,
348        registry: &crate::registry::SceneRegistry,
349    ) -> Result<(), String> {
350        if let Some(parent) = std::path::Path::new(file_path).parent() {
351            let _ = fs::create_dir_all(parent);
352        }
353
354        let mut ids_to_save = vec![root_entity_id];
355        let children_storage = world.borrow::<Children>();
356
357        let mut i = 0;
358        while i < ids_to_save.len() {
359            let current = ids_to_save[i];
360            if let Some(children_comp) = children_storage.get(current) {
361                for &child_id in &children_comp.0 {
362                    ids_to_save.push(child_id);
363                }
364            }
365            i += 1;
366        }
367
368        let mut entities_data = Self::serialize_entities(world, ids_to_save, registry);
369
370        // Prefab'ın root entity'sinin parent'ını kopar ki bağımsız yüklensin
371        if let Some(root_data) = entities_data
372            .iter_mut()
373            .find(|d| d.original_id == root_entity_id)
374        {
375            root_data.parent_id = None;
376        }
377
378        let prefab = PrefabData {
379            root_id: root_entity_id,
380            entities: entities_data,
381        };
382
383        let string_data = ron::ser::to_string_pretty(&prefab, ron::ser::PrettyConfig::default())
384            .map_err(|e| format!("[SceneData::save_prefab] Serileştirme hatası: {}", e))?;
385
386        fs::write(file_path, string_data)
387            .map_err(|e| format!("[SceneData::save_prefab] Dosya yazma hatası: {}", e))?;
388
389        println!("✅ Prefab kaydedildi → {}", file_path);
390        Ok(())
391    }
392
393    /// Prefab yükle
394    pub fn load_prefab(
395        file_path: &str,
396        parent_entity: Option<u32>,
397        world: &mut World,
398        device: &wgpu::Device,
399        queue: &wgpu::Queue,
400        texture_bind_group_layout: &wgpu::BindGroupLayout,
401        asset_manager: &mut AssetManager,
402        default_texture_bind_group: Arc<wgpu::BindGroup>,
403        registry: &crate::registry::SceneRegistry,
404    ) -> Option<u32> {
405        let string_data = match fs::read_to_string(file_path) {
406            Ok(content) => content,
407            Err(_) => return None,
408        };
409
410        let prefab: PrefabData = match ron::from_str(&string_data) {
411            Ok(p) => p,
412            Err(e) => {
413                println!("❌ Prefab dosyası geçersiz ({}): {}", file_path, e);
414                return None;
415            }
416        };
417
418        let id_map = Self::instantiate_entities(
419            prefab.entities,
420            parent_entity,
421            world,
422            device,
423            queue,
424            texture_bind_group_layout,
425            asset_manager,
426            &default_texture_bind_group,
427            registry,
428        );
429
430        let new_root_id = id_map.get(&prefab.root_id).copied();
431
432        if let (Some(new_r), Some(p_id)) = (new_root_id, parent_entity) {
433            if let Some(p_ent) = world.get_entity(p_id) {
434                let mut children_list = world
435                    .borrow::<Children>()
436                    .get(p_id)
437                    .map(|c| c.0.clone())
438                    .unwrap_or_default();
439                children_list.push(new_r);
440                world.add_component(p_ent, Children(children_list));
441            }
442        }
443
444        println!("✅ Prefab yüklendi ← {}", file_path);
445        new_root_id
446    }
447
448    /// Entity listesini döndürür (Lua API'si için)
449    pub fn get_entity_names(world: &World) -> Vec<(u32, String)> {
450        let mut result = Vec::new();
451        let names = world.borrow::<EntityName>();
452        for (entity_id, _) in names.iter() {
453            if let Some(name) = names.get(entity_id) {
454                result.push((entity_id, name.0.clone()));
455            }
456        }
457        result
458    }
459
460    /// İsme göre entity bul
461    pub fn find_entity_by_name(world: &World, target_name: &str) -> Option<u32> {
462        let names = world.borrow::<EntityName>();
463        for (entity_id, _) in names.iter() {
464            if let Some(name) = names.get(entity_id) {
465                if name.0 == target_name {
466                    return Some(entity_id);
467                }
468            }
469        }
470        None
471    }
472}
473
474#[cfg(test)]
475mod tests {
476
477    // Test removed because EntityData relies dynamically on SceneRegistry serialization
478}