From 6d502087000359f74e43ed2f6d4c6c58bb7358b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 3 Mar 2025 20:29:35 +0100 Subject: [PATCH] Start of material editor --- .../resources/Textures/EditorIcons.dds | Bin 360576 -> 393344 bytes .../resources/Textures/EditorIcons.psd | Bin 93705 -> 99837 bytes .../src/Assets/Editors/MaterialEditor.bf | 169 ++++++++++ .../src/Assets/MaterialAssetLoader.bf | 309 ------------------ .../src/EditWindows/ComponentEditWindow.bf | 9 +- .../src/EditWindows/ContentBrowserWindow.bf | 20 +- .../src/EditWindows/InspectorWindow.bf | 23 +- GlitchyEditor/src/EditorIcons.bf | 4 + GlitchyEditor/src/ImGuiExtension.bf | 5 + GlitchyEngine/src/ImGui/ImGuiExtension.bf | 87 +++-- GlitchyEngine/src/Renderer/Material.bf | 12 +- .../Serialization/DeserializationObject.cs | 36 +- 12 files changed, 304 insertions(+), 370 deletions(-) create mode 100644 GlitchyEditor/src/Assets/Editors/MaterialEditor.bf diff --git a/GlitchyEditor/resources/Textures/EditorIcons.dds b/GlitchyEditor/resources/Textures/EditorIcons.dds index 8d3a23a65712e81efc083f2d5d5f14b45ebd3115..3adbe215e46f3ffc63c517fabc9d4e0a54cf8ba2 100644 GIT binary patch delta 4037 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 diff --git a/GlitchyEditor/resources/Textures/EditorIcons.psd b/GlitchyEditor/resources/Textures/EditorIcons.psd index bd55d9815b50ed60ce85c81ed5add3626f696b2d..4f8c10bfd36836e1150ba26688dfc3946a0fd8a0 100644 GIT binary patch delta 8553 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 delta 3011 zcmZ{m3shCd6^7@>iZ@iQAc9yyBtB}CqF^)p)3#`1NTuV#Wu-1$RGki0qunFcg3ur)z8&_wMSK{_f@^Brsr*1e^7sd6At*GgYuvq zfN=PzFI1DdrcN{F19g(|wW>y)PU}3oL0Z%F^;QW@rJg04=`D|xMN@L%=>@%0O{&gwc50DRiSpMqrC4odHUDXPpMVv zqWZPTiq60#lL23D>Hkm$Jly^&H>*--c+6U=&Ontqtm;e#N_mr+>iz-r{fDS-DvIq% zHByzUCEk{=^3_td4EFO?IyJ-e^HnKlmGXEMJZz=f&Hp#J^mesby`o;`e~DVbnVBY{ zNaZo%*(!@SrTy7#d0cUp8e{fs!)G9HAw>S1hW3`?glh?vq@9;X|?+18w-qzRO-af*Z?a3Hqeel^b zn&U=t*O%`s)idt?jE)1N==kz(J{`rM|Avm^VHjrpsBsO=h_Oht=6dotcE!WxWIzN) zSf4a4q1h6FiB{zo1vEeHjwx2bKgwvHi^MD|y}1aDhZ>vxLC@_T^rZf4FHOH*m~Jip z_Z|*L-gD}~G2g`mtL#6OG-IYB#VY=?292+~TR3L9TY_X)UktZ)e|5^+z0i+oIQI2H zPX9OxDVxtgM-~7cN|6u#N?9@!iSo-fc!~D-Z{Y|N8PXPKDLZ44AhEu9k#@Z=-l3!v zB3O2`<1!u@<3KgV@d)ZE#|C4#1p8q=?cygeTHg0V8kda!HmWGM=OIo8w8wL_YvV9T z8f?42Kh`m#-XGbhm7z}L@bPY&kACv06OZDkxZ=^3Eci)kJc3czipcT~=p`r15GMbO zNA!Qe6eQsR2$TT<=xMVw(B`P890@>>4U$U&0ugPKlq?$pF`0)TN^Xhjhy>bXshCIA z9LcaC5S;rUMay4|gCr%(*Gce|l1>=UjGw>hmhQoLfo9>YWcerNE`nQYR|~tJ~+l@^_I!^hQX<Xnt(HPk_6mFZ-XbFr*51Z3?;u#ab-agqMkH`KGo;_uetCh^| zQtcv=rp!HIlbL2iX>nWfsBc%7HMyEdEDevVD6KVZA(MWe!kuo7X)A@k5;u&8w^6)0 z47w(HQS_BP!?8fG)|n4s{s_$Dn1-@*vdIRa+|5x|zBf4Fyc;KjMilLXl zv9fiHMo(j?tErbjhYTF2Uj>cfc)2wW>9on>({gaU;hjJ}E9Dc+i$N;CFS!$;i>en* zhoq)pCu7LxJjtAdpU@_w&rAMOc#ZSCoI0drGJe4^!a7YhOu-J)$J*#?18HE@h22{n z$H|*d<6Uo`tR~B;XTSoh>*F*zJ6(tC0%@Ri$cN98L@&9gXJ9LBl6!(;pe=fiud5f@ zk+V#p{A3niod?i5qxpcfWm+3apPLg|b(Oyoz zfSKI-0lq8dfe7g0x|y;*75uc>?s9lYZMXzwUi~?j(E*0?PO)_PT5mvWgxx9wYu}K%UWt6jpd<~mqbP<+N>Whd= zlP+DsGQODK*CweeHmfI>nAJ~}aGpmJ*P7{v*P1m3mojfhWq&EZxOEa#hS7XVsb%QL MUs+oz_lr~LH+Z8)r2qf` diff --git a/GlitchyEditor/src/Assets/Editors/MaterialEditor.bf b/GlitchyEditor/src/Assets/Editors/MaterialEditor.bf new file mode 100644 index 0000000..1bb952e --- /dev/null +++ b/GlitchyEditor/src/Assets/Editors/MaterialEditor.bf @@ -0,0 +1,169 @@ +using GlitchyEngine.Renderer; +using ImGui; +using System; +using GlitchyEngine.Math; +using GlitchyEditor.EditWindows; +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Editors; + +class MaterialEditor +{ + public static void ShowEditor(AssetFile assetFile) + { + let material = assetFile.LoadedAsset as Material; + + if (material == null) + return; + + ImGui.PropertyTableStartNewProperty("Base Material"); + + if (material.Parent != null) + ImGui.TextUnformatted(material.Parent.Identifier); + else + ImGui.TextUnformatted("None"); + + ImGui.PropertyTableStartNewProperty("Shader"); + + ImGui.TextUnformatted(material.Effect.Identifier); + + ImGui.PropertyTableStartNewRow(); + if (ImGui.CollapsingHeader("Parameters", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns)) + { + for (let (variableName, bufferVariable) in material.Variables) + { + ImGui.PushID(variableName); + + ImGui.PropertyTableStartNewRow(); + + bool readOnly = bufferVariable.Flags.HasFlag(.Readonly); + + ImGui.BeginDisabled(readOnly); + + if (DrawLockButton(bufferVariable.Flags.HasFlag(.Locked)) && !readOnly) + { + bufferVariable.[Friend]_flags ^= .Locked; + } + + ImGui.SameLine(); + + StringView displayName = variableName; + + ImGui.PropertyTableName(displayName); + + // TODO: Somehow pass display name and type from imported shader to here! + + switch (bufferVariable.ElementType) + { + case .Float: + // TODO: min and max values specified in shader + // TODO: support drag and enter number (specified in shader) + + //for (int r < bufferVariable.Rows) + if (bufferVariable.Rows == 1) + { + switch (bufferVariable.Columns) + { + case 1: + material.GetVariable(bufferVariable.Name, var value); + + //float[1] minV = hasMin ? min.Get() : .(float.MinValue); + //float[1] maxV = hasMax ? max.Get() : .(float.MaxValue); + + if (ImGui.VectorEditor<1>("", ref *(float[1]*)&value, .(), 0.1f /*, minV, maxV*/)) + material.SetVariable(bufferVariable.Name, value); + case 2: + material.GetVariable(bufferVariable.Name, var value); + if (ImGui.Float2Editor("", ref value, .Zero, 0.1f, 100.0f)) + material.SetVariable(bufferVariable.Name, value); + case 3: + material.GetVariable(bufferVariable.Name, var value); + if (ImGui.Float3Editor("", ref value, .Zero, 0.1f, 100.0f)) + material.SetVariable(bufferVariable.Name, value); + case 4: + material.GetVariable(bufferVariable.Name, var value); + if (ImGui.Float4Editor("", ref value, .Zero, 0.1f, 100.0f)) + material.SetVariable(bufferVariable.Name, value); + } + } + default: + ImGui.TextUnformatted(scope $"Element Type {_} not supported"); + } + + ImGui.EndDisabled(); + + ImGui.PopID(); + } + } + + ImGui.PropertyTableStartNewRow(); + if (ImGui.CollapsingHeader("Textures", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns)) + { + for (var (textureName, texture) in ref material.Textures) + { + ImGui.PushID(textureName); + + ImGui.PropertyTableStartNewRow(); + + bool readOnly = texture.Flags.HasFlag(.Readonly); + + ImGui.BeginDisabled(readOnly); + + if (DrawLockButton(texture.Flags.HasFlag(.Locked)) && !readOnly) + { + texture.Flags ^= .Locked; + } + + ImGui.SameLine(); + + ImGui.PropertyTableName(textureName); + + if (ComponentEditWindow.ShowAssetDropTarget(ref texture.TextureHandle)) + { + + } + + ImGui.EndDisabled(); + + ImGui.PopID(); + } + } + } + + private static bool DrawLockButton(bool isLocked) + { + ImGui.PushStyleVar(.ItemInnerSpacing, ImGui.Vec2(0, 0)); + ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0)); + + let colors = ImGui.GetStyle().Colors; + + ImGui.Vec4 hoveredColor = colors[(int)ImGui.Col.ButtonHovered]; + hoveredColor.w = 0.5f; + + ImGui.Vec4 activeColor = colors[(int)ImGui.Col.ButtonActive]; + activeColor.w = 0.5f; + + ImGui.PushStyleColor(.ButtonHovered, hoveredColor); + ImGui.PushStyleColor(.ButtonActive, activeColor); + + float padding = 2.0f; + + float size = ImGui.GetTextLineHeight() - 2 * padding; + + ImGui.PushID(0); + + bool result = ImGui.ImageButton("", isLocked ? EditorIcons.Instance.Icon_Locked : EditorIcons.Instance.Icon_Unlocked, .(size, size), .Zero, .Ones); + + ImGui.PopID(); + + if (isLocked) + ImGui.AttachTooltip("Click to unlock. The property is locked and cannot be changed by children."); + else + ImGui.AttachTooltip("Click to lock. The property is unlocked and can be changed by children."); + + ImGui.PopStyleColor(3); + ImGui.PopStyleVar(1); + + return result; + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf index 4dc4dbe..749bd39 100644 --- a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf +++ b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf @@ -255,312 +255,3 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor return new Self(assetFile); } } - -[BonTarget, BonPolyRegister] -class MaterialAssetLoaderConfig : AssetLoaderConfig -{ - -} - -[BonTarget] -public enum VariableValue -{ - case bool(bool Value); - case bool2(bool2 Value); - case bool3(bool3 Value); - case bool4(bool4 Value); - case int(int Value); - case int2(int2 Value); - case int3(int3 Value); - case int4(int4 Value); - case uint(uint Value); - case uint2(uint2 Value); - case uint3(uint3 Value); - case uint4(uint4 Value); - case Float(float Value); - case Float2(float2 Value); - case Float3(float3 Value); - case Float4(float4 Value); - case ColorRGB(ColorRGB Value); - case ColorRGBA(ColorRGBA Value); - case None; - - /*static this() - { - gBonEnv.typeHandlers.Add(typeof(Self), - ((.)new => VariableValueSerialize, (.)new => VariableValueDeserialize)); - } - - static void VariableValueSerialize(BonWriter writer, ValueView value, BonEnvironment env) - { - Log.EngineLogger.Assert(value.type == typeof(Self)); - - let variableValue = value.Get(); - - writer.Type(variableValue) - using (writer.ObjectBlock()) - { - Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env); - Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env); - - - } - } - - static Result VariableValueDeserialize(BonReader reader, ValueView val, BonEnvironment env) - { - return .Ok; - }*/ -} - -[BonTarget] -class MaterialFile -{ - public String Effect ~ delete _; - - //public Dictionary Textures ~ DeleteDictionaryAndKeysAndValues!(_); - public Dictionary> Textures ~ DeleteDictionaryAndKeys!(_); - public Dictionary Variables ~ - { - if (_ != null) - { - for (var entry in _) - { - delete entry.key; - //delete entry.value; - /*if (entry.value.HasValue) - entry.value->Dispose();*/ - } - - delete _; - } - }; - - /*static this() - { - gBonEnv.typeHandlers.Add(typeof(Self), - ((.)new => MaterialSerialize, (.)new => MaterialDeserialize)); - } - - static void MaterialSerialize(BonWriter writer, ValueView value, BonEnvironment env) - { - Log.EngineLogger.Assert(value.type == typeof(Self)); - - let materialFile = value.Get(); - - using (writer.ObjectBlock()) - { - Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env); - Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env); - - - } - } - - private static void SerializeVariablesDictionary(BonWriter writer, MaterialFile materialFile, BonEnvironment env) - { - using (writer.ArrayBlock()) - { - for (let (name, value) in materialFile.Variables) - { - let keyVal = ValueView(typeof(String), name); - Serialize.Value(writer, keyVal, env); - writer.Pair(); - - ValueView valueVal;// = ValueView(, entriesPtr + (currentIndex * entryStride) + entryValueOffset); - switch(value.GetType()) - { - case typeof(ColorRGBA): - writer.Identifier("ColorRGBA"); - default: - - } - - Serialize.Value(writer, valueVal, env); - } - } - } - - static Result MaterialDeserialize(BonReader reader, ValueView val, BonEnvironment env) - { - return .Ok; - }*/ -} - -class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader -{ - private static readonly List _fileExtensions = new .(){".mat"} ~ delete _; - - public static List FileExtensions => _fileExtensions; - - public AssetLoaderConfig GetDefaultConfig() - { - return new ModelAssetLoaderConfig(); - } - - public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager) - { - StreamReader reader = scope .(file); - - String text = scope .(); - - reader.ReadToEnd(text); - - MaterialFile materialFile = scope .(); - - var result = Bon.Deserialize(ref materialFile, text); - - if (result case .Err) - { - Log.EngineLogger.Error("Failed to load material."); - Debug.SafeBreak(); - return null; - } - - Effect fx = Content.GetAsset(contentManager.LoadAsset(materialFile.Effect, true), contentManager); - - Material material = new Material(fx); - - for (let (slotName, textureHandle) in materialFile.Textures) - { - AssetHandle texture = contentManager.LoadAsset(textureHandle); - - if (texture.IsInvalid) - { - Log.EngineLogger.Error($"Failed to load texture \"{textureHandle}\"."); - } - - material.SetTexture(slotName, texture); - } - - for (let (slotName, variableValue) in materialFile.Variables) - { - switch (variableValue) - { - case .ColorRGBA(let value): - material.SetVariable(slotName, value); - case .ColorRGB(let value): - material.SetVariable(slotName, value); - case .Float(let value): - material.SetVariable(slotName, value); - case .Float2(let value): - material.SetVariable(slotName, value); - case .Float3(let value): - material.SetVariable(slotName, value); - case .Float4(let value): - material.SetVariable(slotName, value); - case .None: - default: - Log.EngineLogger.Error($"Unknown variable type of variable {slotName}: {variableValue}"); - } - - - } - - return material; - } - - public Result EditorSaveAsset(Stream file, Asset asset, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager) - { - Material material = asset as Material; - - if (material == null) - { - Log.EngineLogger.Error("Asset must be a Material!"); - return .Err; - } - - MaterialFile materialFile = scope .(); - - materialFile.Effect = new String(material.Effect?.Identifier ?? ""); - materialFile.Textures = new .(); - materialFile.Variables = new .(); - - // TODO: Fix - /*for (let (slotName, texture) in material.[Friend]_textures) - { - materialFile.Textures.Add(new String(slotName), texture.Handle); - }*/ - - Effect effect = material.Effect; - - if (effect != null) - { - for (let (name, arguments) in effect.[Friend]_variableDescriptions) - { - VariableValue variableValue = .None; - - let variable = effect.Variables[name]; - - bool hasPreviewType = MaterialAssetPropertiesEditor.TryGetValue(arguments, "Type", var previewType); - - if (hasPreviewType && previewType.Get() == "Color") - { - Log.EngineLogger.AssertDebug(variable.ElementType == .Float && variable.Rows == 1); - - if (variable.Columns == 3) - { - material.GetVariable(variable.Name, var value); - - value = ColorRGB.LinearToSRGB((ColorRGB)value); - - //variantValue = new box value; - variableValue = .ColorRGB(value); - } - else if (variable.Columns == 4) - { - material.GetVariable(variable.Name, var value); - - value = ColorRGBA.LinearToSRGB((ColorRGBA)value); - - variableValue = .ColorRGBA(value); - } - } - else if (variable.ElementType == .Float && variable.Rows == 1) - { - switch (variable.Columns) - { - case 1: - material.GetVariable(variable.Name, let value); - variableValue = .Float(value); - case 2: - material.GetVariable(variable.Name, let value); - variableValue = .Float2(value); - case 3: - material.GetVariable(variable.Name, let value); - variableValue = .Float3(value); - case 4: - material.GetVariable(variable.Name, let value); - variableValue = .Float4(value); - } - } - - materialFile.Variables.Add(new String(name), variableValue); - } - } - - String text = scope .(); - - gBonEnv.serializeFlags |= .IncludeDefault | .Verbose; - - Bon.Serialize(materialFile, text); - - StreamWriter writer = scope .(file, .UTF8, 1024); - writer.Write(text); - - return .Ok; - } - - Material _placeholder; - Material _error; - - public Asset GetPlaceholderAsset(Type assetType) - { - return default; - } - - public Asset GetErrorAsset(Type assetType) - { - return default; - } -} \ No newline at end of file diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index 4ec27c4..91f2cf2 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -504,7 +504,7 @@ namespace GlitchyEditor.EditWindows ShowAssetDropTarget(ref spriteRendererComponent.Material); } - private static bool ShowAssetDropTarget(ref AssetHandle target) + public static bool ShowAssetDropTarget(ref AssetHandle target) { bool changed = false; @@ -539,8 +539,10 @@ namespace GlitchyEditor.EditWindows return changed; } - private static void ShowAssetDropTarget(ref AssetHandle target) where T : Asset + public static bool ShowAssetDropTarget(ref AssetHandle target) where T : Asset { + bool changed = false; + T currentAsset = target.Get(); StringView identifier = (target.IsValid ? "" : "None"); @@ -565,10 +567,13 @@ namespace GlitchyEditor.EditWindows // TODO: Somehow validate the type, please! target = (AssetHandle)Content.LoadAsset(path); + changed = true; } ImGui.EndDragDropTarget(); } + + return changed; } private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent) diff --git a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf index 83be384..b9cd06f 100644 --- a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf +++ b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf @@ -637,7 +637,10 @@ namespace GlitchyEditor.EditWindows if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left)) { - OpenEntry(entry); + if (ImGui.IsKeyDown(.LeftCtrl)) + OpenPropertiesWindow(entry); + else + OpenEntry(entry); } if (_assetToRename == entry->Path) @@ -870,12 +873,17 @@ namespace GlitchyEditor.EditWindows if (ImGui.MenuItem("Properties...")) { - AssetHandle? assetHandle = fileOrFolder->AssetFile?.AssetConfig?.AssetHandle; + OpenPropertiesWindow(fileOrFolder); + } + } - if (assetHandle != null) - { - new PropertiesWindow(_editor, .Asset(assetHandle.Value)); - } + private void OpenPropertiesWindow(TreeNode fileOrFolder) + { + AssetHandle? assetHandle = fileOrFolder->AssetFile?.AssetConfig?.AssetHandle; + + if (assetHandle != null) + { + new PropertiesWindow(_editor, .Asset(assetHandle.Value)); } } diff --git a/GlitchyEditor/src/EditWindows/InspectorWindow.bf b/GlitchyEditor/src/EditWindows/InspectorWindow.bf index 3c5ec27..06d2c57 100644 --- a/GlitchyEditor/src/EditWindows/InspectorWindow.bf +++ b/GlitchyEditor/src/EditWindows/InspectorWindow.bf @@ -8,6 +8,8 @@ using GlitchyEditor.Assets; using GlitchyEngine.Core; using System.Collections; using GlitchyEngine.World; +using GlitchyEngine.Renderer; +using GlitchyEditor.Assets.Editors; namespace GlitchyEditor.EditWindows; @@ -140,25 +142,42 @@ class InspectorWindow : EditorWindow AssetFile assetFile = assetNode->Value.AssetFile; + // TODO: assetFile.loadedAsset has the asset + // TODO: we need to manually load it + // TODO: if available, show editor for asset + // TODO: if asset changed, show save button? + if (ImGui.BeginPropertyTable("asset_properties", ImGui.GetID("asset_properties"))) { assetFile.AssetConfig?.ImporterConfig?.ShowEditor(assetFile); assetFile.AssetConfig?.ProcessorConfig?.ShowEditor(assetFile); assetFile.AssetConfig?.ExporterConfig?.ShowEditor(assetFile); + if (assetFile.LoadedAsset != null) + { + // TODO: Where do we get the asset editor from? + if (let mat = assetFile.LoadedAsset as Material) + { + MaterialEditor.ShowEditor(assetFile); + //ImGui.TextUnformatted(assetFile.LoadedAsset.Identifier); + } + } + ImGui.EndTable(); ImGui.Separator(); } - bool hasChanges = (assetFile.AssetConfig?.ImporterConfig?.Changed ?? false) || (assetFile.AssetConfig?.ProcessorConfig?.Changed ?? false) || (assetFile.AssetConfig?.ExporterConfig?.Changed ?? false); + bool hasChanges = (assetFile.AssetConfig?.ImporterConfig?.Changed == true) || + (assetFile.AssetConfig?.ProcessorConfig?.Changed == true) || + (assetFile.AssetConfig?.ExporterConfig?.Changed == true); if (!hasChanges) ImGui.BeginDisabled(); if (ImGui.Button("Apply")) { - assetFile.SaveAssetConfig(); + assetFile.SaveAssetConfigIfChanged(); } if (!hasChanges) diff --git a/GlitchyEditor/src/EditorIcons.bf b/GlitchyEditor/src/EditorIcons.bf index e94959b..2c1e9fa 100644 --- a/GlitchyEditor/src/EditorIcons.bf +++ b/GlitchyEditor/src/EditorIcons.bf @@ -34,6 +34,8 @@ namespace GlitchyEditor public SubTexture2D Icon_Save ~ _.ReleaseRef(); public SubTexture2D Icon_ContextMenu ~ _.ReleaseRef(); public SubTexture2D File_Hlsl ~ _.ReleaseRef(); + public SubTexture2D Icon_Locked ~ _.ReleaseRef(); + public SubTexture2D Icon_Unlocked ~ _.ReleaseRef(); public static EditorIcons Instance => _editorIcons; @@ -73,6 +75,8 @@ namespace GlitchyEditor Icon_Save = GetNextGridTexture(ref pen, iconSize); Icon_ContextMenu = GetNextGridTexture(ref pen, iconSize); File_Hlsl = GetNextGridTexture(ref pen, iconSize); + Icon_Locked = GetNextGridTexture(ref pen, iconSize); + Icon_Unlocked = GetNextGridTexture(ref pen, iconSize); } public ~this() diff --git a/GlitchyEditor/src/ImGuiExtension.bf b/GlitchyEditor/src/ImGuiExtension.bf index c2f31f2..668a427 100644 --- a/GlitchyEditor/src/ImGuiExtension.bf +++ b/GlitchyEditor/src/ImGuiExtension.bf @@ -54,6 +54,11 @@ extension ImGui { PropertyTableStartNewRow(); + PropertyTableName(propertyName); + } + + public static void PropertyTableName(StringView propertyName) + { bool isFirstTableRow = ImGui.TableGetRowIndex() == 0; if (isFirstTableRow) diff --git a/GlitchyEngine/src/ImGui/ImGuiExtension.bf b/GlitchyEngine/src/ImGui/ImGuiExtension.bf index 3d4625a..472b9d8 100644 --- a/GlitchyEngine/src/ImGui/ImGuiExtension.bf +++ b/GlitchyEngine/src/ImGui/ImGuiExtension.bf @@ -176,7 +176,7 @@ namespace ImGui (.(55, 55, 230), .(55, 55, 150), .(90, 90, 230)), (.(230, 25, 45), .(230, 25, 45), .(230, 25, 45))); - public static bool EditVector(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float columnWidth = 100f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .()) where NumComponents : const int32 + public static bool EditVector(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float columnWidth = 100f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(true, )) where NumComponents : const int32 { const String[?] componentNames = .("X", "Y", "Z", "W"); const String[?] componentIds = .("##X", "##Y", "##Z", "##W"); @@ -185,27 +185,36 @@ namespace ImGui bool changed = false; bool deactivated = false; - - PushID(label); - defer PopID(); - int currentId = ImGui.GetID(""); + if (!label.IsEmpty) + { + PushID(label); + defer:: PopID(); + + Columns(2); + defer:: Columns(1); + SetColumnWidth(0, columnWidth); - Columns(2); - defer Columns(1); - SetColumnWidth(0, columnWidth); + //int currentId = ImGui.GetID(""); + + TextUnformatted(label); - TextUnformatted(label); - - NextColumn(); - - PushMultiItemsWidths(NumComponents, CalcItemWidth()); - + NextColumn(); + } + float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f; ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight); - - PushStyleVar(.ItemSpacing, Vec2.Zero); + + float itemWidth = CalcItemWidth(); + + float itemWidthNoButton = itemWidth - buttonSize.y * NumComponents; + + bool hideButtons = (itemWidthNoButton / NumComponents) < ImGui.CalcTextSize("0.000").x; + PushMultiItemsWidths(NumComponents, hideButtons ? itemWidth : itemWidthNoButton); + + PushStyleVar(.ItemSpacing, Vec2.Zero); + for (int i < NumComponents) { if (i > 0) @@ -219,13 +228,16 @@ namespace ImGui ImGui.BeginDisabled(!componentEnabled[i]); - if (Button(componentNames[i], buttonSize)) + if (!hideButtons) { - value[i] = resetValues[i]; - changed = true; + if (Button(componentNames[i], buttonSize)) + { + value[i] = resetValues[i]; + changed = true; + } + + SameLine(); } - - SameLine(); if (DragFloat(componentIds[i], &value[i], dragSpeed, minValue[i], maxValue[i])) { @@ -278,7 +290,7 @@ namespace ImGui return VectorEditor<4>(label, ref *(float[4]*)&value, (float[4])resetValues, dragSpeed, (float[4])minValue, (float[4])maxValue, (bool[4])componentEnabled, format); } - public static bool VectorEditor(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(), StringView[NumComponents] numberFormat = .()) where NumComponents : const int32 + public static bool VectorEditor(StringView label, ref float[NumComponents] value, float[NumComponents] resetValues = .(), float dragSpeed = 0.1f, float[NumComponents] minValue = .(), float[NumComponents] maxValue = .(), bool[NumComponents] componentEnabled = .(true, ), StringView[NumComponents] numberFormat = .()) where NumComponents : const int32 { const String[?] componentNames = .("X", "Y", "Z", "W"); const String[?] componentIds = .("##X", "##Y", "##Z", "##W"); @@ -297,7 +309,16 @@ namespace ImGui float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f; ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight); - float dragFloatWidth = componentWidth - buttonSize.x - GetStyle().FramePadding.x; + float dragFloatWidth = componentWidth - buttonSize.x; + + bool showButtons = dragFloatWidth > CalcTextSize("0.000").x; + + if (!showButtons) + { + dragFloatWidth = componentWidth; + } + + dragFloatWidth -= GetStyle().FramePadding.x * 2; componentLoop: for (int i < NumComponents) { @@ -314,16 +335,19 @@ namespace ImGui //PushItemWidth(buttonSize.x); - if (Button(componentNames[i], buttonSize)) + if (showButtons) { - value[i] = resetValues[i]; - changed = true; + if (Button(componentNames[i], buttonSize)) + { + value[i] = resetValues[i]; + changed = true; + } + + PushStyleVar(.ItemSpacing, Vec2.Zero); + + SameLine(); } - PushStyleVar(.ItemSpacing, Vec2.Zero); - - SameLine(); - StringView format = "0.#####"; if (!numberFormat[i].IsWhiteSpace) @@ -356,7 +380,8 @@ namespace ImGui PopItemWidth(); - PopStyleVar(); + if (showButtons) + PopStyleVar(); EndDisabled(); diff --git a/GlitchyEngine/src/Renderer/Material.bf b/GlitchyEngine/src/Renderer/Material.bf index 47752b8..b82e22e 100644 --- a/GlitchyEngine/src/Renderer/Material.bf +++ b/GlitchyEngine/src/Renderer/Material.bf @@ -27,6 +27,9 @@ public class Material : Asset private BufferCollection _bufferCollection ~ _?.ReleaseRef(); + public Dictionary Variables => _variables; + public TextureCollection Textures => _textureCollection; + public Effect Effect { get => _effect; @@ -313,7 +316,7 @@ public class Material : Asset } } -public class TextureCollection +public class TextureCollection : IEnumerable<(String key, TextureEntry value)> { public enum TextureFlags { @@ -328,7 +331,7 @@ public class TextureCollection Readonly = 0x8 } - private struct TextureEntry + public struct TextureEntry { public AssetHandle TextureHandle; public int32? groupTarget; @@ -465,4 +468,9 @@ public class TextureCollection } protected extern Result SetTexturePlatform(); + + public Dictionary.Enumerator GetEnumerator() + { + return _entries.GetEnumerator(); + } } \ No newline at end of file diff --git a/ScriptCore/Serialization/DeserializationObject.cs b/ScriptCore/Serialization/DeserializationObject.cs index 119bde2..b490cc3 100644 --- a/ScriptCore/Serialization/DeserializationObject.cs +++ b/ScriptCore/Serialization/DeserializationObject.cs @@ -150,9 +150,19 @@ public class DeserializationObject [StructLayout(LayoutKind.Explicit)] private struct DataHelper { + [StructLayout(LayoutKind.Sequential)] + public unsafe struct StringView + { + public byte* Utf8Ptr; + public long Length; + } + [FieldOffset(0)] public EngineObjectReferenceHelper EngineObjectReference; + [FieldOffset(0)] + public StringView String; + [FieldOffset(0)] public ulong UInt; @@ -172,35 +182,25 @@ public class DeserializationObject { string completeFieldName = $"{_structScopeName}{fieldName}"; - // Decimal is the larges primitive we store so we use a decimal as stack allocated memory (because stackalloc doesn't seem to work :( - //decimal backingFieldOnStack = 0.0m; - //byte* rawData = (byte*)&backingFieldOnStack; - - //byte* rawData = stackalloc byte[sizeof(DataHelper)]; - //ref DataHelper dataHelper = ref Unsafe.AsRef(rawData); - DataHelper dataHelper = new(); byte* rawData = (byte*)Unsafe.AsPointer(ref dataHelper); - // //byte* rawData = stackalloc byte[sizeof(DataHelper)]; - // //ref DataHelper dataHelper = ref Unsafe.AsRef(rawData); - ScriptGlue.Serialization_DeserializeField(_internalContext, expectedType, completeFieldName, rawData, out SerializationType actualType); string? GetString() { - // rawData contains a Pointer and a string length! - byte* utf8Ptr = *(byte**)rawData; - - if (utf8Ptr == null) + if (dataHelper.String.Utf8Ptr == null) return null; - - ulong length = *(ulong*)(rawData + 8); - if (length == 0) + if (dataHelper.String.Length == 0) return string.Empty; - return Encoding.UTF8.GetString(utf8Ptr, (int)length); + if (dataHelper.String.Length is < 0 or > int.MaxValue) + { + throw new InvalidOperationException($"String length is invalid: {dataHelper.String.Length}"); + } + + return Encoding.UTF8.GetString(dataHelper.String.Utf8Ptr, (int)dataHelper.String.Utf8Ptr); } object? value = actualType switch