Nodes, scene tree, inspector, properties

This commit is contained in:
Chris Bell 2025-01-16 16:03:40 -06:00
parent b8dd6a9614
commit b411a9ee37
10 changed files with 234 additions and 24 deletions

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" /> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project> </project>

View File

@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOpenGlControlBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbd52d95392cd166965daf17dd7beae45eca78a7b438a5392e6fd6222cc44aa_003FOpenGlControlBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOpenGlControlBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbd52d95392cd166965daf17dd7beae45eca78a7b438a5392e6fd6222cc44aa_003FOpenGlControlBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATreeViewItem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbb6fabe194b96d1b9091f838cddf2cd4f3bfae54118f5dbbd6db31d46988adf_003FTreeViewItem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@ -9,8 +9,6 @@ public partial class App : Application
public override void Initialize() public override void Initialize()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()

View File

@ -1,8 +0,0 @@
using GLTFViewer.Rendering;
namespace GLTFViewer;
public static class Engine
{
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace GLTFViewer.Engine;
public class Node
{
public string Name { get; }
public ObservableCollection<Node> Children { get; }
public ObservableCollection<NodeProperty> Properties { get; }
public Node(string name)
{
Name = name;
Children = new ObservableCollection<Node>();
Properties = new ObservableCollection<NodeProperty>();
}
public Node(string name, ObservableCollection<Node> children)
{
Name = name;
Children = children;
Properties = new ObservableCollection<NodeProperty>();
}
public void AddChild(Node child)
{
Children.Add(child);
}
public void RemoveChild(Node child)
{
Children.Remove(child);
}
public void AddProperty(NodeProperty property)
{
if (Properties.Any(p => p.Name == property.Name))
{
throw new InvalidOperationException($"A property with the name {property.Name} already exists in node {Name}.");
}
Properties.Add(property);
}
public void RemoveProperty(NodeProperty property)
{
if (!Properties.Remove(property))
{
Console.WriteLine($"Could not remove property {property.Name} from node {Name}: Property not found.");
}
}
public void SetProperty<T>(string name, T value)
{
var property = Properties.OfType<NodeProperty<T>>().FirstOrDefault(p => p.Name == name);
if (property == null)
{
property = new NodeProperty<T>(name, value);
Properties.Add(property);
}
else
{
property.Value = value;
}
}
}

View File

@ -0,0 +1,21 @@
namespace GLTFViewer.Engine;
public abstract class NodeProperty
{
public string Name { get; }
protected NodeProperty(string name)
{
Name = name;
}
}
public class NodeProperty<T> : NodeProperty
{
public T Value { get; set; }
public NodeProperty(string name, T value) : base(name)
{
Value = value;
}
}

View File

@ -11,7 +11,7 @@ public class Renderer
public void Initialize(GlInterface gl) public void Initialize(GlInterface gl)
{ {
GLLoader.LoadBindings(new AvaloniaBindingsContext(gl)); GLLoader.LoadBindings(new AvaloniaBindingsContext(gl));
GL.ClearColor(0,0,1,1); GL.ClearColor(1,1,1,1);
} }
public void Render(GlInterface gl, int width, int height, int fbo) public void Render(GlInterface gl, int width, int height, int fbo)

View File

@ -0,0 +1,42 @@
using System.Collections.ObjectModel;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
namespace GLTFViewer.Engine.Ui;
public class SceneTree
{
public Node Root { get; } = new Node("root");
private TreeView _sceneTreeView;
private ObservableCollection<Node> _nodes;
public SceneTree(TreeView sceneTreeView)
{
_sceneTreeView = sceneTreeView;
Initialize();
}
public void Initialize()
{
Root.AddProperty(new NodeProperty<string>("TestPropertyString", "TestValue"));
Root.AddProperty(new NodeProperty<int>("TestPropertyInt", 42));
_nodes = new ObservableCollection<Node>()
{
Root
};
_sceneTreeView.ItemsSource = _nodes;
_sceneTreeView.ItemTemplate = new FuncTreeDataTemplate<Node>(
(node, _) =>
{
var textBlock = new TextBlock { Text = node.Name };
return textBlock;
},
node => node.Children
);
}
}

View File

@ -5,17 +5,51 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="GLTFViewer.MainUI"> x:Class="GLTFViewer.MainUI">
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="DimGray"> <!-- <StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="DimGray"> -->
<StackPanel HorizontalAlignment="Stretch" Height="200"> <!-- <StackPanel HorizontalAlignment="Stretch" Height="200"> -->
<Border BorderThickness="2" BorderBrush="Black" Padding="0 10"> <!-- <Border BorderThickness="2" BorderBrush="Black" Padding="0 10"> -->
<!-- <TextBlock Text="Inspector" TextAlignment="Center"/> -->
<!-- </Border> -->
<!-- -->
<!-- <ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> -->
<!-- <TreeView x:Name="PropertyTreeView"></TreeView> -->
<!-- </ScrollViewer> -->
<!-- </StackPanel> -->
<!-- -->
<!-- <StackPanel> -->
<!-- <Border BorderThickness="2" BorderBrush="Black" Padding="0 10"> -->
<!-- <TextBlock Text="Tree" TextAlignment="Center"/> -->
<!-- </Border> -->
<!-- -->
<!-- <ScrollViewer Height="100" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"> -->
<!-- <TreeView x:Name="SceneTreeView"></TreeView> -->
<!-- </ScrollViewer> -->
<!-- </StackPanel> -->
<!-- </StackPanel> -->
<Grid Background="DimGray">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderThickness="2" BorderBrush="Black" Padding="0 10">
<TextBlock Text="Inspector" TextAlignment="Center"/> <TextBlock Text="Inspector" TextAlignment="Center"/>
</Border> </Border>
</StackPanel>
<Border BorderThickness="2" BorderBrush="Black" Padding="0 10"> <ScrollViewer Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TreeView x:Name="PropertyTreeView"></TreeView>
</ScrollViewer>
<Border Grid.Row="2" BorderThickness="2" BorderBrush="Black" Padding="0 10">
<TextBlock Text="Tree" TextAlignment="Center"/> <TextBlock Text="Tree" TextAlignment="Center"/>
</Border> </Border>
<TreeView HorizontalAlignment="Stretch">
<TreeViewItem></TreeViewItem> <ScrollViewer Grid.Row="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
</TreeView> <TreeView x:Name="SceneTreeView"></TreeView>
</StackPanel> </ScrollViewer>
</Grid>
</UserControl> </UserControl>

View File

@ -1,13 +1,65 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using GLTFViewer.Engine;
using GLTFViewer.Engine.Ui;
namespace GLTFViewer; namespace GLTFViewer;
public partial class MainUI : UserControl public partial class MainUI : UserControl
{ {
private SceneTree _sceneTree;
private TreeView _sceneTreeView;
private TreeView _propertyTreeView;
public MainUI() public MainUI()
{ {
InitializeComponent(); InitializeComponent();
_sceneTreeView = SceneTreeView;
_propertyTreeView = PropertyTreeView;
_sceneTree = new SceneTree(_sceneTreeView);
_sceneTreeView.SelectionChanged += PropertyTreeViewOnSelectionChanged;
var random = new Random();
Node previousNode = _sceneTree.Root;
for (int i = 0; i < 20; i++)
{
var newNode = new Node($"child{i}");
if (random.Next(2) == 0)
{
previousNode.AddChild(newNode);
}
else
{
_sceneTree.Root.AddChild(newNode);
}
previousNode = newNode;
}
}
private void PropertyTreeViewOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
_propertyTreeView.ItemsSource = _sceneTreeView.SelectedItem is Node node ? node.Properties : null;
_propertyTreeView.ItemTemplate = new FuncTreeDataTemplate<NodeProperty>(
(property, _) =>
{
var textBlock = new TextBlock { Text = property.Name };
return textBlock;
},
property => null
);
} }
} }