From 37197cbdf1c5d4d0777c70de9af11c45fbb33745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 24 Jul 2023 23:16:44 +0200 Subject: [PATCH] Allow dragging and dropping of Entities in editor + New Dictionary Helpers + Made ImGui Payloads safer + Start of Component drag'n'drop --- .../Assets/Scripts/MyTestEntity.cs | 50 ++-- GlitchyEditor/content/Scenes/physics2D.scene | 69 ++--- .../resources/scripts/ScriptCore.deps.json | 16 ++ .../src/Assets/MaterialAssetLoader.bf | 4 +- .../src/EditWindows/ComponentEditWindow.bf | 260 +++++++++++++----- .../src/EditWindows/ContentBrowserWindow.bf | 4 +- .../src/EditWindows/EditorViewportWindow.bf | 2 +- .../src/EditWindows/EntityHierarchyWindow.bf | 26 +- .../src/EditWindows/GameViewportWindow.bf | 2 +- GlitchyEditor/src/Editor.bf | 7 + GlitchyEditor/src/ImGuiExtension.bf | 33 +++ GlitchyEngine/src/Helper.bf | 20 ++ GlitchyEngine/src/Scripting/ScriptClass.bf | 58 +++- GlitchyEngine/src/Scripting/ScriptEngine.bf | 107 ++++--- .../src/Scripting/ScriptFieldType.bf | 3 + GlitchyEngine/src/Scripting/ScriptInstance.bf | 4 +- GlitchyEngine/src/World/Scene.bf | 9 + GlitchyEngineHelper/src/Mono/Mono.bf | 3 + ScriptCore/Editor/ShowInEditorAttribute.cs | 6 + 19 files changed, 500 insertions(+), 183 deletions(-) create mode 100644 GlitchyEditor/src/ImGuiExtension.bf diff --git a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs index c9a7284..f74c915 100644 --- a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs +++ b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs @@ -25,36 +25,36 @@ namespace Sandbox class MyTestEntity : Entity { - //[ShowInEditor] + [ShowInEditor] RigidBody2D _rigidBody; - public bool Bo; + //public bool Bo; - public byte By; - public ushort Us; - public uint Ui; - public ulong Ul; + //public byte By; + //public ushort Us; + //public uint Ui; + //public ulong Ul; - public sbyte Sb; - public short Sh; - public int In; - public long Lo; + //public sbyte Sb; + //public short Sh; + //public int In; + //public long Lo; - public float Fl; - public double Do; - public float2 V2; - public float3 V3; - public float4 V4; + //public float Fl; + //public double Do; + //public float2 V2; + //public float3 V3; + //public float4 V4; public Entity TheEntity; public float JumpForce = 2000; [ShowInEditor] float MoveForce = 1000; [ShowInEditor] private int MyNumber = 1337; - [ShowInEditor] public double MyDouble = 1000.0f; - - public MyStruct AStruct; + //[ShowInEditor] public double MyDouble = 1000.0f; + //public MyStruct AStruct; + public Camera Camera; /// @@ -67,12 +67,16 @@ namespace Sandbox Log.Info($"Jump Force: {JumpForce}"); _rigidBody ??= GetComponent() ?? AddComponent(); - - Camera = FindEntityWithName("Camera").As(); - + if (Camera == null) { - Log.Error("Camera not found."); + Log.Warning("Camera wasn't set in editor. Searching..."); + Camera = FindEntityWithName("Camera").As(); + + if (Camera == null) + { + Log.Error("Camera not found."); + } } } @@ -99,7 +103,7 @@ namespace Sandbox if (Input.IsKeyPressed(Key.E)) Camera.DistanceFromPlayer += deltaTime; - + if (Input.IsKeyPressing(Key.Space)) { force.Y += JumpForce; diff --git a/GlitchyEditor/content/Scenes/physics2D.scene b/GlitchyEditor/content/Scenes/physics2D.scene index 2949867..a8c3087 100644 --- a/GlitchyEditor/content/Scenes/physics2D.scene +++ b/GlitchyEditor/content/Scenes/physics2D.scene @@ -71,8 +71,8 @@ SpriteRendererComponent = { Color = { R = 1, - G = 0.941886961, - B = 0.401484549, + G = 0.941887021, + B = 0.401484519, A = 1 }, Sprite = "", @@ -162,7 +162,7 @@ OrthographicHeight = 10, OrthographicNearPlane = 0, OrthographicFarPlane = 10, - AspectRatio = 0.884925187, + AspectRatio = 1.17151165, FixedAspectRatio = false }, ScriptComponent = { @@ -195,8 +195,8 @@ }, TransformComponent = { Position = { - X = -0.121203184, - Y = 0.660160959, + X = -0.121203199, + Y = 0.660161018, Z = 0 }, Rotation = { @@ -234,37 +234,12 @@ ScriptComponent = { ScriptClass = "Sandbox.MyTestEntity", Fields = [ - Bo = (Bool)false, - By = (Byte)0, - Us = (UShort)57, - Ui = (UInt)54, - Ul = (ULong)53, - Sb = (SByte)-128, - Sh = (Short)95, - In = (Int)-149, - Lo = (Long)82, - Fl = (Float)-98.122963, - Do = (Double)118, - V2 = (float2){ - X = 11, - Y = 12 - }, - V3 = (float3){ - X = 13, - Y = 14, - Z = 15 - }, - V4 = (float4){ - X = 14, - Y = 17, - Z = 18, - W = 19 - }, + _rigidBody = (Component)0, TheEntity = (Entity)0, JumpForce = (Float)200, MoveForce = (Float)148, MyNumber = (Int)22, - MyDouble = (Double)23 + Camera = (Entity)1491484622542812645 ] } }, { @@ -301,7 +276,7 @@ Color = { R = 0.985467017, G = 1, - B = 0.569978058 + B = 0.569977999 } } }, @@ -348,8 +323,8 @@ SpriteRendererComponent = { Color = { R = 0.425658971, - G = 0.855207562, - B = 0.0558161102, + G = 0.855207503, + B = 0.0558161139, A = 1 }, Sprite = "", @@ -363,14 +338,14 @@ TransformComponent = { Position = { X = 6.74610233, - Y = -2.03225899, + Y = -2.03225923, Z = 0 }, Rotation = { X = 0, Y = 0, Z = -0.331792623, - W = 0.943352342 + W = 0.943352461 }, Scale = { X = 5.29557419, @@ -409,7 +384,7 @@ }, SpriteRendererComponent = { Color = { - R = 0.666116953, + R = 0.666117013, G = 0.0600316115, B = 1, A = 1 @@ -424,8 +399,8 @@ }, TransformComponent = { Position = { - X = 15.2321301, - Y = -1.98709249, + X = 15.2321291, + Y = -1.98709261, Z = 0 }, Rotation = { @@ -435,7 +410,7 @@ W = 0.990847468 }, Scale = { - X = 13.9764566, + X = 13.9764547, Y = 1, Z = 1 }, @@ -471,7 +446,7 @@ }, SpriteRendererComponent = { Color = { - R = 0.176544324, + R = 0.176544309, G = 0.666713238, B = 0.687030852, A = 1 @@ -487,24 +462,24 @@ TransformComponent = { Position = { X = -6.27842855, - Y = 2.62550211, + Y = 2.62550187, Z = 0 }, Rotation = { X = 0, Y = 0, - Z = 0.187087834, + Z = 0.187087849, W = 0.982343197 }, Scale = { - X = 0.999999523, + X = 0.999999464, Y = 6.49634314, Z = 1 }, EditorEulerRotation = { X = 0, Y = 0, - Z = 0.376393586 + Z = 0.376393616 } }, Rigidbody2D = { @@ -522,7 +497,7 @@ }, Density = 1, Friction = 0.5, - Restitution = 0.899999976, + Restitution = 0.899999917, RestitutionThreshold = 0.5 } } diff --git a/GlitchyEditor/resources/scripts/ScriptCore.deps.json b/GlitchyEditor/resources/scripts/ScriptCore.deps.json index 8dbf87a..84213a6 100644 --- a/GlitchyEditor/resources/scripts/ScriptCore.deps.json +++ b/GlitchyEditor/resources/scripts/ScriptCore.deps.json @@ -9,12 +9,21 @@ ".NETStandard,Version=v2.0/": { "ScriptCore/1.0.0": { "dependencies": { + "Half": "1.0.0", "NETStandard.Library": "2.0.3" }, "runtime": { "ScriptCore.dll": {} } }, + "Half/1.0.0": { + "runtime": { + "lib/netstandard2.0/System.Half.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + }, "Microsoft.NETCore.Platforms/1.1.0": {}, "NETStandard.Library/2.0.3": { "dependencies": { @@ -29,6 +38,13 @@ "serviceable": false, "sha512": "" }, + "Half/1.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-EPLLPQA1UjF9QM3ymNhWLhgcnBNc8e1po4qWRWTD3Hd9xiohYlkCob0QjDIbJs6Nvfd4eAbsjxIwRyh7httyXA==", + "path": "half/1.0.0", + "hashPath": "half.1.0.0.nupkg.sha512" + }, "Microsoft.NETCore.Platforms/1.1.0": { "type": "package", "serviceable": true, diff --git a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf index 5970890..31583bc 100644 --- a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf +++ b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf @@ -33,7 +33,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { @@ -78,7 +78,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index a74885f..da751e6 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -7,6 +7,8 @@ using GlitchyEngine.Renderer; using GlitchyEngine; using GlitchyEngine.Content; using GlitchyEngine.Scripting; +using GlitchyEngine.Core; +using Mono; namespace GlitchyEditor.EditWindows { @@ -278,7 +280,7 @@ namespace GlitchyEditor.EditWindows if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { @@ -306,7 +308,7 @@ namespace GlitchyEditor.EditWindows if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { @@ -338,7 +340,7 @@ namespace GlitchyEditor.EditWindows if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { @@ -601,94 +603,94 @@ namespace GlitchyEditor.EditWindows { let scriptFields = ScriptEngine.GetScriptFieldMap(entity); - for (var (name, field) in ref scriptFields) + for (var (fieldName, field) in ref scriptFields) { switch (field.Type) { case .Bool: var value = field.GetData(); - if (ImGui.Checkbox(name.CStr(), &value)) + if (ImGui.Checkbox(fieldName.CStr(), &value)) field.SetData(value); case .SByte: var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .S8, &value)) + if (ImGui.DragScalar(fieldName.CStr(), .S8, &value)) + field.SetData(value); + case .Short: + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .S16, &value)) field.SetData(value); - case .Short: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .S16, &value)) - field.SetData(value); case .Int: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .S32, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .S32, &value)) + field.SetData(value); case .Int2: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .S32, &value, 2)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 2)) + field.SetData(value); case .Int3: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .S32, &value, 3)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 3)) + field.SetData(value); case .Int4: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .S32, &value, 4)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 4)) + field.SetData(value); case .Long: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .S64, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .S64, &value)) + field.SetData(value); case .Byte: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .U8, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .U8, &value)) + field.SetData(value); case .UShort: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .U16, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .U16, &value)) + field.SetData(value); case .UInt: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .U32, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .U32, &value)) + field.SetData(value); case .ULong: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .U64, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .U64, &value)) + field.SetData(value); case .Float: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .Float, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .Float, &value)) + field.SetData(value); case .float2: - var value = field.GetData(); - if (ImGui.Editfloat2(name, ref value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.Editfloat2(fieldName, ref value)) + field.SetData(value); case .float3: - var value = field.GetData(); - if (ImGui.Editfloat3(name, ref value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.Editfloat3(fieldName, ref value)) + field.SetData(value); case .float4: - var value = field.GetData(); - if (ImGui.Editfloat4(name, ref value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.Editfloat4(fieldName, ref value)) + field.SetData(value); case .Double: - var value = field.GetData(); - if (ImGui.DragScalar(name.CStr(), .Double, &value)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalar(fieldName.CStr(), .Double, &value)) + field.SetData(value); case .Double2: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .Double, &value, 2)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 2)) + field.SetData(value); case .Double3: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .Double, &value, 3)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 3)) + field.SetData(value); case .Double4: - var value = field.GetData(); - if (ImGui.DragScalarN(name.CStr(), .Double, &value, 4)) - field.SetData(value); + var value = field.GetData(); + if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 4)) + field.SetData(value); case .Enum: // TODO! @@ -697,11 +699,16 @@ namespace GlitchyEditor.EditWindows // TODO! case .Entity: - // TODO! + ShowEntityReceiver(field, fieldName, scriptClass); + case .Component: + ShowComponentReceiver(field, fieldName, scriptClass); case .Struct: // TODO! + case .Class: + // We don't support editing classes + default: Log.EngineLogger.Error($"Unhandled field type {field.Type}"); } @@ -709,6 +716,133 @@ namespace GlitchyEditor.EditWindows } } + private static void ShowEntityReceiver(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass) + { + var entityId = field.GetData(); + + Result fieldEntity = Editor.Instance.CurrentScene.GetEntityByID(entityId); + + String entityName = scope .(32); + + if (fieldEntity case .Ok(Entity e)) + entityName.Set(e.Name); + else + entityName..Clear().AppendF($"Missing entity ({entityId})"); + + ImGui.Text($"{fieldName}: "); + ImGui.SameLine(); + + if (ImGui.Button(entityName)) + { + // TODO: Show a selector or something + } + + if (ImGui.BeginDragDropTarget()) + { + ImGui.Payload* peekPayload = ImGui.AcceptDragDropPayload(.Entity, .AcceptPeekOnly); + + bool allowDrop = false; + + if (peekPayload != null) + { + Entity draggedEntity = *(Entity*)peekPayload.Data; + + ScriptClass draggedScriptClass = ScriptEngine.[Friend]s_EntityRoot; + + if (draggedEntity.TryGetComponent(let draggedScript)) + { + var draggedClassName = draggedScript.ScriptClassName; + draggedScriptClass = ScriptEngine.GetScriptClass(draggedClassName); + + } + + // TODO: I don't like the fact, that we are using mono directly + + ScriptField scriptField = scriptClass.Fields[fieldName]; + var fieldMonoClass = Mono.mono_type_get_class(scriptField.GetMonoType()); + + allowDrop = draggedScriptClass.[Friend]IsSubclass(fieldMonoClass); + } + + if (allowDrop) + { + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); + + if (payload != null) + { + Log.EngineLogger.AssertDebug(payload.DataSize == sizeof(Entity)); + + Entity droppedEntity = *(Entity*)payload.Data; + + if (droppedEntity.IsValid) + field.SetData(droppedEntity.UUID); + } + } + + ImGui.EndDragDropTarget(); + } + } + + private static void ShowComponentReceiver(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass) + { + var entityId = field.GetData(); + + Result fieldEntity = Editor.Instance.CurrentScene.GetEntityByID(entityId); + + String entityName = scope .(32); + + if (fieldEntity case .Ok(Entity e)) + entityName.Set(e.Name); + else + entityName..Clear().AppendF($"Missing reference ({entityId})"); + + ImGui.Text($"{fieldName}: "); + ImGui.SameLine(); + + if (ImGui.Button(entityName)) + { + // TODO: Show a selector or something + } + + if (ImGui.BeginDragDropTarget()) + { + ImGui.Payload* peekPayload = ImGui.AcceptDragDropPayload(.Entity, .AcceptPeekOnly); + + bool allowDrop = false; + + if (peekPayload != null) + { + Entity draggedEntity = *(Entity*)peekPayload.Data; + + // TODO: I don't like the fact, that we are using mono directly + + ScriptField scriptField = scriptClass.Fields[fieldName]; + MonoReflectionType* reflectionType = Mono.mono_type_get_object(ScriptEngine.[Friend]s_AppDomain, scriptField.GetMonoType()); + + // TODO: We shouldn't abuse the script glue like that... + // ScriptGlue should only be called by C#, not by Beef... + allowDrop = ScriptGlue.[Friend]Entity_HasComponent(draggedEntity.UUID, reflectionType); + } + + if (allowDrop) + { + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); + + if (payload != null) + { + Log.EngineLogger.AssertDebug(payload.DataSize == sizeof(Entity)); + + Entity droppedEntity = *(Entity*)payload.Data; + + if (droppedEntity.IsValid) + field.SetData(droppedEntity.UUID); + } + } + + ImGui.EndDragDropTarget(); + } + } + private static void LabelColumn(StringView label) { ImGui.TextUnformatted(label); @@ -773,7 +907,7 @@ namespace GlitchyEditor.EditWindows if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { diff --git a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf index cedbd30..75c007d 100644 --- a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf +++ b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf @@ -324,7 +324,7 @@ namespace GlitchyEditor.EditWindows Path.Fixup(fullpath); - ImGui.SetDragDropPayload("CONTENT_BROWSER_ITEM", fullpath.CStr(), (.)fullpath.Length, .Once); + ImGui.SetDragDropPayload(.ContentBrowserItem, fullpath.CStr(), (.)fullpath.Length, .Once); ImGui.EndDragDropSource(); } @@ -370,7 +370,7 @@ namespace GlitchyEditor.EditWindows String fullpath = scope String(entry->Path); fullpath.AppendF($"#{subAsset.Name}"); - ImGui.SetDragDropPayload("CONTENT_BROWSER_ITEM", fullpath.CStr(), (.)fullpath.Length, .Once); + ImGui.SetDragDropPayload(.ContentBrowserItem, fullpath.CStr(), (.)fullpath.Length, .Once); ImGui.EndDragDropSource(); } diff --git a/GlitchyEditor/src/EditWindows/EditorViewportWindow.bf b/GlitchyEditor/src/EditWindows/EditorViewportWindow.bf index dc803b2..f1abfde 100644 --- a/GlitchyEditor/src/EditWindows/EditorViewportWindow.bf +++ b/GlitchyEditor/src/EditWindows/EditorViewportWindow.bf @@ -137,7 +137,7 @@ namespace GlitchyEditor.EditWindows { if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { diff --git a/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf b/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf index b969fd6..9cad41a 100644 --- a/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf +++ b/GlitchyEditor/src/EditWindows/EntityHierarchyWindow.bf @@ -341,9 +341,13 @@ namespace GlitchyEditor.EditWindows if (deleted) return; + bool isDragged = false; + if(ImGui.BeginDragDropSource()) { - ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity)); + isDragged = true; + + ImGui.SetDragDropPayload(.Entity, &tree.Value, sizeof(Entity)); ImGui.Text(name); @@ -352,7 +356,7 @@ namespace GlitchyEditor.EditWindows if(ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); if(payload != null) { @@ -395,7 +399,7 @@ namespace GlitchyEditor.EditWindows bool clicked = ImGui.IsItemClicked(.Left); bool clickedRight = ImGui.IsItemClicked(.Right); - + if(isOpen) { for(var child in tree.Children) @@ -407,6 +411,12 @@ namespace GlitchyEditor.EditWindows } if (clicked || clickedRight) + { + lastClickedEntity = tree.Value; + } + + //if (!isDragged && (clicked || clickedRight)) + if (!isDragged && (!ImGui.IsMouseDown(.Left) && !ImGui.IsMouseDown(.Right) && ImGui.IsItemHovered()) && tree.Value == lastClickedEntity) { if (inSelectedList && !clickedRight) { @@ -418,9 +428,13 @@ namespace GlitchyEditor.EditWindows SelectEntity(tree.Value, !ImGui.GetIO().KeyCtrl && !clickedRight); inSelectedList = true; } + + lastClickedEntity = .(); } } + private static Entity lastClickedEntity; + private void ShowEntityHierarchy() { StringView searchString = StringView(&_entitySearchChars); @@ -464,7 +478,7 @@ namespace GlitchyEditor.EditWindows { if(ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity); if(payload != null) { @@ -527,6 +541,10 @@ namespace GlitchyEditor.EditWindows ImGuiPrintEntityTree(scope .(entity)); } } + + // If the mouse was released and no entity took the chance to become selected we probably hovered the background while releasing -> select no entity + if (!ImGui.IsMouseDown(.Left) && !ImGui.IsMouseDown(.Right)) + lastClickedEntity = .(); } } } diff --git a/GlitchyEditor/src/EditWindows/GameViewportWindow.bf b/GlitchyEditor/src/EditWindows/GameViewportWindow.bf index 0fbe2de..242f264 100644 --- a/GlitchyEditor/src/EditWindows/GameViewportWindow.bf +++ b/GlitchyEditor/src/EditWindows/GameViewportWindow.bf @@ -91,7 +91,7 @@ namespace GlitchyEditor.EditWindows { if (ImGui.BeginDragDropTarget()) { - ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); + ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem); if (payload != null) { diff --git a/GlitchyEditor/src/Editor.bf b/GlitchyEditor/src/Editor.bf index 50b31e7..75224d8 100644 --- a/GlitchyEditor/src/Editor.bf +++ b/GlitchyEditor/src/Editor.bf @@ -53,9 +53,16 @@ namespace GlitchyEditor public SceneRenderer GameSceneRenderer {get; set;} public SceneRenderer EditorSceneRenderer {get; set;} + private static Editor s_Instance; + + public static Editor Instance => s_Instance; + /// Creates a new editor for the given world public this(Scene scene, EditorContentManager contentManager) { + Log.EngineLogger.AssertDebug(s_Instance == null, "Cannot create a second instance of a singleton."); + s_Instance = this; + _scene = scene; _contentManager = contentManager; diff --git a/GlitchyEditor/src/ImGuiExtension.bf b/GlitchyEditor/src/ImGuiExtension.bf new file mode 100644 index 0000000..1114185 --- /dev/null +++ b/GlitchyEditor/src/ImGuiExtension.bf @@ -0,0 +1,33 @@ +using System; + +namespace ImGui; + +enum DragDropPayloadType +{ + case ContentBrowserItem; + case Entity; + + public String GetName() + { + switch (this) + { + case .ContentBrowserItem: + return "CONTENT_BROWSER_ITEM"; + case .Entity: + return "ENTITY"; + } + } +} + +extension ImGui +{ + public static Payload* AcceptDragDropPayload(DragDropPayloadType type, DragDropFlags flags = .None) + { + return AcceptDragDropPayload(type.GetName(), flags); + } + + public static bool SetDragDropPayload(DragDropPayloadType type, void* data, size sz, Cond cond = .None) + { + return SetDragDropPayload(type.GetName(), data, sz, cond); + } +} diff --git a/GlitchyEngine/src/Helper.bf b/GlitchyEngine/src/Helper.bf index 6f7fcd8..1248450 100644 --- a/GlitchyEngine/src/Helper.bf +++ b/GlitchyEngine/src/Helper.bf @@ -70,5 +70,25 @@ namespace GlitchyEngine delete dictionary; } } + + public static mixin ClearDictionaryAndReleaseKeys(var dictionary) + { + if (dictionary != null) + { + for (var value in dictionary) + value.key?.ReleaseRef(); + dictionary.Clear(); + } + } + + public static mixin ClearDictionaryAndReleaseValues(var dictionary) + { + if (dictionary != null) + { + for (var value in dictionary) + value.value?.ReleaseRef(); + dictionary.Clear(); + } + } } } diff --git a/GlitchyEngine/src/Scripting/ScriptClass.bf b/GlitchyEngine/src/Scripting/ScriptClass.bf index 2e16085..7d88541 100644 --- a/GlitchyEngine/src/Scripting/ScriptClass.bf +++ b/GlitchyEngine/src/Scripting/ScriptClass.bf @@ -21,6 +21,33 @@ public struct ScriptField IsStatic = isStatic; FieldType = fieldType; } + + public bool IsType(SharpClass otherClass, bool checkIfSubtype) + { + return IsType(otherClass.GetMonoType(), checkIfSubtype); + } + + public bool IsType(MonoType* otherType, bool checkIfSubtype) + { + MonoType* myType = GetMonoType(); + + if (checkIfSubtype) + { + MonoClass* myClass = Mono.mono_type_get_class(myType); + MonoClass* otherClass = Mono.mono_type_get_class(otherType); + + return Mono.mono_class_is_subclass_of(myClass, otherClass, false); + } + else + { + return myType == otherType; + } + } + + public MonoType* GetMonoType() + { + return Mono.mono_field_get_type(_monoField); + } } public struct ScriptFieldInstance @@ -90,9 +117,19 @@ class SharpClass : SharpType ExtractFields(); } + internal MonoType* GetMonoType() + { + return Mono.mono_class_get_type(_monoClass); + } + internal bool IsType(MonoType* type) { - return Mono.mono_class_get_type(_monoClass) == type; + return GetMonoType() == type; + } + + internal bool IsSubclass(MonoClass* @class) + { + return Mono.mono_class_is_subclass_of(_monoClass, @class, false); } private void ExtractFields() @@ -180,8 +217,8 @@ class ScriptClass : SharpClass public Dictionary Fields => _monoFields;*/ [AllowAppend] - public this(StringView classNamespace, StringView className, MonoImage* image) : - base(classNamespace, className, image) + public this(StringView classNamespace, StringView className, MonoImage* image, ScriptFieldType scriptFieldType = .Class) : + base(classNamespace, className, image, scriptFieldType) { //_constructor = (ConstructorMethod)GetMethodThunk(".ctor", 1); // GetMethod(".ctor", 1);// _constructor = GetMethod(".ctor", 1); @@ -288,6 +325,19 @@ class ScriptClass : SharpClass public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) { - Mono.Mono.mono_field_set_value(instance, field, &value); + /*if (typeof(T) == typeof(MonoObject*)) + { + // TODO: MonoObject* is a pointer already, so we don't take the pointer + Mono.Mono.mono_field_set_value(instance, field, (void*)value); + } + else + {*/ + Mono.Mono.mono_field_set_value(instance, field, &value); + //} + } + + public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) where T : struct* + { + Mono.Mono.mono_field_set_value(instance, field, value); } } diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index bee290d..fd03d7a 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -58,12 +58,14 @@ static class ScriptEngine private static Scene s_Context ~ _?.ReleaseRef(); + private static ScriptClass s_ComponentRoot ~ _?.ReleaseRef(); private static ScriptClass s_EntityRoot ~ _?.ReleaseRef(); private static ScriptClass s_EngineObject ~ _?.ReleaseRef(); private static Dictionary _sharpClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_); private static Dictionary _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_); + private static Dictionary _componentClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_); private static Dictionary _entityScriptInstances = new .() ~ { for (var entry in _) @@ -74,6 +76,7 @@ static class ScriptEngine } public static Dictionary EntityClasses => _entityScripts; + public static Dictionary ComponentClasses => _componentClasses; public static Scene Context => s_Context; @@ -170,7 +173,12 @@ static class ScriptEngine CreateAppDomain("GlitchyEngineScriptRuntime"); (s_CoreAssembly, s_CoreAssemblyImage) = LoadAssembly("resources/scripts/ScriptCore.dll", _debuggingEnabled); (s_AppAssembly, s_AppAssemblyImage) = LoadAssembly("SandboxProject/Assets/Scripts/bin/Sandbox.dll", _debuggingEnabled); - + + ClearDictionaryAndReleaseValues!(_sharpClasses); + + GetCoreAttributes(); + GetCoreClasses(); + GetEntitiesFromAssemblies(); ScriptGlue.RegisterManagedComponents(); @@ -206,12 +214,12 @@ static class ScriptEngine script.Instance.Instantiate(entity.UUID); - CopyEditorFieldsToInstance(entity, script); + //CopyEditorFieldsToInstance(entity, script); return true; } - private static void CopyEditorFieldsToInstance(Entity entity, ScriptComponent* script) + public static void CopyEditorFieldsToInstance(Entity entity, ScriptComponent* script) { // Technically the map is for a different entity (namely the editor-entity), // however the UUID is the same, so we get the correct field map @@ -220,10 +228,24 @@ static class ScriptEngine for (var (fieldName, field) in fields) { // TODO: a litte assertion maybe? - + ScriptField scriptField = script.Instance.ScriptClass.Fields[fieldName]; - script.Instance.SetFieldValue(scriptField, field._data); + switch (scriptField.FieldType) + { + case .Entity: + // On the C# side we actually differentiate between an Entity and the Script + // in the sense that getting an entity and a script yields two different results (one creates a new Entity-Class instance, the other returns the actual instance). + // But here its just easier to always use the script instance. + // Obviously breaks once we support multiple scripts per entity. + UUID referencedId = field.GetData(); + MonoObject* referencedEntity = GetManagedInstance(referencedId); + script.Instance.SetFieldValue(scriptField, referencedEntity); + case .Component: + + default: + script.Instance.SetFieldValue(scriptField, field._data); + } } } @@ -297,32 +319,27 @@ static class ScriptEngine return (assembly, image); } + /// Retrieves the base classes from which every Component or Entity inherits + static void GetCoreClasses() + { + s_EngineObject?.ReleaseRef(); + s_EntityRoot?.ReleaseRef(); + s_ComponentRoot?.ReleaseRef(); + + s_EngineObject = new ScriptClass("GlitchyEngine.Core", "EngineObject", s_CoreAssemblyImage, .Class); + s_EntityRoot = new ScriptClass("GlitchyEngine", "Entity", s_CoreAssemblyImage, .Entity); + s_ComponentRoot = new ScriptClass("GlitchyEngine", "Component", s_CoreAssemblyImage, .Component); + } + + /// Retrieves the classes for Attributes that are defined in the Core library + static void GetCoreAttributes() + { + Attributes.s_ShowInEditorAttribute = Mono.mono_class_from_name(s_CoreAssemblyImage, "GlitchyEngine.Editor", "ShowInEditorAttribute"); + } + private static void GetEntitiesFromAssemblies() { - for (var entry in _entityScripts) - { - entry.value.ReleaseRef(); - } - _entityScripts.Clear(); - - for (var sharpClass in _sharpClasses) - { - sharpClass.value.ReleaseRef(); - } - _sharpClasses.Clear(); - - if (s_EngineObject != null) - { - s_EngineObject.ReleaseRef(); - s_EntityRoot.ReleaseRef(); - } - - s_EngineObject = new ScriptClass("GlitchyEngine.Core", "EngineObject", s_CoreAssemblyImage); - s_EntityRoot = new ScriptClass("GlitchyEngine", "Entity", s_CoreAssemblyImage); - - Log.EngineLogger.Assert(s_EntityRoot != null); - - Attributes.s_ShowInEditorAttribute = Mono.mono_class_from_name(s_CoreAssemblyImage, "GlitchyEngine.Editor", "ShowInEditorAttribute"); + ClearDictionaryAndReleaseValues!(_entityScripts); MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(s_AppAssemblyImage, .MONO_TABLE_TYPEDEF); int32 numTypes = Mono.mono_table_info_get_rows(typeDefinitionsTable); @@ -336,10 +353,19 @@ static class ScriptEngine char8* name = Mono.mono_metadata_string_heap(s_AppAssemblyImage, (.)cols[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_NAME]); MonoClass* monoClass = Mono.mono_class_from_name(s_AppAssemblyImage, nameSpace, name); - + + // Check if it is an entity if (monoClass != null && Mono.mono_class_is_subclass_of(monoClass, s_EntityRoot.[Friend]_monoClass, false)) { - ScriptClass entityScript = new ScriptClass(StringView(nameSpace), StringView(name), s_AppAssemblyImage); + ScriptClass entityScript = new ScriptClass(StringView(nameSpace), StringView(name), s_AppAssemblyImage, .Entity); + _entityScripts.Add(entityScript.FullName, entityScript); + + Log.EngineLogger.Info($"Added entity \"{entityScript.FullName}\""); + } + // Check if it is a component + else if (monoClass != null && Mono.mono_class_is_subclass_of(monoClass, s_ComponentRoot.[Friend]_monoClass, false)) + { + ScriptClass entityScript = new ScriptClass(StringView(nameSpace), StringView(name), s_AppAssemblyImage, .Entity); _entityScripts.Add(entityScript.FullName, entityScript); Log.EngineLogger.Info($"Added entity \"{entityScript.FullName}\""); @@ -399,8 +425,8 @@ static class ScriptEngine StringView classNamespace = StringView(Mono.mono_class_get_namespace(monoClass)); // TODO: at the moment only allow user-structs - if (classNamespace.StartsWith("GlitchyEngine")) - return null; + //if (classNamespace.StartsWith("GlitchyEngine")) + // return null; switch (fieldType) { @@ -408,7 +434,20 @@ static class ScriptEngine ScriptFieldType scriptType = .None; if (fieldType == .Class) - scriptType = .Class; + { + if (Mono.mono_class_is_subclass_of(monoClass, s_EntityRoot._monoClass, false)) + { + scriptType = .Entity; + } + else if (Mono.mono_class_is_subclass_of(monoClass, s_ComponentRoot._monoClass, false)) + { + scriptType = .Component; + } + else + { + scriptType = .Class; + } + } else if (fieldType == .Enum) scriptType = .Enum; else if (fieldType == .Valuetype) diff --git a/GlitchyEngine/src/Scripting/ScriptFieldType.bf b/GlitchyEngine/src/Scripting/ScriptFieldType.bf index 175555e..acb9012 100644 --- a/GlitchyEngine/src/Scripting/ScriptFieldType.bf +++ b/GlitchyEngine/src/Scripting/ScriptFieldType.bf @@ -27,6 +27,7 @@ enum ScriptFieldType case Double, Double2, Double3, Double4; case Entity; + case Component; public Type GetBeefType() { @@ -79,6 +80,8 @@ enum ScriptFieldType case .Entity: return typeof(UUID); + case .Component: + return typeof(UUID); default: return null; diff --git a/GlitchyEngine/src/Scripting/ScriptInstance.bf b/GlitchyEngine/src/Scripting/ScriptInstance.bf index 9cbc933..340cfe8 100644 --- a/GlitchyEngine/src/Scripting/ScriptInstance.bf +++ b/GlitchyEngine/src/Scripting/ScriptInstance.bf @@ -21,6 +21,8 @@ class ScriptInstance : RefCounter private bool _isCreated = false; + internal MonoObject* MonoInstance => _instance; + public this(ScriptClass scriptClass) { Log.EngineLogger.AssertDebug(scriptClass != null); @@ -37,8 +39,6 @@ class ScriptInstance : RefCounter _scriptClass?.ReleaseRef(); } - internal MonoObject* MonoInstance => _instance; - public void Instantiate(UUID uuid) { _instance = _scriptClass.CreateInstance(uuid); diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index e9fb7cf..a2e2feb 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -100,6 +100,15 @@ namespace GlitchyEngine.World ScriptEngine.InitializeInstance(targetEntity, targetComponent); } + // Copy values to entities. + // We do this in a separate loop because we might reference other entities. + // If we did it in a single loop these entities might not exist yet. + for (let (handle, script) in target._ecsWorld.Enumerate()) + { + Entity entity = .(handle, this); + ScriptEngine.CopyEditorFieldsToInstance(entity, script); + } + // Copy transforms... needs special handling for the Parent<->Child relations for (let (sourceHandle, sourceTransform) in _ecsWorld.Enumerate()) { diff --git a/GlitchyEngineHelper/src/Mono/Mono.bf b/GlitchyEngineHelper/src/Mono/Mono.bf index 32007cf..96af9c7 100644 --- a/GlitchyEngineHelper/src/Mono/Mono.bf +++ b/GlitchyEngineHelper/src/Mono/Mono.bf @@ -158,6 +158,9 @@ static class Mono [LinkName(.C)] public static extern MonoType* mono_reflection_type_get_type(MonoReflectionType* reflectionType); + [LinkName(.C)] + public static extern MonoReflectionType* mono_type_get_object(MonoDomain *domain, MonoType *type); + [LinkName(.C)] public static extern MonoClassField* mono_class_get_fields(MonoClass* klass, gpointer* iter); diff --git a/ScriptCore/Editor/ShowInEditorAttribute.cs b/ScriptCore/Editor/ShowInEditorAttribute.cs index cfaa605..6cce197 100644 --- a/ScriptCore/Editor/ShowInEditorAttribute.cs +++ b/ScriptCore/Editor/ShowInEditorAttribute.cs @@ -2,6 +2,12 @@ using System; namespace GlitchyEngine.Editor; +// TODO: Maybe rename, definitely move into different namespace +/* Why? + * - this attribute also means that the Field is being serialized + * - Editor-Namespace wont be available for distribution builds of the game + */ +[AttributeUsage(AttributeTargets.Field)] public sealed class ShowInEditorAttribute : Attribute { public string DisplayName { get; set; } = null;