From bca72cf0d494ce717b3e195b48a52e163092d7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 10 Apr 2022 12:18:43 +0200 Subject: [PATCH] Add / Remove Components --- .../src/EditWindows/ComponentEditWindow.bf | 359 +++++++++--------- GlitchyEngine/src/Extension/System/String.bf | 21 + GlitchyEngine/src/ImGui/ImGuiExtension.bf | 100 ++++- GlitchyEngine/src/World/Components.bf | 14 +- 4 files changed, 302 insertions(+), 192 deletions(-) create mode 100644 GlitchyEngine/src/Extension/System/String.bf diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index 577ae1e..00e84c3 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -2,6 +2,7 @@ using ImGui; using GlitchyEngine.World; using System; using GlitchyEngine.Math; +using System.Collections; namespace GlitchyEditor.EditWindows { @@ -13,9 +14,19 @@ namespace GlitchyEditor.EditWindows private EntityHierarchyWindow _entityHierarchyWindow; + private List<(Type, ComponentAttribute attribute)> _componentTypes = new .() ~ delete _; + public this(EntityHierarchyWindow entityHierarchyWindow) { _entityHierarchyWindow = entityHierarchyWindow; + + for (let type in Type.Types) + { + if (let componentAttribute = type.GetCustomAttribute()) + { + _componentTypes.Add((type, componentAttribute)); + } + } } protected override void InternalShow() @@ -39,9 +50,56 @@ namespace GlitchyEditor.EditWindows private void ShowComponents(Entity entity) { ShowNameComponentEditor(entity); - ShowTransformComponentEditor(entity); - ShowCameraComponentEditor(entity); - ShowSpriteRendererComponentEditor(entity); + + ShowComponentEditor("Transform", entity, => ShowTransformComponentEditor); + ShowComponentEditor("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu); + ShowComponentEditor("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu); + + ShowAddComponentButton(entity); + } + + private static void ShowComponentContextMenu(Entity entity, TComponent* component) where TComponent: struct, new + { + if (ImGui.Selectable("Remove Component")) + entity.RemoveComponent(); + } + + private static void ShowComponentEditor(String header, Entity entity, function void(Entity, TComponent*) showComponentEditor, function void(Entity, TComponent*) showComponentContextMenu = null) where TComponent: struct, new + { + if (!entity.HasComponent()) + return; + + TComponent* component = entity.GetComponent(); + + ImGui.PushID(header); + + bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed); + + if (showComponentContextMenu != null) + { + ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x); + + if (ImGui.SmallButton("...")) + { + ImGui.OpenPopup("component_popup"); + } + + if (ImGui.BeginPopup("component_popup")) + { + showComponentContextMenu(entity, component); + + ImGui.EndPopup(); + } + } + + if (nodeOpen) + { + showComponentEditor(entity, component); + + ImGui.TreePop(); + } + + ImGui.PopID(); } private static void ShowNameComponentEditor(Entity entity) @@ -79,215 +137,152 @@ namespace GlitchyEditor.EditWindows } } - private static bool Vector3Control(StringView label, ref Vector3 value, Vector3 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f) + private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform) { - bool changed = false; + Vector3 position = transform.Position; + if (ImGui.EditVector3("Position", ref position)) + transform.Position = position; - ImGui.PushID(label); - defer ImGui.PopID(); - - ImGui.Columns(2); - defer ImGui.Columns(1); - ImGui.SetColumnWidth(0, columnWidth); - - ImGui.TextUnformatted(label); - - ImGui.NextColumn(); - - ImGui.PushMultiItemsWidths(3, ImGui.CalcItemWidth()); - ImGui.PushStyleVar(.ItemSpacing, ImGui.Vec2.Zero); - defer ImGui.PopStyleVar(); - - float lineHeight = ImGui.GetFont().FontSize + ImGui.GetStyle().FramePadding.y * 2.0f; - ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight); - - ImGui.PushStyleColor(.Button, ImGui.Color(230, 25, 45).Value); - ImGui.PushStyleColor(.ButtonHovered, ImGui.Color(150, 25, 45).Value); - ImGui.PushStyleColor(.ButtonActive, ImGui.Color(230, 120, 130).Value); - - if (ImGui.Button("X", buttonSize)) - { - value.X = resetValues.X; - changed = true; - } - - ImGui.SameLine(); - - if (ImGui.DragFloat("##X", &value.X, dragSpeed)) - changed = true; - - ImGui.PopItemWidth(); - ImGui.SameLine(); - - ImGui.PopStyleColor(3); - ImGui.PushStyleColor(.Button, ImGui.Color(50, 190, 15).Value); - ImGui.PushStyleColor(.ButtonHovered, ImGui.Color(50, 120, 15).Value); - ImGui.PushStyleColor(.ButtonActive, ImGui.Color(116, 190, 99).Value); - - if (ImGui.Button("Y", buttonSize)) - { - value.Y = resetValues.Y; - changed = true; - } + Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler); + if (ImGui.EditVector3("Rotation", ref rotationEuler)) + transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); - ImGui.SameLine(); - - if (ImGui.DragFloat("##Y", &value.Y, dragSpeed)) - changed = true; - - ImGui.PopItemWidth(); - ImGui.SameLine(); - - ImGui.PopStyleColor(3); - ImGui.PushStyleColor(.Button, ImGui.Color(55, 55, 230).Value); - ImGui.PushStyleColor(.ButtonHovered, ImGui.Color(55, 55, 150).Value); - ImGui.PushStyleColor(.ButtonActive, ImGui.Color(90, 90, 230).Value); - - if (ImGui.Button("Z", buttonSize)) - { - value.Z = resetValues.Z; - changed = true; - } - - ImGui.SameLine(); - - if (ImGui.DragFloat("##Z", &value.Z, dragSpeed)) - changed = true; - - ImGui.PopItemWidth(); - - ImGui.PopStyleColor(3); - - return changed; - } - - private static void ShowTransformComponentEditor(Entity entity) - { - if (!entity.HasComponent()) - return; - - var transform = entity.GetComponent(); - - if(ImGui.TreeNodeEx("Transform", .DefaultOpen)) - { - Vector3 position = transform.Position; - if (Vector3Control("Position", ref position)) - transform.Position = position; - - Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler); - if (Vector3Control("Rotation", ref rotationEuler)) - transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); - - Vector3 scale = transform.Scale; - if (Vector3Control("Scale", ref scale, .One)) - transform.Scale = scale; - - ImGui.TreePop(); - } + Vector3 scale = transform.Scale; + if (ImGui.EditVector3("Scale", ref scale, .One)) + transform.Scale = scale; } static String[] strings = new String[]("Orthographic", "Perspective", "Perspective (Infinite)") ~ delete _; - private static void ShowCameraComponentEditor(Entity entity) + private static void ShowCameraComponentEditor(Entity entity, CameraComponent* cameraComponent) { - if (!entity.HasComponent()) - return; + ImGui.Checkbox("Primary", &cameraComponent.Primary); - if(ImGui.TreeNodeEx("Camera", .DefaultOpen)) + var camera = ref cameraComponent.Camera; + + String typeName = strings[camera.ProjectionType.Underlying]; + + if (ImGui.BeginCombo("Projection", typeName.CStr())) { - var cameraComponent = entity.GetComponent(); - - ImGui.Checkbox("Primary", &cameraComponent.Primary); - - var camera = ref cameraComponent.Camera; - - String typeName = strings[camera.ProjectionType.Underlying]; - - if (ImGui.BeginCombo("Projection", typeName.CStr())) + for (int i = 0; i < 3; i++) { - for (int i = 0; i < 3; i++) + bool isSelected = (typeName == strings[i]); + + if (ImGui.Selectable(strings[i], isSelected)) { - bool isSelected = (typeName == strings[i]); - - if (ImGui.Selectable(strings[i], isSelected)) - { - camera.ProjectionType = (.)i; - } - - if (isSelected) - ImGui.SetItemDefaultFocus(); + camera.ProjectionType = (.)i; } - ImGui.EndCombo(); + if (isSelected) + ImGui.SetItemDefaultFocus(); } - if (camera.ProjectionType == .Perspective) - { - float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); - if (ImGui.DragFloat("Fov Y", &fovY, 0.1f)) - camera.PerspectiveFovY = MathHelper.ToRadians(fovY); - - float near = camera.PerspectiveNearPlane; - if (ImGui.DragFloat("Near", &near, 0.1f)) - camera.PerspectiveNearPlane = near; + ImGui.EndCombo(); + } - float far = camera.PerspectiveFarPlane; - if (ImGui.DragFloat("Far", &far, 0.1f)) - camera.PerspectiveFarPlane = far; - } - else if (camera.ProjectionType == .InfinitePerspective) - { - float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); - if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f)) - camera.PerspectiveFovY = MathHelper.ToRadians(fovY); - - float near = camera.PerspectiveNearPlane; - if (ImGui.DragFloat("Near", &near, 0.1f)) - camera.PerspectiveNearPlane = near; - } - else if (camera.ProjectionType == .Orthographic) - { - float size = camera.OrthographicHeight; - if (ImGui.DragFloat("Size", &size, 0.1f)) - camera.OrthographicHeight = size; - - float near = camera.OrthographicNearPlane; - if (ImGui.DragFloat("Near", &near, 0.1f)) - camera.OrthographicNearPlane = near; + if (camera.ProjectionType == .Perspective) + { + float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); + if (ImGui.DragFloat("Fov Y", &fovY, 0.1f)) + camera.PerspectiveFovY = MathHelper.ToRadians(fovY); + + float near = camera.PerspectiveNearPlane; + if (ImGui.DragFloat("Near", &near, 0.1f)) + camera.PerspectiveNearPlane = near; - float far = camera.OrthographicFarPlane; - if (ImGui.DragFloat("Far", &far, 0.1f)) - camera.OrthographicFarPlane = far; - } + float far = camera.PerspectiveFarPlane; + if (ImGui.DragFloat("Far", &far, 0.1f)) + camera.PerspectiveFarPlane = far; + } + else if (camera.ProjectionType == .InfinitePerspective) + { + float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); + if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f)) + camera.PerspectiveFovY = MathHelper.ToRadians(fovY); + + float near = camera.PerspectiveNearPlane; + if (ImGui.DragFloat("Near", &near, 0.1f)) + camera.PerspectiveNearPlane = near; + } + else if (camera.ProjectionType == .Orthographic) + { + float size = camera.OrthographicHeight; + if (ImGui.DragFloat("Size", &size, 0.1f)) + camera.OrthographicHeight = size; + + float near = camera.OrthographicNearPlane; + if (ImGui.DragFloat("Near", &near, 0.1f)) + camera.OrthographicNearPlane = near; - bool fixedAspectRatio = camera.FixedAspectRatio; - if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio)) - camera.FixedAspectRatio = fixedAspectRatio; + float far = camera.OrthographicFarPlane; + if (ImGui.DragFloat("Far", &far, 0.1f)) + camera.OrthographicFarPlane = far; + } - if (fixedAspectRatio) - { - float aspect = camera.AspectRatio; - if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f)) - camera.AspectRatio = aspect; - } + bool fixedAspectRatio = camera.FixedAspectRatio; + if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio)) + camera.FixedAspectRatio = fixedAspectRatio; - ImGui.TreePop(); + if (fixedAspectRatio) + { + float aspect = camera.AspectRatio; + if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f)) + camera.AspectRatio = aspect; } } - private static void ShowSpriteRendererComponentEditor(Entity entity) + private static void ShowSpriteRendererComponentEditor(Entity entity, SpriterRendererComponent* spriteRendererComponent) { - if (!entity.HasComponent()) - return; + ImGui.ColorEdit4("Color", ref spriteRendererComponent.Color); + } + + private static void ShowAddComponentButton(Entity entity) + { + static char8[128] searchBuffer = .(); + static StringView searchFilter = .(); - var spriterRendererComponent = entity.GetComponent(); + static float buttonWidth = 100; - if(ImGui.TreeNodeEx("Sprite Renderer", .DefaultOpen)) + ImGui.NewLine(); + ImGui.Separator(); + ImGui.NewLine(); + + void ShowComponentButton(String name) where TComponent : struct, new { - ImGui.ColorEdit4("Color", ref spriterRendererComponent.Color); + float textWidth = ImGui.CalcTextSize(name.CStr()).x; + buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2); - ImGui.TreePop(); + if (name.Contains(searchFilter, true) && ImGui.Selectable(name)) + { + if (!entity.HasComponent()) + entity.AddComponent(); + } } + + float textWidth = ImGui.CalcTextSize("Add Component...").x; + buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2); + + ImGui.PushItemWidth(buttonWidth); + + ImGui.NewLine(); + ImGui.SameLine(ImGui.GetContentRegionMax().x / 2 - buttonWidth / 2); + + if (ImGui.BeginCombo("##add_component_combo", "Add Component...", .NoArrowButton)) + { + if (ImGui.InputText("##search_component_name", &searchBuffer, (.)searchBuffer.Count)) + { + searchFilter = .(&searchBuffer); + } + + ImGui.Separator(); + + ShowComponentButton("Camera"); + ShowComponentButton("Sprite Renderer"); + + ImGui.EndCombo(); + } + + ImGui.PopItemWidth(); } } } diff --git a/GlitchyEngine/src/Extension/System/String.bf b/GlitchyEngine/src/Extension/System/String.bf new file mode 100644 index 0000000..e21442d --- /dev/null +++ b/GlitchyEngine/src/Extension/System/String.bf @@ -0,0 +1,21 @@ +namespace System +{ + extension String + { + [Inline] + public void CopyTo(Span target) + { + CopyTo(target.Ptr, target.Length); + } + + [Inline] + public void CopyTo(char8* target, int targetLength) + { + int copiedChars = Math.Min(targetLength - 1, Length); + + Internal.MemCpy(target, Ptr, copiedChars); + + target[copiedChars] = '\0'; + } + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/ImGui/ImGuiExtension.bf b/GlitchyEngine/src/ImGui/ImGuiExtension.bf index a00593c..cafe8aa 100644 --- a/GlitchyEngine/src/ImGui/ImGuiExtension.bf +++ b/GlitchyEngine/src/ImGui/ImGuiExtension.bf @@ -6,8 +6,19 @@ namespace ImGui { extension ImGui { - // TODO: Color-functions + extension Vec2 + { + public static explicit operator Vector2(Vec2 v) => .(v.x, v.y); + public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y); + } + extension Vec4 + { + public static explicit operator Vector4(Vec4 v) => .(v.x, v.y, v.z, v.w); + public static explicit operator Vec4(Vector4 v) => .(v.X, v.Y, v.Z, v.W); + } + + // TODO: Color-functions public static bool ColorEdit3(char* label, ref ColorRGB col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags); public static bool ColorEdit3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags); @@ -29,16 +40,87 @@ namespace ImGui /// Releases references that accumulated calls like ImGui::Image protected internal static extern void CleanupFrame(); - extension Vec2 + /// Control to edit a vector 3 with drag functionality and reset buttons + public static bool EditVector3(StringView label, ref Vector3 value, Vector3 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f) { - public static explicit operator Vector2(Vec2 v) => .(v.x, v.y); - public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y); - } + bool changed = false; - extension Vec4 - { - public static explicit operator Vector4(Vec4 v) => .(v.x, v.y, v.z, v.w); - public static explicit operator Vec4(Vector4 v) => .(v.X, v.Y, v.Z, v.W); + PushID(label); + defer PopID(); + + Columns(2); + defer Columns(1); + SetColumnWidth(0, columnWidth); + + TextUnformatted(label); + + NextColumn(); + + PushMultiItemsWidths(3, CalcItemWidth()); + PushStyleVar(.ItemSpacing, Vec2.Zero); + defer PopStyleVar(); + + float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f; + ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight); + + PushStyleColor(.Button, Color(230, 25, 45).Value); + PushStyleColor(.ButtonHovered, Color(150, 25, 45).Value); + PushStyleColor(.ButtonActive, Color(230, 120, 130).Value); + + if (Button("X", buttonSize)) + { + value.X = resetValues.X; + changed = true; + } + + SameLine(); + + if (DragFloat("##X", &value.X, dragSpeed)) + changed = true; + + PopItemWidth(); + SameLine(); + + PopStyleColor(3); + PushStyleColor(.Button, Color(50, 190, 15).Value); + PushStyleColor(.ButtonHovered, Color(50, 120, 15).Value); + PushStyleColor(.ButtonActive, Color(116, 190, 99).Value); + + if (Button("Y", buttonSize)) + { + value.Y = resetValues.Y; + changed = true; + } + + SameLine(); + + if (DragFloat("##Y", &value.Y, dragSpeed)) + changed = true; + + PopItemWidth(); + SameLine(); + + PopStyleColor(3); + PushStyleColor(.Button, Color(55, 55, 230).Value); + PushStyleColor(.ButtonHovered, Color(55, 55, 150).Value); + PushStyleColor(.ButtonActive, Color(90, 90, 230).Value); + + if (Button("Z", buttonSize)) + { + value.Z = resetValues.Z; + changed = true; + } + + SameLine(); + + if (DragFloat("##Z", &value.Z, dragSpeed)) + changed = true; + + PopItemWidth(); + + PopStyleColor(3); + + return changed; } } } diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index 2748e67..28e6b6b 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -4,6 +4,17 @@ using System; namespace GlitchyEngine.World { + [AttributeUsage(.Struct, .ReflectAttribute, ReflectUser=.Methods | .NonStaticFields)] + struct ComponentAttribute : Attribute + { + public String Name; + + public this(String name) + { + Name = name; + } + } + /// If an entity has the EditorComponent it won't be displayed in the scene hierarchy. struct EditorComponent { @@ -13,7 +24,8 @@ namespace GlitchyEngine.World } } - + + [Component("Sprite Renderer")] struct SpriterRendererComponent : IDisposableComponent { public Texture2D Sprite = null;