1use std::collections::HashMap;
6use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct PrefabId(pub u32);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct PrefabInstanceId(pub u32);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct ComponentId(pub u32);
18
19#[derive(Debug, Clone, PartialEq)]
22pub enum ComponentValue {
23 Bool(bool),
24 Int(i64),
25 Float(f64),
26 Vec2([f64; 2]),
27 Vec3([f64; 3]),
28 Vec4([f64; 4]),
29 String(String),
30 EntityRef(u32),
31 PrefabRef(PrefabId),
32 List(Vec<ComponentValue>),
33 Map(Vec<(String, ComponentValue)>),
34}
35
36impl ComponentValue {
37 pub fn type_name(&self) -> &'static str {
38 match self {
39 Self::Bool(_) => "bool",
40 Self::Int(_) => "int",
41 Self::Float(_) => "float",
42 Self::Vec2(_) => "vec2",
43 Self::Vec3(_) => "vec3",
44 Self::Vec4(_) => "vec4",
45 Self::String(_) => "string",
46 Self::EntityRef(_) => "entity_ref",
47 Self::PrefabRef(_) => "prefab_ref",
48 Self::List(_) => "list",
49 Self::Map(_) => "map",
50 }
51 }
52
53 pub fn is_same_type(&self, other: &Self) -> bool {
54 self.type_name() == other.type_name()
55 }
56
57 pub fn as_float(&self) -> Option<f64> {
58 match self {
59 Self::Float(v) => Some(*v),
60 Self::Int(v) => Some(*v as f64),
61 Self::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
62 _ => None,
63 }
64 }
65
66 pub fn lerp(&self, other: &Self, t: f64) -> Option<Self> {
67 match (self, other) {
68 (Self::Float(a), Self::Float(b)) => Some(Self::Float(a + (b - a) * t)),
69 (Self::Int(a), Self::Int(b)) => Some(Self::Int(*a + ((*b - *a) as f64 * t) as i64)),
70 (Self::Vec2(a), Self::Vec2(b)) => Some(Self::Vec2([
71 a[0] + (b[0] - a[0]) * t,
72 a[1] + (b[1] - a[1]) * t,
73 ])),
74 (Self::Vec3(a), Self::Vec3(b)) => Some(Self::Vec3([
75 a[0] + (b[0] - a[0]) * t,
76 a[1] + (b[1] - a[1]) * t,
77 a[2] + (b[2] - a[2]) * t,
78 ])),
79 (Self::Vec4(a), Self::Vec4(b)) => Some(Self::Vec4([
80 a[0] + (b[0] - a[0]) * t,
81 a[1] + (b[1] - a[1]) * t,
82 a[2] + (b[2] - a[2]) * t,
83 a[3] + (b[3] - a[3]) * t,
84 ])),
85 _ => None,
86 }
87 }
88}
89
90impl fmt::Display for ComponentValue {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Self::Bool(v) => write!(f, "{}", v),
94 Self::Int(v) => write!(f, "{}", v),
95 Self::Float(v) => write!(f, "{:.4}", v),
96 Self::Vec2(v) => write!(f, "({:.3},{:.3})", v[0], v[1]),
97 Self::Vec3(v) => write!(f, "({:.3},{:.3},{:.3})", v[0], v[1], v[2]),
98 Self::Vec4(v) => write!(f, "({:.3},{:.3},{:.3},{:.3})", v[0],v[1],v[2],v[3]),
99 Self::String(s) => write!(f, "\"{}\"", s),
100 Self::EntityRef(id) => write!(f, "entity:{}", id),
101 Self::PrefabRef(id) => write!(f, "prefab:{}", id.0),
102 Self::List(items) => {
103 write!(f, "[")?;
104 for (i, item) in items.iter().enumerate() {
105 if i > 0 { write!(f, ",")?; }
106 write!(f, "{}", item)?;
107 }
108 write!(f, "]")
109 }
110 Self::Map(entries) => {
111 write!(f, "{{")?;
112 for (i, (k, v)) in entries.iter().enumerate() {
113 if i > 0 { write!(f, ",")?; }
114 write!(f, "{}:{}", k, v)?;
115 }
116 write!(f, "}}")
117 }
118 }
119 }
120}
121
122#[derive(Debug, Clone)]
125pub struct ComponentDef {
126 pub id: ComponentId,
127 pub name: String,
128 pub category: String,
129 pub fields: Vec<FieldDef>,
130 pub version: u32,
131 pub deprecated: bool,
132 pub description: &'static str,
133}
134
135#[derive(Debug, Clone)]
136pub struct FieldDef {
137 pub name: String,
138 pub default_value: ComponentValue,
139 pub description: &'static str,
140 pub min: Option<f64>,
141 pub max: Option<f64>,
142 pub hidden_in_editor: bool,
143 pub read_only: bool,
144 pub animatable: bool,
145}
146
147impl FieldDef {
148 pub fn new(name: &str, default: ComponentValue) -> Self {
149 Self {
150 name: name.to_string(),
151 default_value: default,
152 description: "",
153 min: None,
154 max: None,
155 hidden_in_editor: false,
156 read_only: false,
157 animatable: false,
158 }
159 }
160
161 pub fn float(name: &str, default: f64) -> Self {
162 Self::new(name, ComponentValue::Float(default))
163 }
164
165 pub fn vec3(name: &str, x: f64, y: f64, z: f64) -> Self {
166 Self::new(name, ComponentValue::Vec3([x, y, z]))
167 }
168
169 pub fn bool(name: &str, default: bool) -> Self {
170 Self::new(name, ComponentValue::Bool(default))
171 }
172
173 pub fn string(name: &str, default: &str) -> Self {
174 Self::new(name, ComponentValue::String(default.to_string()))
175 }
176}
177
178#[derive(Debug, Clone)]
181pub struct ComponentInstance {
182 pub def_id: ComponentId,
183 pub values: HashMap<String, ComponentValue>,
184 pub enabled: bool,
185}
186
187impl ComponentInstance {
188 pub fn new(def_id: ComponentId) -> Self {
189 Self { def_id, values: HashMap::new(), enabled: true }
190 }
191
192 pub fn get(&self, field: &str) -> Option<&ComponentValue> {
193 self.values.get(field)
194 }
195
196 pub fn set(&mut self, field: &str, value: ComponentValue) {
197 self.values.insert(field.to_string(), value);
198 }
199
200 pub fn from_def(def: &ComponentDef) -> Self {
201 let mut inst = Self::new(def.id);
202 for field in &def.fields {
203 inst.values.insert(field.name.clone(), field.default_value.clone());
204 }
205 inst
206 }
207}
208
209#[derive(Debug, Clone)]
212pub struct PrefabNode {
213 pub name: String,
214 pub local_position: [f64; 3],
215 pub local_rotation: [f64; 4], pub local_scale: [f64; 3],
217 pub components: Vec<ComponentInstance>,
218 pub children: Vec<PrefabNode>,
219 pub tags: Vec<String>,
220 pub active: bool,
221 pub static_: bool,
222 pub layer: u32,
223 pub nested_prefab: Option<(PrefabId, Vec<PrefabOverride>)>,
224}
225
226impl PrefabNode {
227 pub fn new(name: &str) -> Self {
228 Self {
229 name: name.to_string(),
230 local_position: [0.0, 0.0, 0.0],
231 local_rotation: [0.0, 0.0, 0.0, 1.0],
232 local_scale: [1.0, 1.0, 1.0],
233 components: Vec::new(),
234 children: Vec::new(),
235 tags: Vec::new(),
236 active: true,
237 static_: false,
238 layer: 0,
239 nested_prefab: None,
240 }
241 }
242
243 pub fn add_component(&mut self, inst: ComponentInstance) {
244 self.components.push(inst);
245 }
246
247 pub fn add_child(&mut self, child: PrefabNode) {
248 self.children.push(child);
249 }
250
251 pub fn find_child(&self, name: &str) -> Option<&PrefabNode> {
252 for child in &self.children {
253 if child.name == name { return Some(child); }
254 if let Some(found) = child.find_child(name) { return Some(found); }
255 }
256 None
257 }
258
259 pub fn find_child_mut(&mut self, name: &str) -> Option<&mut PrefabNode> {
260 let idx = self.children.iter().position(|c| c.name == name);
262 if let Some(i) = idx {
263 return Some(&mut self.children[i]);
264 }
265 for child in &mut self.children {
266 if let Some(found) = child.find_child_mut(name) { return Some(found); }
267 }
268 None
269 }
270
271 pub fn total_node_count(&self) -> usize {
272 1 + self.children.iter().map(|c| c.total_node_count()).sum::<usize>()
273 }
274
275 pub fn depth(&self) -> u32 {
276 1 + self.children.iter().map(|c| c.depth()).max().unwrap_or(0)
277 }
278}
279
280#[derive(Debug, Clone)]
284pub struct PrefabOverride {
285 pub path: String, pub value: ComponentValue,
287 pub added: bool, pub removed: bool, }
290
291impl PrefabOverride {
292 pub fn set(path: &str, value: ComponentValue) -> Self {
293 Self {
294 path: path.to_string(),
295 value,
296 added: false,
297 removed: false,
298 }
299 }
300
301 pub fn add(path: &str, value: ComponentValue) -> Self {
302 Self {
303 path: path.to_string(),
304 value,
305 added: true,
306 removed: false,
307 }
308 }
309
310 pub fn remove(path: &str) -> Self {
311 Self {
312 path: path.to_string(),
313 value: ComponentValue::Bool(false),
314 added: false,
315 removed: true,
316 }
317 }
318
319 pub fn node_path(&self) -> &str {
320 if let Some(dot) = self.path.rfind('.') {
321 &self.path[..dot]
322 } else {
323 &self.path
324 }
325 }
326
327 pub fn field_path(&self) -> Option<&str> {
328 self.path.rfind('.').map(|dot| &self.path[dot+1..])
329 }
330}
331
332#[derive(Debug, Clone)]
335pub struct PrefabVariant {
336 pub name: String,
337 pub description: String,
338 pub overrides: Vec<PrefabOverride>,
339 pub is_default: bool,
340 pub thumbnail_path: Option<String>,
341 pub tags: Vec<String>,
342}
343
344impl PrefabVariant {
345 pub fn new(name: &str) -> Self {
346 Self {
347 name: name.to_string(),
348 description: String::new(),
349 overrides: Vec::new(),
350 is_default: false,
351 thumbnail_path: None,
352 tags: Vec::new(),
353 }
354 }
355
356 pub fn default_variant() -> Self {
357 let mut v = Self::new("Default");
358 v.is_default = true;
359 v
360 }
361
362 pub fn add_override(&mut self, ov: PrefabOverride) {
363 if let Some(existing) = self.overrides.iter_mut().find(|o| o.path == ov.path) {
365 *existing = ov;
366 } else {
367 self.overrides.push(ov);
368 }
369 }
370
371 pub fn remove_override(&mut self, path: &str) {
372 self.overrides.retain(|o| o.path != path);
373 }
374
375 pub fn get_override(&self, path: &str) -> Option<&PrefabOverride> {
376 self.overrides.iter().find(|o| o.path == path)
377 }
378
379 pub fn override_count(&self) -> usize { self.overrides.len() }
380}
381
382#[derive(Debug, Clone)]
385pub struct Prefab {
386 pub id: PrefabId,
387 pub name: String,
388 pub description: String,
389 pub author: String,
390 pub version: u32,
391 pub root: PrefabNode,
392 pub variants: Vec<PrefabVariant>,
393 pub active_variant: usize,
394 pub asset_path: Option<String>,
395 pub thumbnail_path: Option<String>,
396 pub tags: Vec<String>,
397 pub created_at: u64,
398 pub modified_at: u64,
399 pub is_locked: bool,
400}
401
402impl Prefab {
403 pub fn new(id: PrefabId, name: &str) -> Self {
404 Self {
405 id,
406 name: name.to_string(),
407 description: String::new(),
408 author: String::new(),
409 version: 1,
410 root: PrefabNode::new(name),
411 variants: vec![PrefabVariant::default_variant()],
412 active_variant: 0,
413 asset_path: None,
414 thumbnail_path: None,
415 tags: Vec::new(),
416 created_at: 0,
417 modified_at: 0,
418 is_locked: false,
419 }
420 }
421
422 pub fn add_variant(&mut self, variant: PrefabVariant) {
423 self.variants.push(variant);
424 }
425
426 pub fn active_variant(&self) -> &PrefabVariant {
427 &self.variants[self.active_variant.min(self.variants.len().saturating_sub(1))]
428 }
429
430 pub fn active_variant_mut(&mut self) -> &mut PrefabVariant {
431 let idx = self.active_variant.min(self.variants.len().saturating_sub(1));
432 &mut self.variants[idx]
433 }
434
435 pub fn set_active_variant(&mut self, name: &str) -> bool {
436 if let Some(idx) = self.variants.iter().position(|v| v.name == name) {
437 self.active_variant = idx;
438 return true;
439 }
440 false
441 }
442
443 pub fn node_count(&self) -> usize { self.root.total_node_count() }
444 pub fn depth(&self) -> u32 { self.root.depth() }
445
446 pub fn variant_count(&self) -> usize { self.variants.len() }
447
448 pub fn mark_modified(&mut self, time: u64) {
449 self.modified_at = time;
450 self.version += 1;
451 }
452}
453
454#[derive(Debug, Clone)]
457pub struct PrefabInstance {
458 pub id: PrefabInstanceId,
459 pub prefab_id: PrefabId,
460 pub variant_name: Option<String>,
461 pub entity_id: u32,
462 pub overrides: Vec<PrefabOverride>,
463 pub position: [f64; 3],
464 pub rotation: [f64; 4],
465 pub scale: [f64; 3],
466 pub unlinked: bool,
467 pub parent_instance: Option<PrefabInstanceId>,
468}
469
470impl PrefabInstance {
471 pub fn new(id: PrefabInstanceId, prefab_id: PrefabId, entity_id: u32) -> Self {
472 Self {
473 id,
474 prefab_id,
475 variant_name: None,
476 entity_id,
477 overrides: Vec::new(),
478 position: [0.0, 0.0, 0.0],
479 rotation: [0.0, 0.0, 0.0, 1.0],
480 scale: [1.0, 1.0, 1.0],
481 unlinked: false,
482 parent_instance: None,
483 }
484 }
485
486 pub fn add_override(&mut self, ov: PrefabOverride) {
487 if let Some(existing) = self.overrides.iter_mut().find(|o| o.path == ov.path) {
488 *existing = ov;
489 } else {
490 self.overrides.push(ov);
491 }
492 }
493
494 pub fn clear_override(&mut self, path: &str) {
495 self.overrides.retain(|o| o.path != path);
496 }
497
498 pub fn clear_all_overrides(&mut self) {
499 self.overrides.clear();
500 }
501
502 pub fn has_overrides(&self) -> bool { !self.overrides.is_empty() }
503 pub fn override_count(&self) -> usize { self.overrides.len() }
504
505 pub fn is_override_at(&self, path: &str) -> bool {
506 self.overrides.iter().any(|o| o.path == path)
507 }
508
509 pub fn effective_value<'a>(&'a self, path: &str, prefab: &'a Prefab) -> Option<&'a ComponentValue> {
510 if let Some(ov) = self.overrides.iter().find(|o| o.path == path) {
512 if !ov.removed { return Some(&ov.value); }
513 return None;
514 }
515 let variant_name = self.variant_name.as_deref().unwrap_or("Default");
517 if let Some(variant) = prefab.variants.iter().find(|v| v.name == variant_name) {
518 if let Some(ov) = variant.overrides.iter().find(|o| o.path == path) {
519 if !ov.removed { return Some(&ov.value); }
520 return None;
521 }
522 }
523 if let Some(default) = prefab.variants.iter().find(|v| v.is_default) {
525 if let Some(ov) = default.overrides.iter().find(|o| o.path == path) {
526 if !ov.removed { return Some(&ov.value); }
527 }
528 }
529 None
530 }
531}
532
533#[derive(Debug, Clone)]
536pub struct PrefabDiff {
537 pub path: String,
538 pub kind: DiffKind,
539 pub base_value: Option<ComponentValue>,
540 pub instance_value: Option<ComponentValue>,
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq)]
544pub enum DiffKind {
545 Modified,
546 Added,
547 Removed,
548}
549
550impl fmt::Display for PrefabDiff {
551 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552 match self.kind {
553 DiffKind::Modified => write!(f, "~ {} : {:?} → {:?}", self.path,
554 self.base_value, self.instance_value),
555 DiffKind::Added => write!(f, "+ {} : {:?}", self.path, self.instance_value),
556 DiffKind::Removed => write!(f, "- {} : {:?}", self.path, self.base_value),
557 }
558 }
559}
560
561pub fn diff_instance(instance: &PrefabInstance, _prefab: &Prefab) -> Vec<PrefabDiff> {
562 let mut diffs = Vec::new();
563 for ov in &instance.overrides {
564 diffs.push(PrefabDiff {
565 path: ov.path.clone(),
566 kind: if ov.added { DiffKind::Added }
567 else if ov.removed { DiffKind::Removed }
568 else { DiffKind::Modified },
569 base_value: None,
570 instance_value: if ov.removed { None } else { Some(ov.value.clone()) },
571 });
572 }
573 diffs
574}
575
576#[derive(Debug, Clone)]
579pub struct PrefabLibrary {
580 prefabs: HashMap<PrefabId, Prefab>,
581 instances: HashMap<PrefabInstanceId, PrefabInstance>,
582 next_prefab_id: u32,
583 next_instance_id: u32,
584 pub search_index: Vec<(PrefabId, String, Vec<String>)>,
585}
586
587impl PrefabLibrary {
588 pub fn new() -> Self {
589 let mut lib = Self {
590 prefabs: HashMap::new(),
591 instances: HashMap::new(),
592 next_prefab_id: 1,
593 next_instance_id: 1,
594 search_index: Vec::new(),
595 };
596 lib.populate_defaults();
597 lib
598 }
599
600 fn populate_defaults(&mut self) {
601 let shapes = [
603 ("Cube", vec!["mesh", "primitive", "shape"]),
604 ("Sphere", vec!["mesh", "primitive", "shape"]),
605 ("Cylinder", vec!["mesh", "primitive", "shape"]),
606 ("Capsule", vec!["mesh", "primitive", "shape"]),
607 ("Plane", vec!["mesh", "primitive", "flat"]),
608 ("Quad", vec!["mesh", "primitive", "flat", "ui"]),
609 ];
610 for (name, tags) in &shapes {
611 let id = PrefabId(self.next_prefab_id);
612 self.next_prefab_id += 1;
613 let mut p = Prefab::new(id, name);
614 p.tags = tags.iter().map(|s| s.to_string()).collect();
615 p.tags.push("built-in".into());
616 self.prefabs.insert(id, p);
617 }
618 let lights = [
620 ("Directional Light", vec!["light", "directional"]),
621 ("Point Light", vec!["light", "point"]),
622 ("Spot Light", vec!["light", "spot"]),
623 ("Area Light", vec!["light", "area"]),
624 ];
625 for (name, tags) in &lights {
626 let id = PrefabId(self.next_prefab_id);
627 self.next_prefab_id += 1;
628 let mut p = Prefab::new(id, name);
629 p.tags = tags.iter().map(|s| s.to_string()).collect();
630 p.tags.push("built-in".into());
631 self.prefabs.insert(id, p);
632 }
633 let id = PrefabId(self.next_prefab_id);
635 self.next_prefab_id += 1;
636 let mut cam = Prefab::new(id, "Camera");
637 cam.tags = vec!["camera".into(), "built-in".into()];
638 self.prefabs.insert(id, cam);
639
640 let sdf_id = PrefabId(self.next_prefab_id);
642 self.next_prefab_id += 1;
643 let mut sdf = Prefab::new(sdf_id, "SDF Body");
644 sdf.tags = vec!["sdf".into(), "body".into(), "built-in".into()];
645 self.prefabs.insert(sdf_id, sdf);
646
647 let ff_id = PrefabId(self.next_prefab_id);
649 self.next_prefab_id += 1;
650 let mut ff = Prefab::new(ff_id, "Force Field");
651 ff.tags = vec!["physics".into(), "force_field".into(), "built-in".into()];
652 self.prefabs.insert(ff_id, ff);
653
654 self.rebuild_index();
655 }
656
657 fn rebuild_index(&mut self) {
658 self.search_index = self.prefabs.iter()
659 .map(|(&id, p)| (id, p.name.clone(), p.tags.clone()))
660 .collect();
661 }
662
663 pub fn register(&mut self, prefab: Prefab) -> PrefabId {
664 let id = prefab.id;
665 self.prefabs.insert(id, prefab);
666 self.rebuild_index();
667 id
668 }
669
670 pub fn create(&mut self, name: &str) -> PrefabId {
671 let id = PrefabId(self.next_prefab_id);
672 self.next_prefab_id += 1;
673 self.prefabs.insert(id, Prefab::new(id, name));
674 self.rebuild_index();
675 id
676 }
677
678 pub fn remove(&mut self, id: PrefabId) -> Option<Prefab> {
679 let p = self.prefabs.remove(&id);
680 self.rebuild_index();
681 p
682 }
683
684 pub fn get(&self, id: PrefabId) -> Option<&Prefab> {
685 self.prefabs.get(&id)
686 }
687
688 pub fn get_mut(&mut self, id: PrefabId) -> Option<&mut Prefab> {
689 self.prefabs.get_mut(&id)
690 }
691
692 pub fn instantiate(&mut self, prefab_id: PrefabId, entity_id: u32) -> Option<PrefabInstanceId> {
693 if !self.prefabs.contains_key(&prefab_id) { return None; }
694 let id = PrefabInstanceId(self.next_instance_id);
695 self.next_instance_id += 1;
696 self.instances.insert(id, PrefabInstance::new(id, prefab_id, entity_id));
697 Some(id)
698 }
699
700 pub fn destroy_instance(&mut self, id: PrefabInstanceId) {
701 self.instances.remove(&id);
702 }
703
704 pub fn instance(&self, id: PrefabInstanceId) -> Option<&PrefabInstance> {
705 self.instances.get(&id)
706 }
707
708 pub fn instance_mut(&mut self, id: PrefabInstanceId) -> Option<&mut PrefabInstance> {
709 self.instances.get_mut(&id)
710 }
711
712 pub fn instances_of(&self, prefab_id: PrefabId) -> Vec<&PrefabInstance> {
713 self.instances.values().filter(|i| i.prefab_id == prefab_id).collect()
714 }
715
716 pub fn search(&self, query: &str) -> Vec<&Prefab> {
717 let q = query.to_lowercase();
718 self.prefabs.values()
719 .filter(|p| {
720 p.name.to_lowercase().contains(&q)
721 || p.tags.iter().any(|t| t.to_lowercase().contains(&q))
722 || p.description.to_lowercase().contains(&q)
723 })
724 .collect()
725 }
726
727 pub fn by_tag(&self, tag: &str) -> Vec<&Prefab> {
728 self.prefabs.values()
729 .filter(|p| p.tags.iter().any(|t| t == tag))
730 .collect()
731 }
732
733 pub fn prefab_count(&self) -> usize { self.prefabs.len() }
734 pub fn instance_count(&self) -> usize { self.instances.len() }
735
736 pub fn propagate_change(&mut self, prefab_id: PrefabId, path: &str, value: ComponentValue) {
738 for inst in self.instances.values_mut() {
739 if inst.prefab_id == prefab_id && !inst.unlinked {
740 if !inst.is_override_at(path) {
742 let _ = (path, &value);
745 }
746 }
747 }
748 }
749
750 pub fn unlink_instance(&mut self, id: PrefabInstanceId) {
752 if let Some(inst) = self.instances.get_mut(&id) {
753 inst.unlinked = true;
754 }
755 }
756
757 pub fn relink_instance(&mut self, id: PrefabInstanceId) {
759 if let Some(inst) = self.instances.get_mut(&id) {
760 inst.unlinked = false;
761 }
762 }
763
764 pub fn create_from_overrides(
766 &mut self,
767 name: &str,
768 instance_id: PrefabInstanceId,
769 ) -> Option<PrefabId> {
770 let base_id = self.instances.get(&instance_id)?.prefab_id;
771 let overrides = self.instances.get(&instance_id)?.overrides.clone();
772 let new_id = self.create(name);
773 if let Some(base) = self.prefabs.get(&base_id) {
774 let mut variant = PrefabVariant::new("FromInstance");
775 for ov in overrides { variant.add_override(ov); }
776 let base_clone = base.root.clone();
777 if let Some(new_prefab) = self.prefabs.get_mut(&new_id) {
778 new_prefab.root = base_clone;
779 new_prefab.variants.push(variant);
780 }
781 }
782 Some(new_id)
783 }
784
785 pub fn stats(&self) -> PrefabLibraryStats {
786 let total_nodes: usize = self.prefabs.values().map(|p| p.node_count()).sum();
787 let total_variants: usize = self.prefabs.values().map(|p| p.variant_count()).sum();
788 PrefabLibraryStats {
789 prefab_count: self.prefabs.len(),
790 instance_count: self.instances.len(),
791 total_nodes,
792 total_variants,
793 overridden_instances: self.instances.values().filter(|i| i.has_overrides()).count(),
794 unlinked_instances: self.instances.values().filter(|i| i.unlinked).count(),
795 }
796 }
797}
798
799#[derive(Debug, Clone)]
800pub struct PrefabLibraryStats {
801 pub prefab_count: usize,
802 pub instance_count: usize,
803 pub total_nodes: usize,
804 pub total_variants: usize,
805 pub overridden_instances: usize,
806 pub unlinked_instances: usize,
807}
808
809impl fmt::Display for PrefabLibraryStats {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 write!(f,
812 "Prefabs: {} ({} nodes, {} variants) | Instances: {} ({} overridden, {} unlinked)",
813 self.prefab_count, self.total_nodes, self.total_variants,
814 self.instance_count, self.overridden_instances, self.unlinked_instances,
815 )
816 }
817}
818
819impl Default for PrefabLibrary {
820 fn default() -> Self { Self::new() }
821}
822
823#[derive(Debug, Clone, Copy, PartialEq, Eq)]
826pub enum PrefabEditorMode {
827 Browse,
828 EditPrefab,
829 EditInstance,
830 VariantEditor,
831 DiffView,
832}
833
834#[derive(Debug)]
835pub struct PrefabEditor {
836 pub library: PrefabLibrary,
837 pub mode: PrefabEditorMode,
838 pub selected_prefab: Option<PrefabId>,
839 pub selected_instance: Option<PrefabInstanceId>,
840 pub selected_variant: Option<String>,
841 pub search_query: String,
842 pub filter_tag: Option<String>,
843 pub sort_by: PrefabSortMode,
844 pub show_built_in: bool,
845 pub show_variants: bool,
846 pub show_instances_panel: bool,
847 pub pending_delete: Option<PrefabId>,
848 undo_stack: Vec<PrefabEditAction>,
849 redo_stack: Vec<PrefabEditAction>,
850}
851
852#[derive(Debug, Clone, Copy, PartialEq, Eq)]
853pub enum PrefabSortMode {
854 Name,
855 RecentlyModified,
856 InstanceCount,
857 NodeCount,
858}
859
860#[derive(Debug, Clone)]
861pub enum PrefabEditAction {
862 CreatePrefab(PrefabId),
863 DeletePrefab(PrefabId),
864 AddVariant { prefab: PrefabId, variant_name: String },
865 RemoveVariant { prefab: PrefabId, variant_name: String },
866 AddOverride { instance: PrefabInstanceId, ov: PrefabOverride },
867 RemoveOverride { instance: PrefabInstanceId, path: String },
868 UnlinkInstance(PrefabInstanceId),
869 RelinkInstance(PrefabInstanceId),
870 RenameNode { prefab: PrefabId, path: String, old: String, new: String },
871}
872
873impl PrefabEditor {
874 pub fn new() -> Self {
875 Self {
876 library: PrefabLibrary::new(),
877 mode: PrefabEditorMode::Browse,
878 selected_prefab: None,
879 selected_instance: None,
880 selected_variant: None,
881 search_query: String::new(),
882 filter_tag: None,
883 sort_by: PrefabSortMode::Name,
884 show_built_in: true,
885 show_variants: true,
886 show_instances_panel: true,
887 pending_delete: None,
888 undo_stack: Vec::new(),
889 redo_stack: Vec::new(),
890 }
891 }
892
893 pub fn search_results(&self) -> Vec<&Prefab> {
894 let mut results = if self.search_query.is_empty() {
895 if let Some(tag) = &self.filter_tag {
896 self.library.by_tag(tag)
897 } else {
898 self.library.prefabs.values().collect()
899 }
900 } else {
901 self.library.search(&self.search_query)
902 };
903 if !self.show_built_in {
904 results.retain(|p| !p.tags.contains(&"built-in".to_string()));
905 }
906 match self.sort_by {
907 PrefabSortMode::Name => results.sort_by(|a, b| a.name.cmp(&b.name)),
908 PrefabSortMode::RecentlyModified => results.sort_by(|a, b| b.modified_at.cmp(&a.modified_at)),
909 PrefabSortMode::InstanceCount => {
910 results.sort_by_key(|p| std::cmp::Reverse(self.library.instances_of(p.id).len()));
911 }
912 PrefabSortMode::NodeCount => results.sort_by_key(|p| std::cmp::Reverse(p.node_count())),
913 }
914 results
915 }
916
917 pub fn select_prefab(&mut self, id: PrefabId) {
918 self.selected_prefab = Some(id);
919 self.selected_instance = None;
920 self.mode = PrefabEditorMode::EditPrefab;
921 }
922
923 pub fn select_instance(&mut self, id: PrefabInstanceId) {
924 self.selected_instance = Some(id);
925 self.mode = PrefabEditorMode::EditInstance;
926 if let Some(inst) = self.library.instance(id) {
927 self.selected_prefab = Some(inst.prefab_id);
928 }
929 }
930
931 pub fn create_prefab(&mut self, name: &str) -> PrefabId {
932 let id = self.library.create(name);
933 self.undo_stack.push(PrefabEditAction::CreatePrefab(id));
934 self.redo_stack.clear();
935 self.selected_prefab = Some(id);
936 self.mode = PrefabEditorMode::EditPrefab;
937 id
938 }
939
940 pub fn delete_selected(&mut self) {
941 if let Some(id) = self.selected_prefab.take() {
942 self.undo_stack.push(PrefabEditAction::DeletePrefab(id));
943 self.redo_stack.clear();
944 self.library.remove(id);
945 }
946 }
947
948 pub fn add_override_to_selected(&mut self, ov: PrefabOverride) {
949 if let Some(iid) = self.selected_instance {
950 self.undo_stack.push(PrefabEditAction::AddOverride {
951 instance: iid, ov: ov.clone(),
952 });
953 self.redo_stack.clear();
954 if let Some(inst) = self.library.instance_mut(iid) {
955 inst.add_override(ov);
956 }
957 }
958 }
959
960 pub fn clear_override_on_selected(&mut self, path: &str) {
961 if let Some(iid) = self.selected_instance {
962 self.undo_stack.push(PrefabEditAction::RemoveOverride {
963 instance: iid, path: path.to_string(),
964 });
965 self.redo_stack.clear();
966 if let Some(inst) = self.library.instance_mut(iid) {
967 inst.clear_override(path);
968 }
969 }
970 }
971
972 pub fn undo(&mut self) {
973 if let Some(action) = self.undo_stack.pop() {
974 match &action {
975 PrefabEditAction::CreatePrefab(id) => { self.library.remove(*id); }
976 PrefabEditAction::DeletePrefab(_) => { }
977 _ => {}
978 }
979 self.redo_stack.push(action);
980 }
981 }
982
983 pub fn redo(&mut self) {
984 if let Some(action) = self.redo_stack.pop() {
985 self.undo_stack.push(action);
986 }
987 }
988
989 pub fn diff_view(&self) -> Option<Vec<PrefabDiff>> {
990 let iid = self.selected_instance?;
991 let inst = self.library.instance(iid)?;
992 let prefab = self.library.get(inst.prefab_id)?;
993 Some(diff_instance(inst, prefab))
994 }
995
996 pub fn stats(&self) -> PrefabLibraryStats { self.library.stats() }
997}
998
999use std::cmp::Reverse;
1001
1002impl Default for PrefabEditor {
1003 fn default() -> Self { Self::new() }
1004}
1005
1006#[cfg(test)]
1009mod tests {
1010 use super::*;
1011
1012 #[test]
1013 fn create_and_search() {
1014 let mut lib = PrefabLibrary::new();
1015 let id = lib.create("TestRobot");
1016 if let Some(p) = lib.get_mut(id) {
1017 p.tags.push("robot".into());
1018 }
1019 let results = lib.search("robot");
1020 assert!(!results.is_empty());
1021 assert!(results.iter().any(|p| p.id == id));
1022 }
1023
1024 #[test]
1025 fn instantiate_and_override() {
1026 let mut lib = PrefabLibrary::new();
1027 let pid = lib.create("Cube");
1028 let iid = lib.instantiate(pid, 42).unwrap();
1029 lib.instance_mut(iid).unwrap().add_override(
1030 PrefabOverride::set("Root.Transform.position", ComponentValue::Vec3([1.0, 0.0, 0.0]))
1031 );
1032 assert!(lib.instance(iid).unwrap().has_overrides());
1033 }
1034
1035 #[test]
1036 fn prefab_node_hierarchy() {
1037 let mut root = PrefabNode::new("Root");
1038 root.add_child(PrefabNode::new("Child1"));
1039 let mut c2 = PrefabNode::new("Child2");
1040 c2.add_child(PrefabNode::new("Grandchild"));
1041 root.add_child(c2);
1042 assert_eq!(root.total_node_count(), 4);
1043 assert_eq!(root.depth(), 3);
1044 }
1045
1046 #[test]
1047 fn variant_override() {
1048 let mut variant = PrefabVariant::new("Big");
1049 variant.add_override(PrefabOverride::set(
1050 "Root.Transform.scale",
1051 ComponentValue::Vec3([2.0, 2.0, 2.0]),
1052 ));
1053 assert_eq!(variant.override_count(), 1);
1054 variant.remove_override("Root.Transform.scale");
1055 assert_eq!(variant.override_count(), 0);
1056 }
1057
1058 #[test]
1059 fn component_value_lerp() {
1060 let a = ComponentValue::Float(0.0);
1061 let b = ComponentValue::Float(10.0);
1062 let m = a.lerp(&b, 0.5).unwrap();
1063 assert!((m.as_float().unwrap() - 5.0).abs() < 1e-9);
1064 }
1065
1066 #[test]
1067 fn prefab_editor_search() {
1068 let ed = PrefabEditor::new();
1069 let results = ed.search_results();
1070 assert!(!results.is_empty());
1071 }
1072
1073 #[test]
1074 fn prefab_editor_create_undo() {
1075 let mut ed = PrefabEditor::new();
1076 let count_before = ed.library.prefab_count();
1077 ed.create_prefab("NewPrefab");
1078 assert_eq!(ed.library.prefab_count(), count_before + 1);
1079 ed.undo();
1080 assert_eq!(ed.library.prefab_count(), count_before);
1081 }
1082}