Allow dragging and dropping of Entities in editor

+ New Dictionary Helpers
+ Made ImGui Payloads safer
+ Start of Component drag'n'drop
This commit is contained in:
Simon Lübeß
2023-07-24 23:17:00 +02:00
parent b53ed2d8f2
commit 37197cbdf1
19 changed files with 500 additions and 183 deletions
@@ -25,36 +25,36 @@ namespace Sandbox
class MyTestEntity : Entity class MyTestEntity : Entity
{ {
//[ShowInEditor] [ShowInEditor]
RigidBody2D _rigidBody; RigidBody2D _rigidBody;
public bool Bo; //public bool Bo;
public byte By; //public byte By;
public ushort Us; //public ushort Us;
public uint Ui; //public uint Ui;
public ulong Ul; //public ulong Ul;
public sbyte Sb; //public sbyte Sb;
public short Sh; //public short Sh;
public int In; //public int In;
public long Lo; //public long Lo;
public float Fl; //public float Fl;
public double Do; //public double Do;
public float2 V2; //public float2 V2;
public float3 V3; //public float3 V3;
public float4 V4; //public float4 V4;
public Entity TheEntity; public Entity TheEntity;
public float JumpForce = 2000; public float JumpForce = 2000;
[ShowInEditor] float MoveForce = 1000; [ShowInEditor] float MoveForce = 1000;
[ShowInEditor] private int MyNumber = 1337; [ShowInEditor] private int MyNumber = 1337;
[ShowInEditor] public double MyDouble = 1000.0f; //[ShowInEditor] public double MyDouble = 1000.0f;
public MyStruct AStruct;
//public MyStruct AStruct;
public Camera Camera; public Camera Camera;
/// <summary> /// <summary>
@@ -67,12 +67,16 @@ namespace Sandbox
Log.Info($"Jump Force: {JumpForce}"); Log.Info($"Jump Force: {JumpForce}");
_rigidBody ??= GetComponent<RigidBody2D>() ?? AddComponent<RigidBody2D>(); _rigidBody ??= GetComponent<RigidBody2D>() ?? AddComponent<RigidBody2D>();
Camera = FindEntityWithName("Camera").As<Camera>();
if (Camera == null) if (Camera == null)
{ {
Log.Error("Camera not found."); Log.Warning("Camera wasn't set in editor. Searching...");
Camera = FindEntityWithName("Camera").As<Camera>();
if (Camera == null)
{
Log.Error("Camera not found.");
}
} }
} }
@@ -99,7 +103,7 @@ namespace Sandbox
if (Input.IsKeyPressed(Key.E)) if (Input.IsKeyPressed(Key.E))
Camera.DistanceFromPlayer += deltaTime; Camera.DistanceFromPlayer += deltaTime;
if (Input.IsKeyPressing(Key.Space)) if (Input.IsKeyPressing(Key.Space))
{ {
force.Y += JumpForce; force.Y += JumpForce;
+22 -47
View File
@@ -71,8 +71,8 @@
SpriteRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 1, R = 1,
G = 0.941886961, G = 0.941887021,
B = 0.401484549, B = 0.401484519,
A = 1 A = 1
}, },
Sprite = "", Sprite = "",
@@ -162,7 +162,7 @@
OrthographicHeight = 10, OrthographicHeight = 10,
OrthographicNearPlane = 0, OrthographicNearPlane = 0,
OrthographicFarPlane = 10, OrthographicFarPlane = 10,
AspectRatio = 0.884925187, AspectRatio = 1.17151165,
FixedAspectRatio = false FixedAspectRatio = false
}, },
ScriptComponent = { ScriptComponent = {
@@ -195,8 +195,8 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = -0.121203184, X = -0.121203199,
Y = 0.660160959, Y = 0.660161018,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
@@ -234,37 +234,12 @@
ScriptComponent = { ScriptComponent = {
ScriptClass = "Sandbox.MyTestEntity", ScriptClass = "Sandbox.MyTestEntity",
Fields = [ Fields = [
Bo = (Bool)false, _rigidBody = (Component)0,
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
},
TheEntity = (Entity)0, TheEntity = (Entity)0,
JumpForce = (Float)200, JumpForce = (Float)200,
MoveForce = (Float)148, MoveForce = (Float)148,
MyNumber = (Int)22, MyNumber = (Int)22,
MyDouble = (Double)23 Camera = (Entity)1491484622542812645
] } ] }
}, },
{ {
@@ -301,7 +276,7 @@
Color = { Color = {
R = 0.985467017, R = 0.985467017,
G = 1, G = 1,
B = 0.569978058 B = 0.569977999
} }
} }
}, },
@@ -348,8 +323,8 @@
SpriteRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 0.425658971, R = 0.425658971,
G = 0.855207562, G = 0.855207503,
B = 0.0558161102, B = 0.0558161139,
A = 1 A = 1
}, },
Sprite = "", Sprite = "",
@@ -363,14 +338,14 @@
TransformComponent = { TransformComponent = {
Position = { Position = {
X = 6.74610233, X = 6.74610233,
Y = -2.03225899, Y = -2.03225923,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
X = 0, X = 0,
Y = 0, Y = 0,
Z = -0.331792623, Z = -0.331792623,
W = 0.943352342 W = 0.943352461
}, },
Scale = { Scale = {
X = 5.29557419, X = 5.29557419,
@@ -409,7 +384,7 @@
}, },
SpriteRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 0.666116953, R = 0.666117013,
G = 0.0600316115, G = 0.0600316115,
B = 1, B = 1,
A = 1 A = 1
@@ -424,8 +399,8 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = 15.2321301, X = 15.2321291,
Y = -1.98709249, Y = -1.98709261,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
@@ -435,7 +410,7 @@
W = 0.990847468 W = 0.990847468
}, },
Scale = { Scale = {
X = 13.9764566, X = 13.9764547,
Y = 1, Y = 1,
Z = 1 Z = 1
}, },
@@ -471,7 +446,7 @@
}, },
SpriteRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 0.176544324, R = 0.176544309,
G = 0.666713238, G = 0.666713238,
B = 0.687030852, B = 0.687030852,
A = 1 A = 1
@@ -487,24 +462,24 @@
TransformComponent = { TransformComponent = {
Position = { Position = {
X = -6.27842855, X = -6.27842855,
Y = 2.62550211, Y = 2.62550187,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
X = 0, X = 0,
Y = 0, Y = 0,
Z = 0.187087834, Z = 0.187087849,
W = 0.982343197 W = 0.982343197
}, },
Scale = { Scale = {
X = 0.999999523, X = 0.999999464,
Y = 6.49634314, Y = 6.49634314,
Z = 1 Z = 1
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 0, X = 0,
Y = 0, Y = 0,
Z = 0.376393586 Z = 0.376393616
} }
}, },
Rigidbody2D = { Rigidbody2D = {
@@ -522,7 +497,7 @@
}, },
Density = 1, Density = 1,
Friction = 0.5, Friction = 0.5,
Restitution = 0.899999976, Restitution = 0.899999917,
RestitutionThreshold = 0.5 RestitutionThreshold = 0.5
} }
} }
@@ -9,12 +9,21 @@
".NETStandard,Version=v2.0/": { ".NETStandard,Version=v2.0/": {
"ScriptCore/1.0.0": { "ScriptCore/1.0.0": {
"dependencies": { "dependencies": {
"Half": "1.0.0",
"NETStandard.Library": "2.0.3" "NETStandard.Library": "2.0.3"
}, },
"runtime": { "runtime": {
"ScriptCore.dll": {} "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": {}, "Microsoft.NETCore.Platforms/1.1.0": {},
"NETStandard.Library/2.0.3": { "NETStandard.Library/2.0.3": {
"dependencies": { "dependencies": {
@@ -29,6 +38,13 @@
"serviceable": false, "serviceable": false,
"sha512": "" "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": { "Microsoft.NETCore.Platforms/1.1.0": {
"type": "package", "type": "package",
"serviceable": true, "serviceable": true,
@@ -33,7 +33,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -78,7 +78,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -7,6 +7,8 @@ using GlitchyEngine.Renderer;
using GlitchyEngine; using GlitchyEngine;
using GlitchyEngine.Content; using GlitchyEngine.Content;
using GlitchyEngine.Scripting; using GlitchyEngine.Scripting;
using GlitchyEngine.Core;
using Mono;
namespace GlitchyEditor.EditWindows namespace GlitchyEditor.EditWindows
{ {
@@ -278,7 +280,7 @@ namespace GlitchyEditor.EditWindows
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -306,7 +308,7 @@ namespace GlitchyEditor.EditWindows
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -338,7 +340,7 @@ namespace GlitchyEditor.EditWindows
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -601,94 +603,94 @@ namespace GlitchyEditor.EditWindows
{ {
let scriptFields = ScriptEngine.GetScriptFieldMap(entity); let scriptFields = ScriptEngine.GetScriptFieldMap(entity);
for (var (name, field) in ref scriptFields) for (var (fieldName, field) in ref scriptFields)
{ {
switch (field.Type) switch (field.Type)
{ {
case .Bool: case .Bool:
var value = field.GetData<bool>(); var value = field.GetData<bool>();
if (ImGui.Checkbox(name.CStr(), &value)) if (ImGui.Checkbox(fieldName.CStr(), &value))
field.SetData(value); field.SetData(value);
case .SByte: case .SByte:
var value = field.GetData<int8>(); var value = field.GetData<int8>();
if (ImGui.DragScalar(name.CStr(), .S8, &value)) if (ImGui.DragScalar(fieldName.CStr(), .S8, &value))
field.SetData(value);
case .Short:
var value = field.GetData<int16>();
if (ImGui.DragScalar(fieldName.CStr(), .S16, &value))
field.SetData(value); field.SetData(value);
case .Short:
var value = field.GetData<int16>();
if (ImGui.DragScalar(name.CStr(), .S16, &value))
field.SetData(value);
case .Int: case .Int:
var value = field.GetData<int32>(); var value = field.GetData<int32>();
if (ImGui.DragScalar(name.CStr(), .S32, &value)) if (ImGui.DragScalar(fieldName.CStr(), .S32, &value))
field.SetData(value); field.SetData(value);
case .Int2: case .Int2:
var value = field.GetData<int2>(); var value = field.GetData<int2>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 2)) if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 2))
field.SetData(value); field.SetData(value);
case .Int3: case .Int3:
var value = field.GetData<int3>(); var value = field.GetData<int3>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 3)) if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 3))
field.SetData(value); field.SetData(value);
case .Int4: case .Int4:
var value = field.GetData<int4>(); var value = field.GetData<int4>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 4)) if (ImGui.DragScalarN(fieldName.CStr(), .S32, &value, 4))
field.SetData(value); field.SetData(value);
case .Long: case .Long:
var value = field.GetData<int64>(); var value = field.GetData<int64>();
if (ImGui.DragScalar(name.CStr(), .S64, &value)) if (ImGui.DragScalar(fieldName.CStr(), .S64, &value))
field.SetData(value); field.SetData(value);
case .Byte: case .Byte:
var value = field.GetData<uint8>(); var value = field.GetData<uint8>();
if (ImGui.DragScalar(name.CStr(), .U8, &value)) if (ImGui.DragScalar(fieldName.CStr(), .U8, &value))
field.SetData(value); field.SetData(value);
case .UShort: case .UShort:
var value = field.GetData<uint16>(); var value = field.GetData<uint16>();
if (ImGui.DragScalar(name.CStr(), .U16, &value)) if (ImGui.DragScalar(fieldName.CStr(), .U16, &value))
field.SetData(value); field.SetData(value);
case .UInt: case .UInt:
var value = field.GetData<uint32>(); var value = field.GetData<uint32>();
if (ImGui.DragScalar(name.CStr(), .U32, &value)) if (ImGui.DragScalar(fieldName.CStr(), .U32, &value))
field.SetData(value); field.SetData(value);
case .ULong: case .ULong:
var value = field.GetData<uint64>(); var value = field.GetData<uint64>();
if (ImGui.DragScalar(name.CStr(), .U64, &value)) if (ImGui.DragScalar(fieldName.CStr(), .U64, &value))
field.SetData(value); field.SetData(value);
case .Float: case .Float:
var value = field.GetData<float>(); var value = field.GetData<float>();
if (ImGui.DragScalar(name.CStr(), .Float, &value)) if (ImGui.DragScalar(fieldName.CStr(), .Float, &value))
field.SetData(value); field.SetData(value);
case .float2: case .float2:
var value = field.GetData<float2>(); var value = field.GetData<float2>();
if (ImGui.Editfloat2(name, ref value)) if (ImGui.Editfloat2(fieldName, ref value))
field.SetData(value); field.SetData(value);
case .float3: case .float3:
var value = field.GetData<float3>(); var value = field.GetData<float3>();
if (ImGui.Editfloat3(name, ref value)) if (ImGui.Editfloat3(fieldName, ref value))
field.SetData(value); field.SetData(value);
case .float4: case .float4:
var value = field.GetData<float4>(); var value = field.GetData<float4>();
if (ImGui.Editfloat4(name, ref value)) if (ImGui.Editfloat4(fieldName, ref value))
field.SetData(value); field.SetData(value);
case .Double: case .Double:
var value = field.GetData<double>(); var value = field.GetData<double>();
if (ImGui.DragScalar(name.CStr(), .Double, &value)) if (ImGui.DragScalar(fieldName.CStr(), .Double, &value))
field.SetData(value); field.SetData(value);
case .Double2: case .Double2:
var value = field.GetData<double2>(); var value = field.GetData<double2>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 2)) if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 2))
field.SetData(value); field.SetData(value);
case .Double3: case .Double3:
var value = field.GetData<double3>(); var value = field.GetData<double3>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 3)) if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 3))
field.SetData(value); field.SetData(value);
case .Double4: case .Double4:
var value = field.GetData<double4>(); var value = field.GetData<double4>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 4)) if (ImGui.DragScalarN(fieldName.CStr(), .Double, &value, 4))
field.SetData(value); field.SetData(value);
case .Enum: case .Enum:
// TODO! // TODO!
@@ -697,11 +699,16 @@ namespace GlitchyEditor.EditWindows
// TODO! // TODO!
case .Entity: case .Entity:
// TODO! ShowEntityReceiver(field, fieldName, scriptClass);
case .Component:
ShowComponentReceiver(field, fieldName, scriptClass);
case .Struct: case .Struct:
// TODO! // TODO!
case .Class:
// We don't support editing classes
default: default:
Log.EngineLogger.Error($"Unhandled field type {field.Type}"); 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<UUID>();
Result<Entity> 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<ScriptComponent>(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<UUID>(droppedEntity.UUID);
}
}
ImGui.EndDragDropTarget();
}
}
private static void ShowComponentReceiver(ScriptFieldInstance* field, StringView fieldName, ScriptClass scriptClass)
{
var entityId = field.GetData<UUID>();
Result<Entity> 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<UUID>(droppedEntity.UUID);
}
}
ImGui.EndDragDropTarget();
}
}
private static void LabelColumn(StringView label) private static void LabelColumn(StringView label)
{ {
ImGui.TextUnformatted(label); ImGui.TextUnformatted(label);
@@ -773,7 +907,7 @@ namespace GlitchyEditor.EditWindows
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -324,7 +324,7 @@ namespace GlitchyEditor.EditWindows
Path.Fixup(fullpath); Path.Fixup(fullpath);
ImGui.SetDragDropPayload("CONTENT_BROWSER_ITEM", fullpath.CStr(), (.)fullpath.Length, .Once); ImGui.SetDragDropPayload(.ContentBrowserItem, fullpath.CStr(), (.)fullpath.Length, .Once);
ImGui.EndDragDropSource(); ImGui.EndDragDropSource();
} }
@@ -370,7 +370,7 @@ namespace GlitchyEditor.EditWindows
String fullpath = scope String(entry->Path); String fullpath = scope String(entry->Path);
fullpath.AppendF($"#{subAsset.Name}"); fullpath.AppendF($"#{subAsset.Name}");
ImGui.SetDragDropPayload("CONTENT_BROWSER_ITEM", fullpath.CStr(), (.)fullpath.Length, .Once); ImGui.SetDragDropPayload(.ContentBrowserItem, fullpath.CStr(), (.)fullpath.Length, .Once);
ImGui.EndDragDropSource(); ImGui.EndDragDropSource();
} }
@@ -137,7 +137,7 @@ namespace GlitchyEditor.EditWindows
{ {
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
@@ -341,9 +341,13 @@ namespace GlitchyEditor.EditWindows
if (deleted) if (deleted)
return; return;
bool isDragged = false;
if(ImGui.BeginDragDropSource()) if(ImGui.BeginDragDropSource())
{ {
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity)); isDragged = true;
ImGui.SetDragDropPayload(.Entity, &tree.Value, sizeof(Entity));
ImGui.Text(name); ImGui.Text(name);
@@ -352,7 +356,7 @@ namespace GlitchyEditor.EditWindows
if(ImGui.BeginDragDropTarget()) if(ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity);
if(payload != null) if(payload != null)
{ {
@@ -395,7 +399,7 @@ namespace GlitchyEditor.EditWindows
bool clicked = ImGui.IsItemClicked(.Left); bool clicked = ImGui.IsItemClicked(.Left);
bool clickedRight = ImGui.IsItemClicked(.Right); bool clickedRight = ImGui.IsItemClicked(.Right);
if(isOpen) if(isOpen)
{ {
for(var child in tree.Children) for(var child in tree.Children)
@@ -407,6 +411,12 @@ namespace GlitchyEditor.EditWindows
} }
if (clicked || clickedRight) 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) if (inSelectedList && !clickedRight)
{ {
@@ -418,9 +428,13 @@ namespace GlitchyEditor.EditWindows
SelectEntity(tree.Value, !ImGui.GetIO().KeyCtrl && !clickedRight); SelectEntity(tree.Value, !ImGui.GetIO().KeyCtrl && !clickedRight);
inSelectedList = true; inSelectedList = true;
} }
lastClickedEntity = .();
} }
} }
private static Entity lastClickedEntity;
private void ShowEntityHierarchy() private void ShowEntityHierarchy()
{ {
StringView searchString = StringView(&_entitySearchChars); StringView searchString = StringView(&_entitySearchChars);
@@ -464,7 +478,7 @@ namespace GlitchyEditor.EditWindows
{ {
if(ImGui.BeginDragDropTarget()) if(ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.Entity);
if(payload != null) if(payload != null)
{ {
@@ -527,6 +541,10 @@ namespace GlitchyEditor.EditWindows
ImGuiPrintEntityTree(scope .(entity)); 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 = .();
} }
} }
} }
@@ -91,7 +91,7 @@ namespace GlitchyEditor.EditWindows
{ {
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM"); ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
if (payload != null) if (payload != null)
{ {
+7
View File
@@ -53,9 +53,16 @@ namespace GlitchyEditor
public SceneRenderer GameSceneRenderer {get; set;} public SceneRenderer GameSceneRenderer {get; set;}
public SceneRenderer EditorSceneRenderer {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 /// Creates a new editor for the given world
public this(Scene scene, EditorContentManager contentManager) 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; _scene = scene;
_contentManager = contentManager; _contentManager = contentManager;
+33
View File
@@ -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);
}
}
+20
View File
@@ -70,5 +70,25 @@ namespace GlitchyEngine
delete dictionary; 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();
}
}
} }
} }
+54 -4
View File
@@ -21,6 +21,33 @@ public struct ScriptField
IsStatic = isStatic; IsStatic = isStatic;
FieldType = fieldType; 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 public struct ScriptFieldInstance
@@ -90,9 +117,19 @@ class SharpClass : SharpType
ExtractFields(); ExtractFields();
} }
internal MonoType* GetMonoType()
{
return Mono.mono_class_get_type(_monoClass);
}
internal bool IsType(MonoType* type) 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() private void ExtractFields()
@@ -180,8 +217,8 @@ class ScriptClass : SharpClass
public Dictionary<StringView, ScriptField> Fields => _monoFields;*/ public Dictionary<StringView, ScriptField> Fields => _monoFields;*/
[AllowAppend] [AllowAppend]
public this(StringView classNamespace, StringView className, MonoImage* image) : public this(StringView classNamespace, StringView className, MonoImage* image, ScriptFieldType scriptFieldType = .Class) :
base(classNamespace, className, image) base(classNamespace, className, image, scriptFieldType)
{ {
//_constructor = (ConstructorMethod)GetMethodThunk(".ctor", 1); // GetMethod(".ctor", 1);// //_constructor = (ConstructorMethod)GetMethodThunk(".ctor", 1); // GetMethod(".ctor", 1);//
_constructor = GetMethod(".ctor", 1); _constructor = GetMethod(".ctor", 1);
@@ -288,6 +325,19 @@ class ScriptClass : SharpClass
public void SetFieldValue<T>(MonoObject* instance, MonoClassField* field, in T value) public void SetFieldValue<T>(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<T>(MonoObject* instance, MonoClassField* field, in T value) where T : struct*
{
Mono.Mono.mono_field_set_value(instance, field, value);
} }
} }
+73 -34
View File
@@ -58,12 +58,14 @@ static class ScriptEngine
private static Scene s_Context ~ _?.ReleaseRef(); private static Scene s_Context ~ _?.ReleaseRef();
private static ScriptClass s_ComponentRoot ~ _?.ReleaseRef();
private static ScriptClass s_EntityRoot ~ _?.ReleaseRef(); private static ScriptClass s_EntityRoot ~ _?.ReleaseRef();
private static ScriptClass s_EngineObject ~ _?.ReleaseRef(); private static ScriptClass s_EngineObject ~ _?.ReleaseRef();
private static Dictionary<StringView, SharpType> _sharpClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_); private static Dictionary<StringView, SharpType> _sharpClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_);
private static Dictionary<StringView, ScriptClass> _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_); private static Dictionary<StringView, ScriptClass> _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_);
private static Dictionary<StringView, ScriptClass> _componentClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_);
private static Dictionary<UUID, ScriptInstance> _entityScriptInstances = new .() ~ { private static Dictionary<UUID, ScriptInstance> _entityScriptInstances = new .() ~ {
for (var entry in _) for (var entry in _)
@@ -74,6 +76,7 @@ static class ScriptEngine
} }
public static Dictionary<StringView, ScriptClass> EntityClasses => _entityScripts; public static Dictionary<StringView, ScriptClass> EntityClasses => _entityScripts;
public static Dictionary<StringView, ScriptClass> ComponentClasses => _componentClasses;
public static Scene Context => s_Context; public static Scene Context => s_Context;
@@ -170,7 +173,12 @@ static class ScriptEngine
CreateAppDomain("GlitchyEngineScriptRuntime"); CreateAppDomain("GlitchyEngineScriptRuntime");
(s_CoreAssembly, s_CoreAssemblyImage) = LoadAssembly("resources/scripts/ScriptCore.dll", _debuggingEnabled); (s_CoreAssembly, s_CoreAssemblyImage) = LoadAssembly("resources/scripts/ScriptCore.dll", _debuggingEnabled);
(s_AppAssembly, s_AppAssemblyImage) = LoadAssembly("SandboxProject/Assets/Scripts/bin/Sandbox.dll", _debuggingEnabled); (s_AppAssembly, s_AppAssemblyImage) = LoadAssembly("SandboxProject/Assets/Scripts/bin/Sandbox.dll", _debuggingEnabled);
ClearDictionaryAndReleaseValues!(_sharpClasses);
GetCoreAttributes();
GetCoreClasses();
GetEntitiesFromAssemblies(); GetEntitiesFromAssemblies();
ScriptGlue.RegisterManagedComponents(); ScriptGlue.RegisterManagedComponents();
@@ -206,12 +214,12 @@ static class ScriptEngine
script.Instance.Instantiate(entity.UUID); script.Instance.Instantiate(entity.UUID);
CopyEditorFieldsToInstance(entity, script); //CopyEditorFieldsToInstance(entity, script);
return true; 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), // 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 // 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) for (var (fieldName, field) in fields)
{ {
// TODO: a litte assertion maybe? // TODO: a litte assertion maybe?
ScriptField scriptField = script.Instance.ScriptClass.Fields[fieldName]; 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<UUID>();
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); 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() private static void GetEntitiesFromAssemblies()
{ {
for (var entry in _entityScripts) ClearDictionaryAndReleaseValues!(_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");
MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(s_AppAssemblyImage, .MONO_TABLE_TYPEDEF); MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(s_AppAssemblyImage, .MONO_TABLE_TYPEDEF);
int32 numTypes = Mono.mono_table_info_get_rows(typeDefinitionsTable); 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]); 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); 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)) 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); _entityScripts.Add(entityScript.FullName, entityScript);
Log.EngineLogger.Info($"Added entity \"{entityScript.FullName}\""); Log.EngineLogger.Info($"Added entity \"{entityScript.FullName}\"");
@@ -399,8 +425,8 @@ static class ScriptEngine
StringView classNamespace = StringView(Mono.mono_class_get_namespace(monoClass)); StringView classNamespace = StringView(Mono.mono_class_get_namespace(monoClass));
// TODO: at the moment only allow user-structs // TODO: at the moment only allow user-structs
if (classNamespace.StartsWith("GlitchyEngine")) //if (classNamespace.StartsWith("GlitchyEngine"))
return null; // return null;
switch (fieldType) switch (fieldType)
{ {
@@ -408,7 +434,20 @@ static class ScriptEngine
ScriptFieldType scriptType = .None; ScriptFieldType scriptType = .None;
if (fieldType == .Class) 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) else if (fieldType == .Enum)
scriptType = .Enum; scriptType = .Enum;
else if (fieldType == .Valuetype) else if (fieldType == .Valuetype)
@@ -27,6 +27,7 @@ enum ScriptFieldType
case Double, Double2, Double3, Double4; case Double, Double2, Double3, Double4;
case Entity; case Entity;
case Component;
public Type GetBeefType() public Type GetBeefType()
{ {
@@ -79,6 +80,8 @@ enum ScriptFieldType
case .Entity: case .Entity:
return typeof(UUID); return typeof(UUID);
case .Component:
return typeof(UUID);
default: default:
return null; return null;
@@ -21,6 +21,8 @@ class ScriptInstance : RefCounter
private bool _isCreated = false; private bool _isCreated = false;
internal MonoObject* MonoInstance => _instance;
public this(ScriptClass scriptClass) public this(ScriptClass scriptClass)
{ {
Log.EngineLogger.AssertDebug(scriptClass != null); Log.EngineLogger.AssertDebug(scriptClass != null);
@@ -37,8 +39,6 @@ class ScriptInstance : RefCounter
_scriptClass?.ReleaseRef(); _scriptClass?.ReleaseRef();
} }
internal MonoObject* MonoInstance => _instance;
public void Instantiate(UUID uuid) public void Instantiate(UUID uuid)
{ {
_instance = _scriptClass.CreateInstance(uuid); _instance = _scriptClass.CreateInstance(uuid);
+9
View File
@@ -100,6 +100,15 @@ namespace GlitchyEngine.World
ScriptEngine.InitializeInstance(targetEntity, targetComponent); 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<ScriptComponent>())
{
Entity entity = .(handle, this);
ScriptEngine.CopyEditorFieldsToInstance(entity, script);
}
// Copy transforms... needs special handling for the Parent<->Child relations // Copy transforms... needs special handling for the Parent<->Child relations
for (let (sourceHandle, sourceTransform) in _ecsWorld.Enumerate<TransformComponent>()) for (let (sourceHandle, sourceTransform) in _ecsWorld.Enumerate<TransformComponent>())
{ {
+3
View File
@@ -158,6 +158,9 @@ static class Mono
[LinkName(.C)] [LinkName(.C)]
public static extern MonoType* mono_reflection_type_get_type(MonoReflectionType* reflectionType); 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)] [LinkName(.C)]
public static extern MonoClassField* mono_class_get_fields(MonoClass* klass, gpointer* iter); public static extern MonoClassField* mono_class_get_fields(MonoClass* klass, gpointer* iter);
@@ -2,6 +2,12 @@ using System;
namespace GlitchyEngine.Editor; 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 sealed class ShowInEditorAttribute : Attribute
{ {
public string DisplayName { get; set; } = null; public string DisplayName { get; set; } = null;