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#[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#[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#[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#[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 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); let name = names.get(id).map(|n| n.0.clone());
104
105 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, ®istry.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 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 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 let deserializer = bevy_reflect::serde::TypedReflectDeserializer::new(type_reg, ®istry.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 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 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 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 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 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}