From 5cc876fce9aafb3d091f8ba324ca435a54a40f21 Mon Sep 17 00:00:00 2001 From: max Date: Tue, 15 Oct 2024 21:47:23 +0200 Subject: [PATCH] tree hierachy and selection test --- .../Components/SelectedInHierachy.cs | 4 + .../Systems/EditorHierarchyWindow.cs | 120 ++++++++++++++---- Nerfed.Runtime/Systems/LocalToWorldSystem.cs | 1 - 3 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 Nerfed.Editor/Components/SelectedInHierachy.cs diff --git a/Nerfed.Editor/Components/SelectedInHierachy.cs b/Nerfed.Editor/Components/SelectedInHierachy.cs new file mode 100644 index 0000000..8a46182 --- /dev/null +++ b/Nerfed.Editor/Components/SelectedInHierachy.cs @@ -0,0 +1,4 @@ +namespace Nerfed.Editor.Components; + +public readonly record struct SelectedInHierachy; +public readonly record struct ClickedInHierarchy; \ No newline at end of file diff --git a/Nerfed.Editor/Systems/EditorHierarchyWindow.cs b/Nerfed.Editor/Systems/EditorHierarchyWindow.cs index c44e513..2dc52cc 100644 --- a/Nerfed.Editor/Systems/EditorHierarchyWindow.cs +++ b/Nerfed.Editor/Systems/EditorHierarchyWindow.cs @@ -1,59 +1,135 @@ using ImGuiNET; using MoonTools.ECS; +using Nerfed.Editor.Components; using Nerfed.Runtime.Components; namespace Nerfed.Editor.Systems { + // Window that draws entities. internal class EditorHierarchyWindow : MoonTools.ECS.DebugSystem { + private const ImGuiTreeNodeFlags baseFlags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.OpenOnDoubleClick | ImGuiTreeNodeFlags.SpanAvailWidth; + private readonly Filter rootEntitiesWithTransformFilter; - private readonly Filter rootEntitiesFilterBroken; + //private readonly Filter rootEntitiesFilterBroken; private readonly Filter rootEntitiesFilter; + private readonly EditorHierachySelectionSystem hierachySelectionSystem; + public EditorHierarchyWindow(World world) : base(world) { rootEntitiesWithTransformFilter = FilterBuilder.Include().Exclude().Build(); // TODO: this doesn't work. - rootEntitiesFilterBroken = FilterBuilder.Exclude().Build(); + //rootEntitiesFilterBroken = FilterBuilder.Exclude().Build(); // Maybe the parent/child functions should add a root component when not being a child. - rootEntitiesFilter = FilterBuilder.Include().Build(); + rootEntitiesFilter = FilterBuilder.Include().Build(); // Maybe instead of a root, if we need a component that is always on an entity and has some use we could create something like a VersionComponent which only hold an int. // The version would update each time something changes on the entity. - // + // Or a EditorComponent, just a component that always gets added when in editor mode. + + hierachySelectionSystem = new EditorHierachySelectionSystem(world); } public override void Update(TimeSpan delta) { ImGui.Begin("Hierarchy"); - foreach (Entity entity in rootEntitiesWithTransformFilter.Entities) - { - DrawEntityAndChildren(entity); - } + ImGuiTreeNodeFlags flags = baseFlags; + flags |= ImGuiTreeNodeFlags.DefaultOpen; - ImGui.NewLine(); - - foreach (Entity entity in rootEntitiesFilter.Entities) + if (ImGui.TreeNodeEx("World", flags)) { - DrawEntityAndChildren(entity); + foreach (Entity entity in rootEntitiesWithTransformFilter.Entities) + { + DrawEntityAndChildren(entity); + } + + foreach (Entity entity in rootEntitiesFilter.Entities) + { + DrawEntityAndChildren(entity); + } + + ImGui.TreePop(); } ImGui.End(); - } - - private void DrawEntityAndChildren(in Entity entity, int depth = 0) - { - string label = new string(' ', depth); - ImGui.Text($"{label}{entity.ID} | {GetTag(entity)}"); - depth++; - ReverseSpanEnumerator childEntities = World.InRelations(entity); - foreach (Entity childEntity in childEntities) + hierachySelectionSystem.Update(delta); + } + + private void DrawEntityAndChildren(in Entity entity) + { + ImGuiTreeNodeFlags flags = baseFlags; + + if (!World.HasInRelation(entity)) { - DrawEntityAndChildren(childEntity, depth); + flags |= ImGuiTreeNodeFlags.Leaf; + } + + if (World.Has(entity)) + { + flags |= ImGuiTreeNodeFlags.Selected; + } + + if (ImGui.TreeNodeEx($"{entity.ID} | {GetTag(entity)}", flags)) + { + if (ImGui.IsItemClicked() && !ImGui.IsItemToggledOpen()) + { + World.Set(entity, new ClickedInHierarchy()); + } + + ReverseSpanEnumerator childEntities = World.InRelations(entity); + foreach (Entity childEntity in childEntities) + { + DrawEntityAndChildren(childEntity); + } + + ImGui.TreePop(); + } + } + + // System for handling the selected entities in the hierachy. + private class EditorHierachySelectionSystem : MoonTools.ECS.System + { + private readonly Filter selectedEntities; + private readonly Filter clickedEntities; + + public EditorHierachySelectionSystem(World world) : base(world) + { + selectedEntities = FilterBuilder.Include().Build(); + clickedEntities = FilterBuilder.Include().Build(); + } + + public override void Update(TimeSpan delta) + { + ImGuiIOPtr io = ImGui.GetIO(); + + if (!clickedEntities.Empty && !io.KeyCtrl) + { + foreach (Entity entity in selectedEntities.Entities) + { + Remove(entity); + } + } + + foreach (Entity entity in clickedEntities.Entities) + { + // Unselect. + if (Has(entity)) + { + Remove(entity); + } + // Select. + else + { + Set(entity, new SelectedInHierachy()); + } + + Remove(entity); + } } } } diff --git a/Nerfed.Runtime/Systems/LocalToWorldSystem.cs b/Nerfed.Runtime/Systems/LocalToWorldSystem.cs index ac48642..97b31c0 100644 --- a/Nerfed.Runtime/Systems/LocalToWorldSystem.cs +++ b/Nerfed.Runtime/Systems/LocalToWorldSystem.cs @@ -2,7 +2,6 @@ using Nerfed.Runtime.Components; using Nerfed.Runtime.Util; using System.Numerics; -using static System.Runtime.InteropServices.JavaScript.JSType; // TODO: // Explore if having a WorldTransform and LocalTransfom component each holding position, rotation, scale values and the matricies is useful.