Add / Remove Components

This commit is contained in:
Simon Lübeß
2022-04-10 12:18:43 +02:00
parent af37154071
commit bca72cf0d4
4 changed files with 302 additions and 192 deletions
@@ -2,6 +2,7 @@ using ImGui;
using GlitchyEngine.World; using GlitchyEngine.World;
using System; using System;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using System.Collections;
namespace GlitchyEditor.EditWindows namespace GlitchyEditor.EditWindows
{ {
@@ -13,9 +14,19 @@ namespace GlitchyEditor.EditWindows
private EntityHierarchyWindow _entityHierarchyWindow; private EntityHierarchyWindow _entityHierarchyWindow;
private List<(Type, ComponentAttribute attribute)> _componentTypes = new .() ~ delete _;
public this(EntityHierarchyWindow entityHierarchyWindow) public this(EntityHierarchyWindow entityHierarchyWindow)
{ {
_entityHierarchyWindow = entityHierarchyWindow; _entityHierarchyWindow = entityHierarchyWindow;
for (let type in Type.Types)
{
if (let componentAttribute = type.GetCustomAttribute<ComponentAttribute>())
{
_componentTypes.Add((type, componentAttribute));
}
}
} }
protected override void InternalShow() protected override void InternalShow()
@@ -39,9 +50,56 @@ namespace GlitchyEditor.EditWindows
private void ShowComponents(Entity entity) private void ShowComponents(Entity entity)
{ {
ShowNameComponentEditor(entity); ShowNameComponentEditor(entity);
ShowTransformComponentEditor(entity);
ShowCameraComponentEditor(entity); ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor);
ShowSpriteRendererComponentEditor(entity); ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>);
ShowComponentEditor<SpriterRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriterRendererComponent>);
ShowAddComponentButton(entity);
}
private static void ShowComponentContextMenu<TComponent>(Entity entity, TComponent* component) where TComponent: struct, new
{
if (ImGui.Selectable("Remove Component"))
entity.RemoveComponent<TComponent>();
}
private static void ShowComponentEditor<TComponent>(String header, Entity entity, function void(Entity, TComponent*) showComponentEditor, function void(Entity, TComponent*) showComponentContextMenu = null) where TComponent: struct, new
{
if (!entity.HasComponent<TComponent>())
return;
TComponent* component = entity.GetComponent<TComponent>();
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) 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); Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler);
defer ImGui.PopID(); if (ImGui.EditVector3("Rotation", ref rotationEuler))
transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler);
ImGui.Columns(2); Vector3 scale = transform.Scale;
defer ImGui.Columns(1); if (ImGui.EditVector3("Scale", ref scale, .One))
ImGui.SetColumnWidth(0, columnWidth); transform.Scale = scale;
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;
}
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<TransformComponent>())
return;
var transform = entity.GetComponent<TransformComponent>();
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();
}
} }
static String[] strings = new String[]("Orthographic", "Perspective", "Perspective (Infinite)") ~ delete _; 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<CameraComponent>()) ImGui.Checkbox("Primary", &cameraComponent.Primary);
return;
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<CameraComponent>(); for (int i = 0; i < 3; i++)
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++) bool isSelected = (typeName == strings[i]);
if (ImGui.Selectable(strings[i], isSelected))
{ {
bool isSelected = (typeName == strings[i]); camera.ProjectionType = (.)i;
if (ImGui.Selectable(strings[i], isSelected))
{
camera.ProjectionType = (.)i;
}
if (isSelected)
ImGui.SetItemDefaultFocus();
} }
ImGui.EndCombo(); if (isSelected)
ImGui.SetItemDefaultFocus();
} }
if (camera.ProjectionType == .Perspective) ImGui.EndCombo();
{ }
float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
if (ImGui.DragFloat("Fov Y", &fovY, 0.1f))
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
float near = camera.PerspectiveNearPlane; if (camera.ProjectionType == .Perspective)
if (ImGui.DragFloat("Near", &near, 0.1f)) {
camera.PerspectiveNearPlane = near; float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
if (ImGui.DragFloat("Fov Y", &fovY, 0.1f))
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
float far = camera.PerspectiveFarPlane; float near = camera.PerspectiveNearPlane;
if (ImGui.DragFloat("Far", &far, 0.1f)) if (ImGui.DragFloat("Near", &near, 0.1f))
camera.PerspectiveFarPlane = far; camera.PerspectiveNearPlane = near;
}
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; float far = camera.PerspectiveFarPlane;
if (ImGui.DragFloat("Near", &near, 0.1f)) if (ImGui.DragFloat("Far", &far, 0.1f))
camera.PerspectiveNearPlane = near; camera.PerspectiveFarPlane = far;
} }
else if (camera.ProjectionType == .Orthographic) else if (camera.ProjectionType == .InfinitePerspective)
{ {
float size = camera.OrthographicHeight; float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
if (ImGui.DragFloat("Size", &size, 0.1f)) if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f))
camera.OrthographicHeight = size; camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
float near = camera.OrthographicNearPlane; float near = camera.PerspectiveNearPlane;
if (ImGui.DragFloat("Near", &near, 0.1f)) if (ImGui.DragFloat("Near", &near, 0.1f))
camera.OrthographicNearPlane = near; camera.PerspectiveNearPlane = near;
}
else if (camera.ProjectionType == .Orthographic)
{
float size = camera.OrthographicHeight;
if (ImGui.DragFloat("Size", &size, 0.1f))
camera.OrthographicHeight = size;
float far = camera.OrthographicFarPlane; float near = camera.OrthographicNearPlane;
if (ImGui.DragFloat("Far", &far, 0.1f)) if (ImGui.DragFloat("Near", &near, 0.1f))
camera.OrthographicFarPlane = far; camera.OrthographicNearPlane = near;
}
bool fixedAspectRatio = camera.FixedAspectRatio; float far = camera.OrthographicFarPlane;
if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio)) if (ImGui.DragFloat("Far", &far, 0.1f))
camera.FixedAspectRatio = fixedAspectRatio; camera.OrthographicFarPlane = far;
}
if (fixedAspectRatio) bool fixedAspectRatio = camera.FixedAspectRatio;
{ if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio))
float aspect = camera.AspectRatio; camera.FixedAspectRatio = fixedAspectRatio;
if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f))
camera.AspectRatio = aspect;
}
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<SpriterRendererComponent>()) ImGui.ColorEdit4("Color", ref spriteRendererComponent.Color);
return; }
var spriterRendererComponent = entity.GetComponent<SpriterRendererComponent>(); private static void ShowAddComponentButton(Entity entity)
{
static char8[128] searchBuffer = .();
static StringView searchFilter = .();
if(ImGui.TreeNodeEx("Sprite Renderer", .DefaultOpen)) static float buttonWidth = 100;
ImGui.NewLine();
ImGui.Separator();
ImGui.NewLine();
void ShowComponentButton<TComponent>(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<TComponent>())
entity.AddComponent<TComponent>();
}
} }
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<CameraComponent>("Camera");
ShowComponentButton<SpriterRendererComponent>("Sprite Renderer");
ImGui.EndCombo();
}
ImGui.PopItemWidth();
} }
} }
} }
@@ -0,0 +1,21 @@
namespace System
{
extension String
{
[Inline]
public void CopyTo(Span<char8> 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';
}
}
}
+91 -9
View File
@@ -6,8 +6,19 @@ namespace ImGui
{ {
extension 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 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); 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 /// Releases references that accumulated calls like ImGui::Image
protected internal static extern void CleanupFrame(); 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); bool changed = false;
public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y);
}
extension Vec4 PushID(label);
{ defer PopID();
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); 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;
} }
} }
} }
+12
View File
@@ -4,6 +4,17 @@ using System;
namespace GlitchyEngine.World 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. /// If an entity has the EditorComponent it won't be displayed in the scene hierarchy.
struct EditorComponent struct EditorComponent
{ {
@@ -14,6 +25,7 @@ namespace GlitchyEngine.World
} }
} }
[Component("Sprite Renderer")]
struct SpriterRendererComponent : IDisposableComponent struct SpriterRendererComponent : IDisposableComponent
{ {
public Texture2D Sprite = null; public Texture2D Sprite = null;