diff --git a/.gitmodules b/.gitmodules index fa50ef6..12dbfbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,4 +30,7 @@ url = https://github.com/jazzbre/box2d-beef.git [submodule "GlitchyEngine/vendor/Beef.Linq"] path = GlitchyEngine/vendor/Beef.Linq - url = https://github.com/aharabada/Beef.Linq.git \ No newline at end of file + url = https://github.com/aharabada/Beef.Linq.git +[submodule "vendor/ImGui.NET"] + path = vendor/ImGui.NET + url = https://github.com/aharabada/ImGui.NET.git diff --git a/GlitchyEditor/resources/scripts/ImGui.NET.dll b/GlitchyEditor/resources/scripts/ImGui.NET.dll new file mode 100644 index 0000000..48f9be7 Binary files /dev/null and b/GlitchyEditor/resources/scripts/ImGui.NET.dll differ diff --git a/GlitchyEditor/resources/scripts/ImGui.NET.pdb b/GlitchyEditor/resources/scripts/ImGui.NET.pdb new file mode 100644 index 0000000..aa1ae25 Binary files /dev/null and b/GlitchyEditor/resources/scripts/ImGui.NET.pdb differ diff --git a/GlitchyEditor/resources/scripts/ScriptCore.deps.json b/GlitchyEditor/resources/scripts/ScriptCore.deps.json index 84213a6..66f17f3 100644 --- a/GlitchyEditor/resources/scripts/ScriptCore.deps.json +++ b/GlitchyEditor/resources/scripts/ScriptCore.deps.json @@ -10,6 +10,7 @@ "ScriptCore/1.0.0": { "dependencies": { "Half": "1.0.0", + "ImGui.NET": "1.89.9.3", "NETStandard.Library": "2.0.3" }, "runtime": { @@ -29,6 +30,40 @@ "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0" } + }, + "System.Buffers/4.4.0": { + "runtime": { + "lib/netstandard2.0/System.Buffers.dll": { + "assemblyVersion": "4.0.2.0", + "fileVersion": "4.6.25519.3" + } + } + }, + "System.Numerics.Vectors/4.4.0": { + "runtime": { + "lib/netstandard2.0/System.Numerics.Vectors.dll": { + "assemblyVersion": "4.1.3.0", + "fileVersion": "4.6.25519.3" + } + } + }, + "System.Runtime.CompilerServices.Unsafe/4.4.0": { + "runtime": { + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { + "assemblyVersion": "4.0.3.0", + "fileVersion": "0.0.0.0" + } + } + }, + "ImGui.NET/1.89.9.3": { + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.4.0" + }, + "runtime": { + "ImGui.NET.dll": {} + } } } }, @@ -58,6 +93,32 @@ "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", "path": "netstandard.library/2.0.3", "hashPath": "netstandard.library.2.0.3.nupkg.sha512" + }, + "System.Buffers/4.4.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==", + "path": "system.buffers/4.4.0", + "hashPath": "system.buffers.4.4.0.nupkg.sha512" + }, + "System.Numerics.Vectors/4.4.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==", + "path": "system.numerics.vectors/4.4.0", + "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512" + }, + "System.Runtime.CompilerServices.Unsafe/4.4.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-9dLLuBxr5GNmOfl2jSMcsHuteEg32BEfUotmmUkmZjpR3RpVHE8YQwt0ow3p6prwA1ME8WqDVZqrr8z6H8G+Kw==", + "path": "system.runtime.compilerservices.unsafe/4.4.0", + "hashPath": "system.runtime.compilerservices.unsafe.4.4.0.nupkg.sha512" + }, + "ImGui.NET/1.89.9.3": { + "type": "project", + "serviceable": false, + "sha512": "" } } } \ No newline at end of file diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index e3daf9b..d2d69c8 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -572,6 +572,10 @@ namespace GlitchyEditor.EditWindows } ImGui.EndPopup(); } + + ScriptEngine.ShowScriptEditor(entity, scriptComponent); + + ImGui.Separator(); ScriptClass scriptClass = ScriptEngine.GetScriptClass(scriptComponent.ScriptClassName); diff --git a/GlitchyEngine/src/Scripting/ScriptClass.bf b/GlitchyEngine/src/Scripting/ScriptClass.bf index f17063a..43046d1 100644 --- a/GlitchyEngine/src/Scripting/ScriptClass.bf +++ b/GlitchyEngine/src/Scripting/ScriptClass.bf @@ -121,6 +121,16 @@ class SharpClass : SharpType ExtractFields(); } + + internal this(MonoClass* monoClass, ScriptFieldType fieldType = .Class) : + base(StringView(Mono.mono_class_get_namespace(monoClass)), StringView(Mono.mono_class_get_name(monoClass)), fieldType) + { + _monoClass = monoClass; + + Log.EngineLogger.AssertDebug(_monoClass != null); + + ExtractFields(); + } internal MonoType* GetMonoType() { diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index 43753c2..7911f7a 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -61,6 +61,8 @@ static class ScriptEngine private static ScriptClass s_EntityRoot ~ _?.ReleaseRef(); private static ScriptClass s_EngineObject ~ _?.ReleaseRef(); + private static ScriptClass s_EntityEditor ~ _?.ReleaseRef(); + private static Dictionary _sharpClasses = new .() ~ DeleteDictionaryAndReleaseValues!(_); private static Dictionary _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_); @@ -142,6 +144,8 @@ static class ScriptEngine Mono.mono_thread_set_main(Mono.mono_thread_current()); } + + static bool _requestingReload = false; static void InitAssemblyWatcher() { @@ -165,6 +169,13 @@ static class ScriptEngine _userAssemblyWatcher = new FileSystemWatcher(directory, fileName); _userAssemblyWatcher.OnChanged.Add(new (fileName) => { + _userAssemblyWatcher.StopRaisingEvents(); + + if (_requestingReload) + return; + + _requestingReload = true; + // TODO: Temporary, we want to be able to reload while in play-mode. (+ Editor Scripts will be a thing some day) if (_entityScriptInstances.Count > 0) { @@ -173,14 +184,14 @@ static class ScriptEngine } Log.EngineLogger.Info("Script reload requested."); - _userAssemblyWatcher.StopRaisingEvents(); - + Application.Instance.InvokeOnMainThread(new () => { Log.EngineLogger.Info("Reloading scripts..."); ReloadAssemblies(); Log.EngineLogger.Info("Scripts reloaded!"); - + + _requestingReload = false; _userAssemblyWatcher.StartRaisingEvents(); }); @@ -520,10 +531,14 @@ static class ScriptEngine ReleaseRefAndNullify!(s_EngineObject); ReleaseRefAndNullify!(s_EntityRoot); ReleaseRefAndNullify!(s_ComponentRoot); + ReleaseRefAndNullify!(s_EntityEditor); 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); + + // Editor classes + s_EntityEditor = new ScriptClass("GlitchyEngine.Editor", "EntityEditor", s_CoreAssemblyImage, .Class); } /// Retrieves the classes for Attributes that are defined in the Core library @@ -672,13 +687,20 @@ static class ScriptEngine else if (fieldType == .Genericinst) { scriptType = .GenericClass; + return null; + //return new SharpClass(monoClass, scriptType); } else if (fieldType == .SzArray) { scriptType = .Array; return null; } + else if (fieldType == .Object) + { + // TODO: Help + return null; + } Log.EngineLogger.AssertDebug(scriptType != .None); @@ -775,4 +797,24 @@ static class ScriptEngine wrappedException.ReleaseRef(); } + + public static void ShowScriptEditor(Entity entity, ScriptComponent* scriptComponent) + { + let method = s_EntityEditor.GetMethod("ShowDefaultEntityEditor", 2); + + ScriptClass scriptClass = ScriptEngine.GetScriptClass(scriptComponent.ScriptClassName); + + if (scriptClass == null) + return; + + let monoType = scriptClass.GetMonoType(); + let monoReflectionType = Mono.mono_type_get_object(s_AppDomain, monoType); + + let entityId = entity.UUID; + +#unwarn + void*[2] args = .(&entityId, monoReflectionType); + + s_EntityEditor.Invoke(method, null, &args); + } } diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index 9a30638..d0e1d03 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -467,6 +467,12 @@ static class ScriptGlue RegisterCall("ScriptGlue::modf_float4", => GlitchyEngine.Math.modf); } +#endregion + +#region Editor Stuff + + + #endregion private static void RegisterCall(String name, T method) where T : var diff --git a/ScriptCore/Editor/EntityEditor.cs b/ScriptCore/Editor/EntityEditor.cs new file mode 100644 index 0000000..3aa4b74 --- /dev/null +++ b/ScriptCore/Editor/EntityEditor.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using GlitchyEngine.Core; +using ImGuiNET; + +namespace GlitchyEngine.Editor; + +internal class EntityEditor +{ + public static bool ShowFieldInEditor(FieldInfo fieldInfo) + { + if (fieldInfo.IsPublic) + return true; + + var showInEditorAttribute = fieldInfo.GetCustomAttribute(); + + if (showInEditorAttribute != null) + return true; + + return false; + } + + public static void ShowDefaultEntityEditor(UUID entityId, Type entityType) + { + object instance = ScriptGlue.Entity_GetScriptInstance(entityId); + + ShowEditor(entityType, instance); + } + + public static void ShowEditor(Type type, object reference) + { + T GetValue(FieldInfo fieldInfo) + { + return default; + } + + int i = 0; + + foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + { + i++; + ImGui.PushID(i); + + if (ShowFieldInEditor(field)) + { + Type fieldType = field.FieldType; + + if (fieldType.IsGenericType) + { + if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) + { + ImGui.Text($"{field.Name} List"); + } + else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + ImGui.Text($"{field.Name} Dictionary"); + } + } + else if (fieldType.IsArray) + { + ImGui.Text($"{field.Name} Array"); + } + else if (fieldType.IsEnum) + { + ImGui.Text($"{field.Name} Enum"); + } + else if (fieldType.IsPrimitive) + { + ImGui.Text($"{field.Name} Primitive"); + + if (reference != null) + { + if (fieldType == typeof(float)) + { + float f = (float)field.GetValue(reference); + if (ImGui.DragFloat(field.Name, ref f)) + { + field.SetValue(reference, f); + } + } + } + } + else if (fieldType.IsValueType) + { + // Struct + if (ImGui.TreeNode(field.Name)) + { + object @struct = (reference != null) ? field.GetValue(reference) : null; + + ShowEditor(fieldType, @struct); + + if (@struct != null) + { + field.SetValue(reference, @struct); + } + + ImGui.TreePop(); + } + + } + else if (fieldType.IsSubclassOf(typeof(Entity)) || fieldType == typeof(Entity)) + { + // TODO: Show entity drop target + ImGui.Text($"{field.Name} Entity ({fieldType.Name})"); + } + else if (fieldType.IsSubclassOf(typeof(Component))) + { + // TODO: Show component drop target + ImGui.Text($"{field.Name} Component ({fieldType.Name})"); + } + else + { + if (ImGui.TreeNode(field.Name)) + { + // TODO: Button to create / destroy instance + + object @class = (reference != null) ? field.GetValue(reference) : null; + + ShowEditor(fieldType, @class); + ImGui.TreePop(); + } + } + } + + ImGui.PopID(); + } + } +} diff --git a/ScriptCore/Extensions/EnumExtension.cs b/ScriptCore/Extensions/EnumExtension.cs new file mode 100644 index 0000000..9b5d3ea --- /dev/null +++ b/ScriptCore/Extensions/EnumExtension.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Linq; + +namespace GlitchyEngine.Extensions; + +public static class EnumExtension +{ + public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable self) + => self.Select((item, index) => (item, index)); +} \ No newline at end of file diff --git a/ScriptCore/ScriptCore.csproj b/ScriptCore/ScriptCore.csproj index aa5eb63..9bdf54c 100644 --- a/ScriptCore/ScriptCore.csproj +++ b/ScriptCore/ScriptCore.csproj @@ -18,6 +18,7 @@ + diff --git a/ScriptCore/ScriptCore.sln b/ScriptCore/ScriptCore.sln index d692952..09b3350 100644 --- a/ScriptCore/ScriptCore.sln +++ b/ScriptCore/ScriptCore.sln @@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33723.286 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCore", "ScriptCore.csproj", "{454CBEB0-2425-47B4-B75B-BE11EAF56E34}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCore", "ScriptCore.csproj", "{454CBEB0-2425-47B4-B75B-BE11EAF56E34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCoreGenerator", "..\ScriptCoreGenerator\ScriptCoreGenerator.csproj", "{5E8CE036-B41E-4382-AC53-7F9A39ED529D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET", "..\vendor\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj", "{9D60C9CF-044C-4F2D-ADF0-B5A60BE3DADB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +19,14 @@ Global {454CBEB0-2425-47B4-B75B-BE11EAF56E34}.Debug|Any CPU.Build.0 = Debug|Any CPU {454CBEB0-2425-47B4-B75B-BE11EAF56E34}.Release|Any CPU.ActiveCfg = Release|Any CPU {454CBEB0-2425-47B4-B75B-BE11EAF56E34}.Release|Any CPU.Build.0 = Release|Any CPU + {5E8CE036-B41E-4382-AC53-7F9A39ED529D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E8CE036-B41E-4382-AC53-7F9A39ED529D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E8CE036-B41E-4382-AC53-7F9A39ED529D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E8CE036-B41E-4382-AC53-7F9A39ED529D}.Release|Any CPU.Build.0 = Release|Any CPU + {9D60C9CF-044C-4F2D-ADF0-B5A60BE3DADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D60C9CF-044C-4F2D-ADF0-B5A60BE3DADB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D60C9CF-044C-4F2D-ADF0-B5A60BE3DADB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D60C9CF-044C-4F2D-ADF0-B5A60BE3DADB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/vendor/ImGui.NET b/vendor/ImGui.NET new file mode 160000 index 0000000..10d76c3 --- /dev/null +++ b/vendor/ImGui.NET @@ -0,0 +1 @@ +Subproject commit 10d76c3e726a0343fa671f179b7dbeb33f07e8a7