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#[derive(Serialize, Deserialize, Clone)]
14pub struct SceneData {
15 pub entities: Vec<EntityData>,
16}
17
18#[derive(Serialize, Deserialize, Clone)]
20pub struct PrefabData {
21 pub root_id: u32,
22 pub entities: Vec<EntityData>,
23}
24
25#[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#[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 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 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 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 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 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 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(); let mut entity_structs = HashMap::new();
201
202 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 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 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 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 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 let mut resolved_parent = None;
307 if let Some(orig_parent) = data.parent_id {
308 if let Some(&p_id) = id_map.get(&orig_parent) {
310 resolved_parent = Some(p_id);
311 }
312 } else {
313 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 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 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 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 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 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 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 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 }