feat: implement complete scene graph system with hierarchical editor support
- Add SceneNode and EntityNode classes for editor-only metadata storage - Implement SceneGraph view-model with O(1) entity lookup via internal caching - Create IdRemapTable for file-local to global entity ID remapping on load - Implement SceneSerializationContext for load/save operation tracking - Add JSON-serializable SceneAssetData, EntityData, and ComponentData models - Implement SceneSerializer for save/load with validation and reference remapping - Add comprehensive documentation: README.md, IMPLEMENTATION_GUIDE.md, SYSTEM_SUMMARY.md - Update Ghost.Editor.Core.csproj to reference Ghost.Entities assembly - Support parent-child relationships via Hierarchy component - Enforce no cross-scene entity references - Keep runtime minimal: only SceneID, Hierarchy, LocalToWorld components - All editor metadata (names, UI state) stored in editor-only SceneNode/EntityNode classes This implements the architecture from SceneGraph Plan.md with clean separation of concerns, minimal runtime footprint, and AOT compatibility.
This commit is contained in:
50
Ghost.Editor.Core/SceneGraph/SceneNode.cs
Normal file
50
Ghost.Editor.Core/SceneGraph/SceneNode.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Ghost.Entities;
|
||||
|
||||
namespace Ghost.Editor.Core.SceneGraph;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Scene node in the editor hierarchy.
|
||||
/// Contains editor-only metadata like name and display state.
|
||||
/// The actual scene data (entities, components) is stored as SceneID in the runtime ECS world.
|
||||
/// </summary>
|
||||
public class SceneNode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public short SceneId { get; private set; }
|
||||
public Guid SceneGuid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Child entity nodes belonging to this scene.
|
||||
/// </summary>
|
||||
public ObservableCollection<EntityNode> Children { get; }
|
||||
|
||||
public SceneNode(string name, short sceneId, Guid? sceneGuid = null)
|
||||
{
|
||||
Name = name;
|
||||
SceneId = sceneId;
|
||||
SceneGuid = sceneGuid ?? Guid.NewGuid();
|
||||
Children = new ObservableCollection<EntityNode>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an entity node by its global entity ID.
|
||||
/// Searches recursively through the hierarchy.
|
||||
/// </summary>
|
||||
public EntityNode? FindEntityNode(Entity entityId)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child.EntityId == entityId)
|
||||
return child;
|
||||
|
||||
var found = child.FindRecursive(entityId);
|
||||
if (found != null)
|
||||
return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToString() => $"Scene: {Name} (ID: {SceneId})";
|
||||
}
|
||||
Reference in New Issue
Block a user