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 #[serde(default)]
17 pub joints: Vec<gizmo_physics::joints::Joint>,
18}
19
20#[derive(Serialize, Deserialize, Clone)]
22pub struct PrefabData {
23 pub root_id: u32,
24 pub entities: Vec<EntityData>,
25 #[serde(default)]
26 pub joints: Vec<gizmo_physics::joints::Joint>,
27}
28
29#[derive(Serialize, Deserialize, Clone)]
31pub struct EntityData {
32 pub original_id: u32,
33 pub name: Option<String>,
34 pub mesh_source: Option<String>,
35 pub material_source: Option<MaterialData>,
36 #[serde(default)]
37 pub parent_id: Option<u32>,
38 #[serde(default)]
39 pub components: std::collections::BTreeMap<String, ron::Value>,
40}
41
42#[derive(Serialize, Deserialize, Clone)]
44pub struct MaterialData {
45 pub albedo: Vec4,
46 pub roughness: f32,
47 pub metallic: f32,
48 pub unlit: f32,
49 pub texture_source: Option<String>,
50}
51
52impl SceneData {
53 pub fn save(
55 world: &World,
56 file_path: &str,
57 registry: &crate::registry::SceneRegistry,
58 ) -> Result<(), String> {
59 if let Some(parent) = std::path::Path::new(file_path).parent() {
60 let _ = fs::create_dir_all(parent);
61 }
62 let entities_data = Self::serialize_entities(
63 world,
64 world
65 .iter_alive_entities()
66 .into_iter()
67 .map(|e| e.id())
68 .collect(),
69 registry,
70 );
71
72 let mut joints = Vec::new();
73 if let Ok(physics_world) = world.try_get_resource::<gizmo_physics::world::PhysicsWorld>() {
74 joints = physics_world.joints.clone();
75 }
76
77 let scene = SceneData {
78 entities: entities_data,
79 joints,
80 };
81
82 let string_data = ron::ser::to_string_pretty(&scene, ron::ser::PrettyConfig::default())
83 .map_err(|e| format!("[SceneData::save] Serileştirme hatası: {}", e))?;
84
85 fs::write(file_path, string_data)
86 .map_err(|e| format!("[SceneData::save] Dosya yazma hatası: {}", e))?;
87
88 tracing::info!("✅ Sahne kaydedildi → {}", file_path);
89 Ok(())
90 }
91
92 pub fn serialize_entities(
94 world: &World,
95 entity_ids: Vec<u32>,
96 registry: &crate::registry::SceneRegistry,
97 ) -> Vec<EntityData> {
98 let mut entities_data = Vec::new();
99 let names = world.borrow::<EntityName>();
100 let meshes = world.borrow::<Mesh>();
101 let materials = world.borrow::<Material>();
102 let parents = world.borrow::<Parent>();
103
104 for &id in &entity_ids {
105 let name = names.get(id).map(|n| n.0.clone());
106
107 if let Some(ref n) = name {
109 if n.starts_with("Editor ") || n == "Highlight Box" {
110 continue;
111 }
112 }
113
114 let mesh_source = meshes.get(id).map(|m| m.source.clone());
115 let material_source = materials.get(id).map(|m| MaterialData {
116 albedo: m.albedo,
117 roughness: m.roughness,
118 metallic: m.metallic,
119 unlit: match m.material_type {
120 gizmo_renderer::components::MaterialType::Skybox => 2.0,
121 gizmo_renderer::components::MaterialType::Unlit => 1.0,
122 _ => 0.0,
123 },
124 texture_source: m.texture_source.clone(),
125 });
126 let parent_id = parents.get(id).map(|p| p.0);
127
128 let mut dynamic_components = std::collections::BTreeMap::new();
130 for comp_name in registry.all_components() {
131 if let Some(serializer) = registry.get_serializer(comp_name) {
132 if let Some(comp_value) = serializer(world, id) {
133 dynamic_components.insert(comp_name.clone(), comp_value);
134 }
135 }
136 }
137
138 if name.is_some()
139 || mesh_source.is_some()
140 || material_source.is_some()
141 || parent_id.is_some()
142 || !dynamic_components.is_empty()
143 {
144 entities_data.push(EntityData {
145 original_id: id,
146 name,
147 mesh_source,
148 material_source,
149 parent_id,
150 components: dynamic_components,
151 });
152 }
153 }
154 entities_data
155 }
156
157 pub fn load_into(
159 file_path: &str,
160 world: &mut World,
161 device: &wgpu::Device,
162 queue: &wgpu::Queue,
163 texture_bind_group_layout: &wgpu::BindGroupLayout,
164 asset_manager: &mut AssetManager,
165 default_texture_bind_group: Arc<wgpu::BindGroup>,
166 registry: &crate::registry::SceneRegistry,
167 ) -> bool {
168 let string_data = match fs::read_to_string(file_path) {
169 Ok(content) => content,
170 Err(_) => return false,
171 };
172
173 let scene: SceneData = match ron::from_str(&string_data) {
174 Ok(s) => s,
175 Err(e) => {
176 tracing::info!("❌ Sahne dosyası geçersiz ({}): {}", file_path, e);
177 return false;
178 }
179 };
180
181 let id_map = Self::instantiate_entities(
182 scene.entities,
183 None,
184 world,
185 device,
186 queue,
187 texture_bind_group_layout,
188 asset_manager,
189 &default_texture_bind_group,
190 registry,
191 );
192
193 if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics::world::PhysicsWorld>() {
194 for mut joint in scene.joints {
195 if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
196 joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
197 joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
198 physics_world.joints.push(joint);
199 }
200 }
201 }
202
203 tracing::info!("✅ Sahne yüklendi ← {}", file_path);
204 true
205 }
206
207 pub fn instantiate_entities(
209 entities: Vec<EntityData>,
210 root_parent: Option<u32>,
211 world: &mut World,
212 device: &wgpu::Device,
213 queue: &wgpu::Queue,
214 texture_bind_group_layout: &wgpu::BindGroupLayout,
215 asset_manager: &mut AssetManager,
216 default_texture_bind_group: &Arc<wgpu::BindGroup>,
217 registry: &crate::registry::SceneRegistry,
218 ) -> HashMap<u32, u32> {
219 let mut id_map = HashMap::new(); let mut entity_structs = HashMap::new();
221
222 for data in &entities {
224 let root_ent = world.spawn();
225 id_map.insert(data.original_id, root_ent.id());
226 entity_structs.insert(root_ent.id(), root_ent);
227 }
228
229 let mut children_map: HashMap<u32, Vec<u32>> = HashMap::new();
231
232 for data in entities {
233 let new_id = id_map[&data.original_id];
234 let entity = entity_structs[&new_id];
235
236 if let Some(n) = data.name {
237 world.add_component(entity, EntityName::new(&n));
238 }
239 for (comp_name, comp_val) in &data.components {
241 if let Some(deserializer) = registry.get_deserializer(comp_name) {
242 deserializer(world, new_id, comp_val);
243 }
244 }
245
246 if let Some(mesh_src) = data.mesh_source {
247 let mesh = if mesh_src == "inverted_cube" {
248 AssetManager::create_inverted_cube(device)
249 } else if mesh_src == "plane" {
250 AssetManager::create_plane(device, 200.0)
251 } else if mesh_src == "standard_cube" {
252 AssetManager::create_cube(device)
253 } else if mesh_src == "sphere" {
254 AssetManager::create_sphere(device, 1.0, 16, 16)
255 } else if mesh_src == "sprite_quad" {
256 AssetManager::create_sprite_quad(device, 1.0, 1.0)
257 } else if mesh_src.starts_with("gltf_mesh_") {
258 if let Some(cached) = asset_manager.get_cached_mesh(&mesh_src) {
259 cached
260 } else {
261 let file_path = if let Some(idx) = mesh_src.find(".glb") {
263 Some(&mesh_src["gltf_mesh_".len()..idx + 4])
264 } else {
265 mesh_src
266 .find(".gltf")
267 .map(|idx| &mesh_src["gltf_mesh_".len()..idx + 5])
268 };
269
270 if let Some(path) = file_path {
271 let _ = asset_manager.load_gltf_scene(
272 device,
273 queue,
274 texture_bind_group_layout,
275 default_texture_bind_group.clone(),
276 path,
277 );
278 if let Some(cached) = asset_manager.get_cached_mesh(&mesh_src) {
279 cached
280 } else {
281 asset_manager.loading_placeholder_mesh(device)
282 }
283 } else {
284 asset_manager.loading_placeholder_mesh(device)
285 }
286 }
287 } else if mesh_src.starts_with("obj:") {
288 let path = mesh_src.trim_start_matches("obj:");
289 asset_manager.load_obj(device, path)
290 } else {
291 asset_manager.load_obj(device, &mesh_src)
293 };
294 world.add_component(entity, mesh);
295 }
296
297 if let Some(mat_data) = data.material_source {
298 let bind_group = if let Some(tex_path) = &mat_data.texture_source {
299 asset_manager
300 .load_material_texture(device, queue, texture_bind_group_layout, tex_path)
301 .unwrap_or_else(|e| {
302 tracing::info!("Scene Texture error: {}", e);
303 default_texture_bind_group.clone()
304 })
305 } else {
306 default_texture_bind_group.clone()
307 };
308
309 let mut mat = Material::new(bind_group);
310 mat.albedo = mat_data.albedo;
311 mat.roughness = mat_data.roughness;
312 mat.metallic = mat_data.metallic;
313 mat.material_type = if mat_data.unlit > 1.5 {
314 gizmo_renderer::components::MaterialType::Skybox
315 } else if mat_data.unlit > 0.5 {
316 gizmo_renderer::components::MaterialType::Unlit
317 } else {
318 gizmo_renderer::components::MaterialType::Pbr
319 };
320 mat.texture_source = mat_data.texture_source;
321 world.add_component(entity, mat);
322 world.add_component(entity, MeshRenderer::new());
323 }
324
325 let mut resolved_parent = None;
327 if let Some(orig_parent) = data.parent_id {
328 if let Some(&p_id) = id_map.get(&orig_parent) {
330 resolved_parent = Some(p_id);
331 }
332 } else {
333 resolved_parent = root_parent;
335 }
336
337 if let Some(p_id) = resolved_parent {
338 world.add_component(entity, Parent(p_id));
339 children_map.entry(p_id).or_default().push(new_id);
340 }
341 }
342
343 for (p_id, mut c_list) in children_map {
345 if let Some(&p_ent) = entity_structs.get(&p_id) {
346 world.add_component(p_ent, Children(c_list));
347 } else if let Some(p_ent) = world.get_entity(p_id) {
348 let existing: Vec<u32> = world
350 .borrow::<Children>()
351 .get(p_id)
352 .map(|c| c.0.clone())
353 .unwrap_or_default();
354 let mut merged = existing;
355 merged.append(&mut c_list);
356 world.add_component(p_ent, Children(merged));
357 }
358 }
359
360 id_map
361 }
362
363 pub fn save_prefab(
365 world: &World,
366 root_entity_id: u32,
367 file_path: &str,
368 registry: &crate::registry::SceneRegistry,
369 ) -> Result<(), String> {
370 if let Some(parent) = std::path::Path::new(file_path).parent() {
371 let _ = fs::create_dir_all(parent);
372 }
373
374 let mut ids_to_save = vec![root_entity_id];
375 let children_storage = world.borrow::<Children>();
376
377 let mut i = 0;
378 while i < ids_to_save.len() {
379 let current = ids_to_save[i];
380 if let Some(children_comp) = children_storage.get(current) {
381 for &child_id in &children_comp.0 {
382 ids_to_save.push(child_id);
383 }
384 }
385 i += 1;
386 }
387
388 let mut entities_data = Self::serialize_entities(world, ids_to_save.clone(), registry);
389
390 if let Some(root_data) = entities_data
392 .iter_mut()
393 .find(|d| d.original_id == root_entity_id)
394 {
395 root_data.parent_id = None;
396 }
397
398 let mut joints = Vec::new();
399 if let Ok(physics_world) = world.try_get_resource::<gizmo_physics::world::PhysicsWorld>() {
400 for joint in &physics_world.joints {
401 if ids_to_save.contains(&joint.entity_a.id()) && ids_to_save.contains(&joint.entity_b.id()) {
402 joints.push(joint.clone());
403 }
404 }
405 }
406
407 let prefab = PrefabData {
408 root_id: root_entity_id,
409 entities: entities_data,
410 joints,
411 };
412
413 let string_data = ron::ser::to_string_pretty(&prefab, ron::ser::PrettyConfig::default())
414 .map_err(|e| format!("[SceneData::save_prefab] Serileştirme hatası: {}", e))?;
415
416 fs::write(file_path, string_data)
417 .map_err(|e| format!("[SceneData::save_prefab] Dosya yazma hatası: {}", e))?;
418
419 tracing::info!("✅ Prefab kaydedildi → {}", file_path);
420 Ok(())
421 }
422
423 pub fn load_prefab(
425 file_path: &str,
426 parent_entity: Option<u32>,
427 world: &mut World,
428 device: &wgpu::Device,
429 queue: &wgpu::Queue,
430 texture_bind_group_layout: &wgpu::BindGroupLayout,
431 asset_manager: &mut AssetManager,
432 default_texture_bind_group: Arc<wgpu::BindGroup>,
433 registry: &crate::registry::SceneRegistry,
434 ) -> Option<u32> {
435 let string_data = match fs::read_to_string(file_path) {
436 Ok(content) => content,
437 Err(_) => return None,
438 };
439
440 let prefab: PrefabData = match ron::from_str(&string_data) {
441 Ok(p) => p,
442 Err(e) => {
443 tracing::info!("❌ Prefab dosyası geçersiz ({}): {}", file_path, e);
444 return None;
445 }
446 };
447
448 let id_map = Self::instantiate_entities(
449 prefab.entities,
450 parent_entity,
451 world,
452 device,
453 queue,
454 texture_bind_group_layout,
455 asset_manager,
456 &default_texture_bind_group,
457 registry,
458 );
459
460 let new_root_id = id_map.get(&prefab.root_id).copied();
461
462 if let (Some(new_r), Some(p_id)) = (new_root_id, parent_entity) {
463 if let Some(p_ent) = world.get_entity(p_id) {
464 let mut children_list = world
465 .borrow::<Children>()
466 .get(p_id)
467 .map(|c| c.0.clone())
468 .unwrap_or_default();
469 children_list.push(new_r);
470 world.add_component(p_ent, Children(children_list));
471 }
472 }
473
474 if let Ok(mut physics_world) = world.try_get_resource_mut::<gizmo_physics::world::PhysicsWorld>() {
475 for mut joint in prefab.joints {
476 if let (Some(&new_a), Some(&new_b)) = (id_map.get(&joint.entity_a.id()), id_map.get(&joint.entity_b.id())) {
477 joint.entity_a = gizmo_core::entity::Entity::new(new_a, 0);
478 joint.entity_b = gizmo_core::entity::Entity::new(new_b, 0);
479 physics_world.joints.push(joint);
480 }
481 }
482 }
483
484 tracing::info!("✅ Prefab yüklendi ← {}", file_path);
485 new_root_id
486 }
487
488 pub fn get_entity_names(world: &World) -> Vec<(u32, String)> {
490 let mut result = Vec::new();
491 let names = world.borrow::<EntityName>();
492 for (entity_id, _) in names.iter() {
493 if let Some(name) = names.get(entity_id) {
494 result.push((entity_id, name.0.clone()));
495 }
496 }
497 result
498 }
499
500 pub fn find_entity_by_name(world: &World, target_name: &str) -> Option<u32> {
502 let names = world.borrow::<EntityName>();
503 for (entity_id, _) in names.iter() {
504 if let Some(name) = names.get(entity_id) {
505 if name.0 == target_name {
506 return Some(entity_id);
507 }
508 }
509 }
510 None
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use gizmo_core::World;
518 use gizmo_physics::joints::data::{Joint, JointType};
519
520 #[test]
521 fn test_prefab_joint_serialization() {
522 let mut world = World::new();
523 let ent1 = world.spawn();
524 let ent2 = world.spawn();
525
526 let joint = Joint {
527 entity_a: ent1,
528 entity_b: ent2,
529 local_anchor_a: gizmo_math::Vec3::ZERO,
530 local_anchor_b: gizmo_math::Vec3::ZERO,
531 break_force: 1000.0,
532 break_torque: 1000.0,
533 is_broken: false,
534 collision_enabled: false,
535 data: gizmo_physics::joints::data::JointData::Fixed,
536 };
537
538 let prefab_data = PrefabData {
539 root_id: ent1.id(),
540 entities: vec![],
541 joints: vec![joint.clone()],
542 };
543
544 let serialized = ron::ser::to_string(&prefab_data).unwrap();
545 assert!(serialized.contains("Fixed"));
546
547 let deserialized: PrefabData = ron::from_str(&serialized).unwrap();
548 assert_eq!(deserialized.joints.len(), 1);
549 assert!(matches!(deserialized.joints[0].data, gizmo_physics::joints::data::JointData::Fixed));
550 }
551}