diff --git a/GlitchyEditor/resources/Textures/EditorIcons.dds b/GlitchyEditor/resources/Textures/EditorIcons.dds index 8d3a23a..3adbe21 100644 Binary files a/GlitchyEditor/resources/Textures/EditorIcons.dds and b/GlitchyEditor/resources/Textures/EditorIcons.dds differ diff --git a/GlitchyEditor/resources/Textures/EditorIcons.psd b/GlitchyEditor/resources/Textures/EditorIcons.psd index bd55d98..4f8c10b 100644 Binary files a/GlitchyEditor/resources/Textures/EditorIcons.psd and b/GlitchyEditor/resources/Textures/EditorIcons.psd differ 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