feat: implement scene graph system with ECS hierarchy support
Add hierarchical scene graph for editor with TreeView UI, runtime HierarchyUtility for parent/child linked-list management, and incremental sync between editor world and scene graph nodes. - SceneGraphNode/SceneNode/EntityNode with World, Scene, Entity refs - SceneGraphBuilder — construct tree from ECS World queries - HierarchyUtility — SetParent, RemoveParent, IsAncestor, cascade destroy - EditorWorldService + SceneGraphSyncService — editor world lifecycle & incremental sync - Hierarchy.xaml — TreeView with DataTemplate + SceneGraphTemplateSelector - 25 unit tests covering hierarchy ops and scene graph building
This commit is contained in:
@@ -38,10 +38,10 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.251219" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" Version="8.2.251219" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1721" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="2.0.1" />
|
||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,9 +5,40 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Ghost.Editor.Views.Controls"
|
||||
xmlns:sg="using:Ghost.Editor.Core.SceneGraph"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<local:SceneGraphTemplateSelector
|
||||
x:Key="SceneGraphTemplateSelector"
|
||||
SceneNodeTemplate="{StaticResource SceneNodeTemplate}"
|
||||
EntityNodeTemplate="{StaticResource EntityNodeTemplate}" />
|
||||
|
||||
<DataTemplate x:Key="SceneNodeTemplate" x:DataType="sg:SceneNode">
|
||||
<TreeViewItem
|
||||
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind Children, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="10,0,0,0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="EntityNodeTemplate" x:DataType="sg:EntityNode">
|
||||
<TreeViewItem
|
||||
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind Children, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="5,0,0,0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -50,19 +81,17 @@
|
||||
Margin="0,0,4,0"
|
||||
FontSize="{StaticResource ToolbarFontIconFontSize}"
|
||||
Glyph="" />
|
||||
<TextBox Grid.Column="1" PlaceholderText="Sreach item..." />
|
||||
<TextBox Grid.Column="1" PlaceholderText="Search item..." />
|
||||
</Grid>
|
||||
|
||||
<Border Margin="-8,8,-4,-4" Style="{StaticResource HorizontalStrongDivider}" />
|
||||
</StackPanel>
|
||||
|
||||
<ListView Grid.Row="1" Padding="4,2,0,2">
|
||||
<ListViewItem Content="Test" />
|
||||
<ListViewItem Content="Test" />
|
||||
<ListViewItem Content="Test" />
|
||||
<ListViewItem Content="Test" />
|
||||
<ListViewItem Content="Test" />
|
||||
<ListViewItem Content="Test" />
|
||||
</ListView>
|
||||
<TreeView
|
||||
x:Name="SceneTreeView"
|
||||
Grid.Row="1"
|
||||
Padding="4,2,0,2"
|
||||
ItemTemplateSelector="{StaticResource SceneGraphTemplateSelector}"
|
||||
SelectionMode="Single" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,14 +1,62 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Services;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace Ghost.Editor.Views.Controls;
|
||||
|
||||
public sealed partial class Hierarchy : UserControl
|
||||
{
|
||||
private readonly SceneGraphSyncService _syncService;
|
||||
private readonly IInspectorService _inspectorService;
|
||||
private readonly EditorWorldService _worldService;
|
||||
private DispatcherQueueTimer? _syncTimer;
|
||||
|
||||
public Hierarchy()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_syncService = App.GetService<SceneGraphSyncService>();
|
||||
_inspectorService = App.GetService<IInspectorService>();
|
||||
_worldService = App.GetService<EditorWorldService>();
|
||||
|
||||
SceneTreeView.ItemsSource = _worldService.RootNodes;
|
||||
|
||||
SceneTreeView.ItemInvoked += OnTreeViewItemInvoked;
|
||||
SceneTreeView.SelectionChanged += OnTreeViewSelectionChanged;
|
||||
|
||||
_syncTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
_syncTimer.Interval = TimeSpan.FromMilliseconds(100);
|
||||
_syncTimer.Tick += OnSyncTick;
|
||||
_syncTimer.Start();
|
||||
|
||||
Unloaded += OnUnloaded;
|
||||
}
|
||||
|
||||
private void OnSyncTick(DispatcherQueueTimer sender, object args)
|
||||
{
|
||||
if (_syncService.Tick())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTreeViewItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
|
||||
{
|
||||
if (args.InvokedItem is IInspectable inspectable)
|
||||
{
|
||||
_inspectorService.SetSelected(inspectable, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTreeViewSelectionChanged(object sender, TreeViewSelectionChangedEventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
_syncTimer?.Stop();
|
||||
SceneTreeView.ItemInvoked -= OnTreeViewItemInvoked;
|
||||
SceneTreeView.SelectionChanged -= OnTreeViewSelectionChanged;
|
||||
Unloaded -= OnUnloaded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Views.Controls;
|
||||
|
||||
public partial class SceneGraphTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate? SceneNodeTemplate
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DataTemplate? EntityNodeTemplate
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
var result = item switch
|
||||
{
|
||||
SceneNode => SceneNodeTemplate,
|
||||
EntityNode => EntityNodeTemplate,
|
||||
_ => base.SelectTemplateCore(item)
|
||||
};
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
return SelectTemplateCore(item);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user