From 86afa234e70945bb02abd6a39ca8b05efbbf608a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 8 Jun 2024 17:03:21 +0200 Subject: [PATCH] Texture processor cleanup, new asset properties config UI --- .../resources/Textures/EditorIcons.dds.ass | 2 +- .../resources/Textures/ErrorTexture2D.png.ass | 2 +- .../Textures/PlaceholderTexture2D.png.ass | 2 +- GlitchyEditor/src/Assets/AssetConverter.bf | 1 + .../src/Assets/Exporters/TextureExporter.bf | 110 ++++ GlitchyEditor/src/Assets/Importers/Config.bf | 21 + .../src/Assets/Importers/ImportedResource.bf | 15 + .../src/Assets/Importers/Interfaces.bf | 1 + .../src/Assets/Importers/TextureImporter.bf | 470 +----------------- .../Assets/Processors/ProcessedResource.bf | 17 + .../src/Assets/Processors/TextureProcessor.bf | 451 +++++++++++++++++ .../src/EditWindows/ComponentEditWindow.bf | 143 +++--- .../src/EditWindows/PropertiesWindow.bf | 33 +- GlitchyEditor/src/EditorApp.bf | 2 + GlitchyEditor/src/EditorContentManager.bf | 36 +- GlitchyEditor/src/ImGuiExtension.bf | 39 ++ 16 files changed, 793 insertions(+), 552 deletions(-) create mode 100644 GlitchyEditor/src/Assets/Exporters/TextureExporter.bf create mode 100644 GlitchyEditor/src/Assets/Importers/ImportedResource.bf create mode 100644 GlitchyEditor/src/Assets/Processors/ProcessedResource.bf create mode 100644 GlitchyEditor/src/Assets/Processors/TextureProcessor.bf diff --git a/GlitchyEditor/resources/Textures/EditorIcons.dds.ass b/GlitchyEditor/resources/Textures/EditorIcons.dds.ass index efb64cf..7d720ed 100644 --- a/GlitchyEditor/resources/Textures/EditorIcons.dds.ass +++ b/GlitchyEditor/resources/Textures/EditorIcons.dds.ass @@ -6,7 +6,7 @@ _isSrgb = false }, Processor = "TextureProcessor", - ProcessorConfig = (GlitchyEditor.Assets.Importers.TextureProcessorConfig){ + ProcessorConfig = (GlitchyEditor.Assets.Processors.TextureProcessorConfig){ _generateMipMaps = .No, _samplerStateDescription = { MinFilter = .Linear, diff --git a/GlitchyEditor/resources/Textures/ErrorTexture2D.png.ass b/GlitchyEditor/resources/Textures/ErrorTexture2D.png.ass index 5a00083..71f3761 100644 --- a/GlitchyEditor/resources/Textures/ErrorTexture2D.png.ass +++ b/GlitchyEditor/resources/Textures/ErrorTexture2D.png.ass @@ -6,7 +6,7 @@ _isSrgb = false }, Processor = "TextureProcessor", - ProcessorConfig = (GlitchyEditor.Assets.Importers.TextureProcessorConfig){ + ProcessorConfig = (GlitchyEditor.Assets.Processors.TextureProcessorConfig){ _generateMipMaps = .No, _samplerStateDescription = { MinFilter = .Point, diff --git a/GlitchyEditor/resources/Textures/PlaceholderTexture2D.png.ass b/GlitchyEditor/resources/Textures/PlaceholderTexture2D.png.ass index f6bf7e4..1a77dbe 100644 --- a/GlitchyEditor/resources/Textures/PlaceholderTexture2D.png.ass +++ b/GlitchyEditor/resources/Textures/PlaceholderTexture2D.png.ass @@ -6,7 +6,7 @@ _isSrgb = false }, Processor = "TextureProcessor", - ProcessorConfig = (GlitchyEditor.Assets.Importers.TextureProcessorConfig){ + ProcessorConfig = (GlitchyEditor.Assets.Processors.TextureProcessorConfig){ _generateMipMaps = .No, _samplerStateDescription = { MinFilter = .Linear, diff --git a/GlitchyEditor/src/Assets/AssetConverter.bf b/GlitchyEditor/src/Assets/AssetConverter.bf index 115e7ab..f52a22d 100644 --- a/GlitchyEditor/src/Assets/AssetConverter.bf +++ b/GlitchyEditor/src/Assets/AssetConverter.bf @@ -4,6 +4,7 @@ using GlitchyEngine; using System; using System.IO; using GlitchyEngine.Content; +using GlitchyEditor.Assets.Processors; namespace GlitchyEditor.Assets; diff --git a/GlitchyEditor/src/Assets/Exporters/TextureExporter.bf b/GlitchyEditor/src/Assets/Exporters/TextureExporter.bf new file mode 100644 index 0000000..d9cf8ce --- /dev/null +++ b/GlitchyEditor/src/Assets/Exporters/TextureExporter.bf @@ -0,0 +1,110 @@ +using System; +using System.IO; +using GlitchyEngine.Renderer; +using GlitchyEditor.Assets.Processors; +using GlitchyEngine; +using GlitchyEditor.Assets.Importers; +namespace GlitchyEditor.Assets.Exporters; + + +class TextureExporter : IAssetExporter +{ + public AssetExporterConfig CreateDefaultConfig() + { + return new AssetExporterConfig(); + } + + public Result Export(Stream stream, ProcessedResource processedResource, AssetExporterConfig config) + { + Log.EngineLogger.AssertDebug(processedResource is ProcessedTexture); + + ProcessedTexture processedTexture = (.)processedResource; + + /* + + File Format: + TextureType (1 byte) + Pixel Format (4 bytes) + Width of larges mip-slice (4 bytes) + Height of larges mip-slice (4 bytes) + Depth of larges mip-slice (4 bytes) + Array size (4 bytes) + Mip map levels (4 bytes) + Is Cubemap (1 byte) + Data Byte count (8 bytes) + Pixeldata + { + Array[0]: Mip[0] Mip[1] ... Mip[M] + Array[1]: Mip[0] Mip[1] ... Mip[M] + ... + Array[N]: Mip[0] Mip[1] ... Mip[M] + } + + */ + + Try!(stream.Write(processedTexture.Dimension)); + Try!(stream.Write(processedTexture.PixelFormat)); + Try!(stream.Write((uint32)processedTexture.Width)); + Try!(stream.Write((uint32)processedTexture.Height)); + Try!(stream.Write((uint32)processedTexture.Depth)); + Try!(stream.Write((uint32)processedTexture.ArraySize)); + Try!(stream.Write((uint32)processedTexture.MipMapCount)); + Try!(stream.Write(processedTexture.IsCubeMap)); + + Try!(WriteSamplerStateDescription(stream, processedTexture.SamplerStateDescription)); + + for (int arraySlice < processedTexture.ArraySize) + { + int validateWidth = processedTexture.Width; + int validateHeight = processedTexture.Height; + int validateDepth = processedTexture.Depth; + + for (int mipSlice < processedTexture.MipMapCount) + { + ProcessedTexture.TextureSurface slice = processedTexture.Surfaces[arraySlice, mipSlice]; + + Log.EngineLogger.AssertDebug(validateWidth == slice.Width); + Log.EngineLogger.AssertDebug(validateHeight == slice.Height); + Log.EngineLogger.AssertDebug(validateDepth == slice.Depth); + validateWidth /= 2; + validateHeight /= 2; + validateDepth /= 2; + + if (validateWidth < 1) + validateWidth = 1; + + if (validateHeight < 1) + validateHeight = 1; + + if (validateDepth < 1) + validateDepth = 1; + + Try!(stream.Write((uint32)slice.LinePitch)); + Try!(stream.Write((uint32)slice.SlicePitch)); + Try!(stream.Write((uint64)slice.PixelData.Count)); + Try!(stream.TryWrite(slice.PixelData)); + } + } + + return .Ok; + } + + private Result WriteSamplerStateDescription(Stream stream, SamplerStateDescription sampler) + { + Try!(stream.Write(sampler.MinFilter)); + Try!(stream.Write(sampler.MagFilter)); + Try!(stream.Write(sampler.MipFilter)); + Try!(stream.Write(sampler.FilterMode)); + Try!(stream.Write(sampler.ComparisonFunction)); + Try!(stream.Write(sampler.AddressModeU)); + Try!(stream.Write(sampler.AddressModeV)); + Try!(stream.Write(sampler.AddressModeW)); + Try!(stream.Write(sampler.MipLODBias)); + Try!(stream.Write(sampler.MipMinLOD)); + Try!(stream.Write(sampler.MipMaxLOD)); + Try!(stream.Write(sampler.MaxAnisotropy)); + Try!(stream.Write(sampler.BorderColor)); + + return .Ok; + } +} diff --git a/GlitchyEditor/src/Assets/Importers/Config.bf b/GlitchyEditor/src/Assets/Importers/Config.bf index 0eeb680..86bdad1 100644 --- a/GlitchyEditor/src/Assets/Importers/Config.bf +++ b/GlitchyEditor/src/Assets/Importers/Config.bf @@ -1,5 +1,6 @@ using Bon; using GlitchyEngine.Content; +using ImGui; namespace GlitchyEditor.Assets.Importers; @@ -21,18 +22,26 @@ abstract class Config return true; } + + public abstract void ShowEditor(); } [BonTarget, BonPolyRegister] class AssetImporterConfig : Config { + public override void ShowEditor() + { + } } [BonTarget, BonPolyRegister] class AssetProcessorConfig : Config { + public override void ShowEditor() + { + } } [BonTarget, BonPolyRegister] @@ -46,4 +55,16 @@ class AssetExporterConfig : Config get => _compression; set => SetIfChanged(ref _compression, value); } + + public override void ShowEditor() + { + ImGui.PropertyTableStartNewProperty("Compression"); + ImGui.AttachTooltip("Specifies the compression method used to compress the processed asset."); + + AssetCompression compression = _compression; + if (ImGui.EnumCombo("##Compression", ref compression)) + { + Compression = compression; + } + } } diff --git a/GlitchyEditor/src/Assets/Importers/ImportedResource.bf b/GlitchyEditor/src/Assets/Importers/ImportedResource.bf new file mode 100644 index 0000000..f570593 --- /dev/null +++ b/GlitchyEditor/src/Assets/Importers/ImportedResource.bf @@ -0,0 +1,15 @@ +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Importers; + +class ImportedResource +{ + private AssetIdentifier _assetIdentifier ~ delete _; + + public AssetIdentifier AssetIdentifier => _assetIdentifier; + + public this(AssetIdentifier ownAssetIdentifier) + { + _assetIdentifier = ownAssetIdentifier; + } +} diff --git a/GlitchyEditor/src/Assets/Importers/Interfaces.bf b/GlitchyEditor/src/Assets/Importers/Interfaces.bf index 6b9af64..117f126 100644 --- a/GlitchyEditor/src/Assets/Importers/Interfaces.bf +++ b/GlitchyEditor/src/Assets/Importers/Interfaces.bf @@ -2,6 +2,7 @@ using System; using System.Collections; using System.IO; using GlitchyEngine.Content; +using GlitchyEditor.Assets.Processors; namespace GlitchyEditor.Assets.Importers; diff --git a/GlitchyEditor/src/Assets/Importers/TextureImporter.bf b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf index 49ecd44..f107d3d 100644 --- a/GlitchyEditor/src/Assets/Importers/TextureImporter.bf +++ b/GlitchyEditor/src/Assets/Importers/TextureImporter.bf @@ -6,22 +6,14 @@ using GlitchyEngine.Content; using GlitchyEngine; using GlitchyEngine.Renderer; using GlitchyEngine.Math; +using System.Threading; +using GlitchyEditor.Assets.Processors; +using ImGui; + using static GlitchyEditor.Assets.Importers.LoadedTextureInfo; namespace GlitchyEditor.Assets.Importers; -class ImportedResource -{ - private AssetIdentifier _assetIdentifier ~ delete _; - - public AssetIdentifier AssetIdentifier => _assetIdentifier; - - public this(AssetIdentifier ownAssetIdentifier) - { - _assetIdentifier = ownAssetIdentifier; - } -} - class ImportedTexture : ImportedResource { //public TextureDimension TextureType; @@ -48,6 +40,18 @@ class TextureImporterConfig : AssetImporterConfig get => _isSrgb; set => SetIfChanged(ref _isSrgb, value); } + + public override void ShowEditor() + { + ImGui.PropertyTableStartNewProperty("Is sRGB"); + ImGui.AttachTooltip("If checked, the texture will be forced to be imported as sRGB. Refer to the documentation for an detailed explanation what you should do here."); + + bool isSrgb = _isSrgb; + if (ImGui.Checkbox("##isSrgb", &isSrgb)) + { + IsSrgb = isSrgb; + } + } } @@ -64,6 +68,8 @@ class TextureImporter : IAssetImporter public Result Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetImporterConfig config) { + Thread.Sleep(1000); + Log.EngineLogger.AssertDebug(config is TextureImporterConfig); ImportedTexture importedData = new ImportedTexture(new AssetIdentifier(assetIdentifier.FullIdentifier)); @@ -221,443 +227,3 @@ class TextureImporter : IAssetImporter return .Ok; } } - -[BonTarget] -enum GenerateMipMaps -{ - No, - Box, - Kaiser -} - -[BonTarget, BonPolyRegister] -class TextureProcessorConfig : AssetProcessorConfig -{ - [BonInclude] - private GenerateMipMaps _generateMipMaps; - - [BonInclude] - private SamplerStateDescription _samplerStateDescription = .(); - - public GenerateMipMaps GenerateMipMaps - { - get => _generateMipMaps; - set => SetIfChanged(ref _generateMipMaps, value); - } - - public SamplerStateDescription SamplerStateDescription - { - get => _samplerStateDescription; - set => SetIfChanged(ref _samplerStateDescription, value); - } -} - -abstract class ProcessedResource -{ - private AssetIdentifier _assetIdentifier ~ delete _; - - public AssetIdentifier AssetIdentifier => _assetIdentifier; - - public abstract AssetType AssetType {get;} - - public this(AssetIdentifier ownAssetIdentifier) - { - _assetIdentifier = ownAssetIdentifier; - } -} - -class ProcessedTexture : ProcessedResource -{ - public Format PixelFormat = .Unknown; - public int MipMapCount = -1; - public int ArraySize = -1; - public TextureDimension Dimension = .Unknown; - public bool IsCubeMap; - - public int Width = -1; - public int Height = -1; - public int Depth = -1; - - public SamplerStateDescription SamplerStateDescription; - - public override AssetType AssetType => .Texture; - - public class TextureSurface - { - public uint8[] PixelData; - public int Width; - public int Height; - public int Depth; - public int MipLevel; - public int ArraySlice; - public int LinePitch; - public int SlicePitch; - - [AllowAppend] - public this(int width, int height, int depth, Span data, int mipLevel, int arraySlice, int linePitch, int slicePitch) - { - uint8[] pixelData = append uint8[data.Length]; - data.CopyTo(pixelData); - - PixelData = pixelData; - Width = width; - Height = height; - Depth = depth; - MipLevel = mipLevel; - ArraySlice = arraySlice; - LinePitch = linePitch; - SlicePitch = slicePitch; - } - - public uint64 LoadRaw(int x, int y, int z, Format format, ComponentInfo component) - { - Log.EngineLogger.AssertDebug(x >= 0 && y >= 0 && z >= 0 && x < Width && y < Height && z < Depth); - - int64 pixelOffset = x + (Width * y) + (Width * Height) * z; - - int64 bitOffset = pixelOffset * format.BitsPerPixel(); - - bitOffset += component.BitSize; - - int64 byteOffset = bitOffset / 8; - int shift = bitOffset % 8; - - int bytesToRead = (component.BitSize + shift) / 8; - - Log.EngineLogger.AssertDebug(bytesToRead <= 8); - - uint64 data = 0; - - data = PixelData.Ptr[byteOffset]; - data >>= shift; - uint64 mask = (1 << component.BitSize) - 1; - data &= mask; - - return data; - } - } - - public TextureSurface[,] Surfaces; - - public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier) - { - - } - - public ~this() - { - for (int i < Surfaces?.GetLength(0) ?? 0) - { - for (int j < Surfaces.GetLength(1)) - { - delete Surfaces[i, j]; - } - } - - delete Surfaces; - } - - public void SetSurfaceCount(int arraySize, int mipMapCount) - { - ArraySize = arraySize; - - if (IsCubeMap) - ArraySize *= 6; - - Log.EngineLogger.AssertDebug(Surfaces == null || arraySize > Surfaces.GetLength(0)); - Log.EngineLogger.AssertDebug(Surfaces == null ||mipMapCount > Surfaces.GetLength(1)); - - MipMapCount = mipMapCount; - - TextureSurface[,] oldSurfaces = Surfaces; - Surfaces = new TextureSurface[ArraySize, MipMapCount]; - - if (oldSurfaces != null) - { - for (int i < oldSurfaces.GetLength(0)) - { - for (int j < oldSurfaces.GetLength(1)) - { - Surfaces[i, j] = oldSurfaces[i, j]; - } - } - } - } -} - -class TextureProcessor : IAssetProcessor -{ - public AssetProcessorConfig CreateDefaultConfig() - { - return new TextureProcessorConfig(); - } - - public Result Process(ImportedResource importedObject, AssetProcessorConfig config) - { - Log.EngineLogger.AssertDebug(config is TextureProcessorConfig); - Log.EngineLogger.AssertDebug(importedObject is ImportedTexture); - - return Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig)); - } - - private Result ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config) - { - ProcessedTexture processedTexture = new ProcessedTexture(new AssetIdentifier(importedTexture.AssetIdentifier.FullIdentifier)); - - processedTexture.Dimension = importedTexture.TextureInfo.Dimension; - processedTexture.PixelFormat = (.)importedTexture.TextureInfo.PixelFormat; - processedTexture.IsCubeMap = importedTexture.TextureInfo.IsCubeMap; - processedTexture.Width = importedTexture.TextureInfo.Width; - processedTexture.Height = importedTexture.TextureInfo.Height; - processedTexture.Depth = importedTexture.TextureInfo.Depth; - - processedTexture.SamplerStateDescription = config.SamplerStateDescription; - - processedTexture.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount); - - for (LoadedSurface loadedSurface in importedTexture.Surfaces) - { - ProcessedTexture.TextureSurface surface = new .(loadedSurface.Width, loadedSurface.Height, loadedSurface.Depth, - loadedSurface.Data, loadedSurface.MipLevel, loadedSurface.ArrayIndex, loadedSurface.Pitch, loadedSurface.SlicePitch); - - processedTexture.Surfaces[loadedSurface.ArrayIndex * (processedTexture.IsCubeMap ? 6 : 1) + loadedSurface.CubeFace, loadedSurface.MipLevel] = surface; - } - - if (!(config.GenerateMipMaps case .No)) - { - // TODO: Unpack BC-Formats to RGBA - - GenerateMipMaps(processedTexture, config); - } - - // TODO: Pack to BC-Format or what ever was selected. - - return processedTexture; - } - - private int CalculateMipMapCount(int width, int height, int depth) - { - var width, height, depth; - - int count = 0; - - while (true) - { - count++; - - if (width == 1 && height == 1 && depth == 1) - break; - - if (width > 1) - width >>= 1; - if (height > 1) - height >>= 1; - if (depth > 1) - depth >>= 1; - } - - return count; - } - - public bool CanGenerateMipMaps(Format format) - { - // TODO! - return true; - } - - private Result GenerateMipMaps(ProcessedTexture processedTexture, TextureProcessorConfig config) - { - if (!CanGenerateMipMaps(processedTexture.PixelFormat)) - { - return .Err; - } - - int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); - - if (processedTexture.MipMapCount != mipMapCount) - { - processedTexture.SetSurfaceCount(processedTexture.ArraySize, mipMapCount); - } - - for (int arraySlice < processedTexture.ArraySize) - { - ProcessedTexture.TextureSurface largerSurface = processedTexture.Surfaces[arraySlice, 0]; - - for (int mipMap = 1; mipMap < processedTexture.MipMapCount; mipMap++) - { - ref ProcessedTexture.TextureSurface surface = ref processedTexture.Surfaces[arraySlice, mipMap]; - - surface = GenerateMipLevel(processedTexture.PixelFormat, largerSurface, surface); - - largerSurface = surface; - } - } - - return .Ok; - - // TODO: Generate Mip Maps - /*int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); - - processedTexture.SetMipMapCount(mipMapCount); - - for (int slice < processedTexture.ArraySize) - { - for (int mipMap < mipMapCount) - { - if (processedTexture.Surfaces[slice, mipMap] == null) - { - processedTexture.Surfaces[slice, mipMap] = GenerateMipLevel(processedTexture.Surfaces[slice, mipMap - 1]); - } - } - } - */ - } - - private ProcessedTexture.TextureSurface GenerateMipLevel(Format pixelFormat, ProcessedTexture.TextureSurface largerLevel, ProcessedTexture.TextureSurface smallerLevel) - { - var smallerLevel; - - int width = Math.Max(largerLevel.Width / 2, 1); - int height = Math.Max(largerLevel.Height / 2, 1); - int depth = Math.Max(largerLevel.Depth / 2, 1); - - if (smallerLevel == null) - { - smallerLevel = new ProcessedTexture.TextureSurface(width, height, depth, - new uint8[width * height * depth * pixelFormat.BitsPerPixel()], largerLevel.MipLevel + 1, largerLevel.ArraySlice, - -1, -1); // TODO! - } - - // TODO: Kaiser mip maps? - - FormatInfo formatInfo = default; //pixelFormat.GetFormatInfo(); - - // Simple box filter - for (int x < width) - for (int y < height) - for (int z < depth) - { - for (int channel < formatInfo.ComponentCount) - { - ComponentInfo info = formatInfo.Components[channel]; - - switch (info.DataType) - { - case .UNorm, .UInt: - - default: - } - } - } - - return smallerLevel; - } - - private void Box() where DataType : const ComponentDataType - { - - } -} - -class TextureExporter : IAssetExporter -{ - public AssetExporterConfig CreateDefaultConfig() - { - return new AssetExporterConfig(); - } - - public Result Export(Stream stream, ProcessedResource processedResource, AssetExporterConfig config) - { - Log.EngineLogger.AssertDebug(processedResource is ProcessedTexture); - - ProcessedTexture processedTexture = (.)processedResource; - - /* - - File Format: - TextureType (1 byte) - Pixel Format (4 bytes) - Width of larges mip-slice (4 bytes) - Height of larges mip-slice (4 bytes) - Depth of larges mip-slice (4 bytes) - Array size (4 bytes) - Mip map levels (4 bytes) - Is Cubemap (1 byte) - Data Byte count (8 bytes) - Pixeldata - { - Array[0]: Mip[0] Mip[1] ... Mip[M] - Array[1]: Mip[0] Mip[1] ... Mip[M] - ... - Array[N]: Mip[0] Mip[1] ... Mip[M] - } - - */ - - Try!(stream.Write(processedTexture.Dimension)); - Try!(stream.Write(processedTexture.PixelFormat)); - Try!(stream.Write((uint32)processedTexture.Width)); - Try!(stream.Write((uint32)processedTexture.Height)); - Try!(stream.Write((uint32)processedTexture.Depth)); - Try!(stream.Write((uint32)processedTexture.ArraySize)); - Try!(stream.Write((uint32)processedTexture.MipMapCount)); - Try!(stream.Write(processedTexture.IsCubeMap)); - - Try!(WriteSamplerStateDescription(stream, processedTexture.SamplerStateDescription)); - - for (int arraySlice < processedTexture.ArraySize) - { - int validateWidth = processedTexture.Width; - int validateHeight = processedTexture.Height; - int validateDepth = processedTexture.Depth; - - for (int mipSlice < processedTexture.MipMapCount) - { - ProcessedTexture.TextureSurface slice = processedTexture.Surfaces[arraySlice, mipSlice]; - - Log.EngineLogger.AssertDebug(validateWidth == slice.Width); - Log.EngineLogger.AssertDebug(validateHeight == slice.Height); - Log.EngineLogger.AssertDebug(validateDepth == slice.Depth); - validateWidth /= 2; - validateHeight /= 2; - validateDepth /= 2; - - if (validateWidth < 1) - validateWidth = 1; - - if (validateHeight < 1) - validateHeight = 1; - - if (validateDepth < 1) - validateDepth = 1; - - Try!(stream.Write((uint32)slice.LinePitch)); - Try!(stream.Write((uint32)slice.SlicePitch)); - Try!(stream.Write((uint64)slice.PixelData.Count)); - Try!(stream.TryWrite(slice.PixelData)); - } - } - - return .Ok; - } - - private Result WriteSamplerStateDescription(Stream stream, SamplerStateDescription sampler) - { - Try!(stream.Write(sampler.MinFilter)); - Try!(stream.Write(sampler.MagFilter)); - Try!(stream.Write(sampler.MipFilter)); - Try!(stream.Write(sampler.FilterMode)); - Try!(stream.Write(sampler.ComparisonFunction)); - Try!(stream.Write(sampler.AddressModeU)); - Try!(stream.Write(sampler.AddressModeV)); - Try!(stream.Write(sampler.AddressModeW)); - Try!(stream.Write(sampler.MipLODBias)); - Try!(stream.Write(sampler.MipMinLOD)); - Try!(stream.Write(sampler.MipMaxLOD)); - Try!(stream.Write(sampler.MaxAnisotropy)); - Try!(stream.Write(sampler.BorderColor)); - - return .Ok; - } -} - diff --git a/GlitchyEditor/src/Assets/Processors/ProcessedResource.bf b/GlitchyEditor/src/Assets/Processors/ProcessedResource.bf new file mode 100644 index 0000000..505e6bb --- /dev/null +++ b/GlitchyEditor/src/Assets/Processors/ProcessedResource.bf @@ -0,0 +1,17 @@ +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Processors; + +abstract class ProcessedResource +{ + private AssetIdentifier _assetIdentifier ~ delete _; + + public AssetIdentifier AssetIdentifier => _assetIdentifier; + + public abstract AssetType AssetType {get;} + + public this(AssetIdentifier ownAssetIdentifier) + { + _assetIdentifier = ownAssetIdentifier; + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/Processors/TextureProcessor.bf b/GlitchyEditor/src/Assets/Processors/TextureProcessor.bf new file mode 100644 index 0000000..9eb3ddf --- /dev/null +++ b/GlitchyEditor/src/Assets/Processors/TextureProcessor.bf @@ -0,0 +1,451 @@ +using System; +using Bon; +using GlitchyEditor.Assets.Importers; +using GlitchyEngine.Renderer; +using GlitchyEngine.Content; +using GlitchyEngine; +using ImGui; + +namespace GlitchyEditor.Assets.Processors; + +[BonTarget] +enum GenerateMipMaps +{ + No, + Box, + Kaiser +} + +[BonTarget, BonPolyRegister] +class TextureProcessorConfig : AssetProcessorConfig +{ + [BonInclude] + private GenerateMipMaps _generateMipMaps; + + [BonInclude] + private SamplerStateDescription _samplerStateDescription = .(); + + public GenerateMipMaps GenerateMipMaps + { + get => _generateMipMaps; + set => SetIfChanged(ref _generateMipMaps, value); + } + + public SamplerStateDescription SamplerStateDescription + { + get => _samplerStateDescription; + set => SetIfChanged(ref _samplerStateDescription, value); + } + + public override void ShowEditor() + { + ImGui.PropertyTableStartNewProperty("Generate Mip Maps", "Specifies the algorithm used to generate mip maps for this texture."); + + ImGui.BeginDisabled(); + + GenerateMipMaps generateMipMaps = _generateMipMaps; + if (ImGui.EnumCombo("##generateMipMaps", ref generateMipMaps)) + { + GenerateMipMaps = generateMipMaps; + } + + // TODO: Show some mip map generation settings (e.g. count) + + ImGui.EndDisabled(); + + ImGui.PropertyTableStartNewRow(); + if (ImGui.TreeNodeEx("Sampling", ImGui.TreeNodeFlags.SpanAllColumns | ImGui.TreeNodeFlags.Framed)) + { + ref SamplerStateDescription sampler = ref _samplerStateDescription; + + bool IsAnisotropic() + { + return sampler.MinFilter == .Anisotropic || + sampler.MagFilter == .Anisotropic || + sampler.MipFilter == .Anisotropic; + } + + if (IsAnisotropic()) + { + ImGui.PropertyTableStartNewProperty("Filter", "Sampling method used for minification, magnification and mip-level sampling. Note: If one filter is set to Anisotropic, all are set to Anisotropic."); + + FilterFunction filter = .Anisotropic; + if (ImGui.EnumCombo("##minFilter", ref filter)) + { + _changed = true; + + if (sampler.MinFilter == .Anisotropic) + sampler.MinFilter = filter; + + if (sampler.MagFilter == .Anisotropic) + sampler.MagFilter = filter; + + if (sampler.MipFilter == .Anisotropic) + sampler.MipFilter = filter; + } + + ImGui.PropertyTableStartNewProperty("Max Anisotropy", "Clamps the anisotropic sampling rate to the specified value."); + int32 maxAnisotropy = sampler.MaxAnisotropy; + if (ImGui.SliderInt("##maxAnisotropy", &maxAnisotropy, 1, 16)) + { + sampler.MaxAnisotropy = (uint8)maxAnisotropy; + _changed = true; + } + } + else + { + ImGui.PropertyTableStartNewProperty("Min Filter", "Sampling method used for minification. Note: If one filter is set to Anisotropic, all are set to Anisotropic."); + if (ImGui.EnumCombo("##minFilter", ref sampler.MinFilter)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Mag Filter", "Sampling method used for magnification. Note: If one filter is set to Anisotropic, all are set to Anisotropic."); + if (ImGui.EnumCombo("##magFilter", ref sampler.MagFilter)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Mip Filter", "Method used for mip-level sampling. Note: If one filter is set to Anisotropic, all are set to Anisotropic."); + if (ImGui.EnumCombo("##mipFilter", ref sampler.MipFilter)) + _changed = true; + } + + ImGui.PropertyTableStartNewProperty("Mip LOD Bias", "Offset from the calculated mipmap level."); + if (ImGui.DragFloat("##mipLodBias", &sampler.MipLODBias)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Mip Min LOD", "Lower end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed."); + if (ImGui.DragFloat("##mipMinLod", &sampler.MipMinLOD, v_min: 0.0f, v_max: 64)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Mip Max LOD", "Upper end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed."); + if (ImGui.DragFloat("##mipMaxLod", &sampler.MipMaxLOD, v_min: 0.0f, v_max: 64)) + _changed = true; + + // Make sure max lod is always at least min lod. + sampler.MipMaxLOD = Math.Max(sampler.MipMaxLOD, sampler.MipMinLOD); + + ImGui.PropertyTableStartNewProperty("Filter mode", "Filtering method to use when sampling a texture."); + if (ImGui.EnumCombo("##filterMode", ref sampler.FilterMode)) + _changed = true; + + if (sampler.FilterMode == .Comparison) + { + ImGui.PropertyTableStartNewProperty("Comparison function", "The function that is used to compare the sampled data against the existing sampled data."); + if (ImGui.EnumCombo("##ComparisonFunction", ref sampler.ComparisonFunction)) + _changed = true; + } + + ImGui.PropertyTableStartNewProperty("Address Mode U", "Method to use for resolving a u texture coordinate that is outside the 0 to 1 range."); + if (ImGui.EnumCombo("##AddressModeU", ref sampler.AddressModeU)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Address ModeV", "Method to use for resolving a v texture coordinate that is outside the 0 to 1 range."); + if (ImGui.EnumCombo("##AddressModeV", ref sampler.AddressModeV)) + _changed = true; + + ImGui.PropertyTableStartNewProperty("Address ModeW", "Method to use for resolving a w texture coordinate that is outside the 0 to 1 range."); + if (ImGui.EnumCombo("##AddressModeW", ref sampler.AddressModeW)) + _changed = true; + + if (sampler.AddressModeU == .Border || sampler.AddressModeV == .Border || sampler.AddressModeW == .Border) + { + ImGui.PropertyTableStartNewProperty("Border Color", "Border color to use if any of the address modes is set to Border"); + if (ImGui.ColorEdit4("##BorderColor", ref sampler.BorderColor)) + _changed = true; + } + + ImGui.TreePop(); + } + } +} + +class ProcessedTexture : ProcessedResource +{ + public Format PixelFormat = .Unknown; + public int MipMapCount = -1; + public int ArraySize = -1; + public TextureDimension Dimension = .Unknown; + public bool IsCubeMap; + + public int Width = -1; + public int Height = -1; + public int Depth = -1; + + public SamplerStateDescription SamplerStateDescription; + + public override AssetType AssetType => .Texture; + + public class TextureSurface + { + public uint8[] PixelData; + public int Width; + public int Height; + public int Depth; + public int MipLevel; + public int ArraySlice; + public int LinePitch; + public int SlicePitch; + + [AllowAppend] + public this(int width, int height, int depth, Span data, int mipLevel, int arraySlice, int linePitch, int slicePitch) + { + uint8[] pixelData = append uint8[data.Length]; + data.CopyTo(pixelData); + + PixelData = pixelData; + Width = width; + Height = height; + Depth = depth; + MipLevel = mipLevel; + ArraySlice = arraySlice; + LinePitch = linePitch; + SlicePitch = slicePitch; + } + + public uint64 LoadRaw(int x, int y, int z, Format format, ComponentInfo component) + { + Log.EngineLogger.AssertDebug(x >= 0 && y >= 0 && z >= 0 && x < Width && y < Height && z < Depth); + + int64 pixelOffset = x + (Width * y) + (Width * Height) * z; + + int64 bitOffset = pixelOffset * format.BitsPerPixel(); + + bitOffset += component.BitSize; + + int64 byteOffset = bitOffset / 8; + int shift = bitOffset % 8; + + int bytesToRead = (component.BitSize + shift) / 8; + + Log.EngineLogger.AssertDebug(bytesToRead <= 8); + + uint64 data = 0; + + data = PixelData.Ptr[byteOffset]; + data >>= shift; + uint64 mask = (1 << component.BitSize) - 1; + data &= mask; + + return data; + } + } + + public TextureSurface[,] Surfaces; + + public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier) + { + + } + + public ~this() + { + for (int i < Surfaces?.GetLength(0) ?? 0) + { + for (int j < Surfaces.GetLength(1)) + { + delete Surfaces[i, j]; + } + } + + delete Surfaces; + } + + public void SetSurfaceCount(int arraySize, int mipMapCount) + { + ArraySize = arraySize; + + if (IsCubeMap) + ArraySize *= 6; + + Log.EngineLogger.AssertDebug(Surfaces == null || arraySize > Surfaces.GetLength(0)); + Log.EngineLogger.AssertDebug(Surfaces == null ||mipMapCount > Surfaces.GetLength(1)); + + MipMapCount = mipMapCount; + + TextureSurface[,] oldSurfaces = Surfaces; + Surfaces = new TextureSurface[ArraySize, MipMapCount]; + + if (oldSurfaces != null) + { + for (int i < oldSurfaces.GetLength(0)) + { + for (int j < oldSurfaces.GetLength(1)) + { + Surfaces[i, j] = oldSurfaces[i, j]; + } + } + } + } +} + +class TextureProcessor : IAssetProcessor +{ + public AssetProcessorConfig CreateDefaultConfig() + { + return new TextureProcessorConfig(); + } + + public Result Process(ImportedResource importedObject, AssetProcessorConfig config) + { + Log.EngineLogger.AssertDebug(config is TextureProcessorConfig); + Log.EngineLogger.AssertDebug(importedObject is ImportedTexture); + + return Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig)); + } + + private Result ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config) + { + ProcessedTexture processedTexture = new ProcessedTexture(new AssetIdentifier(importedTexture.AssetIdentifier.FullIdentifier)); + + processedTexture.Dimension = importedTexture.TextureInfo.Dimension; + processedTexture.PixelFormat = (.)importedTexture.TextureInfo.PixelFormat; + processedTexture.IsCubeMap = importedTexture.TextureInfo.IsCubeMap; + processedTexture.Width = importedTexture.TextureInfo.Width; + processedTexture.Height = importedTexture.TextureInfo.Height; + processedTexture.Depth = importedTexture.TextureInfo.Depth; + + processedTexture.SamplerStateDescription = config.SamplerStateDescription; + + processedTexture.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount); + + for (LoadedSurface loadedSurface in importedTexture.Surfaces) + { + ProcessedTexture.TextureSurface surface = new .(loadedSurface.Width, loadedSurface.Height, loadedSurface.Depth, + loadedSurface.Data, loadedSurface.MipLevel, loadedSurface.ArrayIndex, loadedSurface.Pitch, loadedSurface.SlicePitch); + + processedTexture.Surfaces[loadedSurface.ArrayIndex * (processedTexture.IsCubeMap ? 6 : 1) + loadedSurface.CubeFace, loadedSurface.MipLevel] = surface; + } + + if (!(config.GenerateMipMaps case .No)) + { + // TODO: Unpack BC-Formats to RGBA + + GenerateMipMaps(processedTexture, config); + } + + // TODO: Pack to BC-Format or what ever was selected. + + return processedTexture; + } + + private int CalculateMipMapCount(int width, int height, int depth) + { + var width, height, depth; + + int count = 0; + + while (true) + { + count++; + + if (width == 1 && height == 1 && depth == 1) + break; + + if (width > 1) + width >>= 1; + if (height > 1) + height >>= 1; + if (depth > 1) + depth >>= 1; + } + + return count; + } + + public bool CanGenerateMipMaps(Format format) + { + // TODO! + return true; + } + + private Result GenerateMipMaps(ProcessedTexture processedTexture, TextureProcessorConfig config) + { + if (!CanGenerateMipMaps(processedTexture.PixelFormat)) + { + return .Err; + } + + int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); + + if (processedTexture.MipMapCount != mipMapCount) + { + processedTexture.SetSurfaceCount(processedTexture.ArraySize, mipMapCount); + } + + for (int arraySlice < processedTexture.ArraySize) + { + ProcessedTexture.TextureSurface largerSurface = processedTexture.Surfaces[arraySlice, 0]; + + for (int mipMap = 1; mipMap < processedTexture.MipMapCount; mipMap++) + { + ref ProcessedTexture.TextureSurface surface = ref processedTexture.Surfaces[arraySlice, mipMap]; + + surface = GenerateMipLevel(processedTexture.PixelFormat, largerSurface, surface); + + largerSurface = surface; + } + } + + return .Ok; + + // TODO: Generate Mip Maps + /*int mipMapCount = CalculateMipMapCount(processedTexture.Width, processedTexture.Height, processedTexture.Depth); + + processedTexture.SetMipMapCount(mipMapCount); + + for (int slice < processedTexture.ArraySize) + { + for (int mipMap < mipMapCount) + { + if (processedTexture.Surfaces[slice, mipMap] == null) + { + processedTexture.Surfaces[slice, mipMap] = GenerateMipLevel(processedTexture.Surfaces[slice, mipMap - 1]); + } + } + } + */ + } + + private ProcessedTexture.TextureSurface GenerateMipLevel(Format pixelFormat, ProcessedTexture.TextureSurface largerLevel, ProcessedTexture.TextureSurface smallerLevel) + { + var smallerLevel; + + int width = Math.Max(largerLevel.Width / 2, 1); + int height = Math.Max(largerLevel.Height / 2, 1); + int depth = Math.Max(largerLevel.Depth / 2, 1); + + if (smallerLevel == null) + { + smallerLevel = new ProcessedTexture.TextureSurface(width, height, depth, + new uint8[width * height * depth * pixelFormat.BitsPerPixel()], largerLevel.MipLevel + 1, largerLevel.ArraySlice, + -1, -1); // TODO! + } + + // TODO: Kaiser mip maps? + + FormatInfo formatInfo = default; //pixelFormat.GetFormatInfo(); + + // Simple box filter + for (int x < width) + for (int y < height) + for (int z < depth) + { + for (int channel < formatInfo.ComponentCount) + { + ComponentInfo info = formatInfo.Components[channel]; + + switch (info.DataType) + { + case .UNorm, .UInt: + + default: + } + } + } + + return smallerLevel; + } + + private void Box() where DataType : const ComponentDataType + { + + } +} diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index 79c1868..7f7bf78 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -178,7 +178,7 @@ namespace GlitchyEditor.EditWindows } } - if (nodeOpen && ImGui.BeginTableEx("properties", TableId, 2, .SizingStretchSame | .BordersInner | .Resizable)) + if (nodeOpen && ImGui.BeginPropertyTable("properties", TableId)) { if (entity.TryGetComponent(let actualComponent)) showComponentEditor(entity, actualComponent); @@ -187,33 +187,6 @@ namespace GlitchyEditor.EditWindows } } - /// Starts a new row in the table and enters the first column. - private static void StartNewRow() - { - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - } - - /// Starts a new property by creating a new table row, writing the name in the first column and entering the second column. - private static void StartNewProperty(StringView propertyName) - { - StartNewRow(); - - bool isFirstTableRow = ImGui.TableGetRowIndex() == 0; - - if (isFirstTableRow) - ImGui.PushItemWidth(-1); - - ImGui.TextUnformatted(propertyName); - - ImGui.AttachTooltip(propertyName); - - ImGui.TableSetColumnIndex(1); - - if (isFirstTableRow) - ImGui.PushItemWidth(-1); - } - private static void ShowNameComponentEditor(Entity entity) { if (!entity.HasComponent()) @@ -239,7 +212,7 @@ namespace GlitchyEditor.EditWindows ImGui.PushItemWidth(-1); - StartNewProperty("Name"); + ImGui.PropertyTableStartNewProperty("Name"); ImGui.PushItemWidth(-1); @@ -264,7 +237,7 @@ namespace GlitchyEditor.EditWindows float3 position = transform.Position; - StartNewProperty("Position"); + ImGui.PropertyTableStartNewProperty("Position"); if (ImGui.Float3Editor("##Position", ref position, resetValues: .Zero, dragSpeed: 0.1f)) { transform.Position = position; @@ -287,7 +260,7 @@ namespace GlitchyEditor.EditWindows rotationEuler.XY = 0; } - StartNewProperty("Rotation"); + ImGui.PropertyTableStartNewProperty("Rotation"); if (ImGui.Float3Editor("##Rotation", ref rotationEuler, resetValues: .Zero, dragSpeed: 0.1f, componentEnabled: componentEditable, format: .("%.3f°",))) { transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); @@ -301,7 +274,7 @@ namespace GlitchyEditor.EditWindows float3 scale = transform.Scale; - StartNewProperty("Scale"); + ImGui.PropertyTableStartNewProperty("Scale"); if (ImGui.Float3Editor("##Scale", ref scale, resetValues: .One, dragSpeed: 0.1f)) transform.Scale = scale; } @@ -310,14 +283,14 @@ namespace GlitchyEditor.EditWindows private static void ShowCameraComponentEditor(Entity entity, CameraComponent* cameraComponent) { - StartNewProperty("Is Primary"); + ImGui.PropertyTableStartNewProperty("Is Primary"); ImGui.Checkbox("##Is_Primary", &cameraComponent.Primary); var camera = ref cameraComponent.Camera; String typeName = strings[camera.ProjectionType.Underlying]; - StartNewProperty("Projection"); + ImGui.PropertyTableStartNewProperty("Projection"); if (ImGui.BeginCombo("##Projection", typeName.CStr())) { for (int i = 0; i < 3; i++) @@ -338,59 +311,59 @@ namespace GlitchyEditor.EditWindows if (camera.ProjectionType == .Perspective) { - StartNewProperty("Fov Y"); + ImGui.PropertyTableStartNewProperty("Fov Y"); float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); if (ImGui.DragFloat("##Fov Y", &fovY, 0.1f, format: "%.3f°")) camera.PerspectiveFovY = MathHelper.ToRadians(fovY); - StartNewProperty("Near"); + ImGui.PropertyTableStartNewProperty("Near"); float near = camera.PerspectiveNearPlane; if (ImGui.DragFloat("##Near", &near, 0.1f)) camera.PerspectiveNearPlane = near; - StartNewProperty("Far"); + ImGui.PropertyTableStartNewProperty("Far"); float far = camera.PerspectiveFarPlane; if (ImGui.DragFloat("##Far", &far, 0.1f)) camera.PerspectiveFarPlane = far; } else if (camera.ProjectionType == .InfinitePerspective) { - StartNewProperty("Vertical FOV"); + ImGui.PropertyTableStartNewProperty("Vertical FOV"); float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY); if (ImGui.DragFloat("##Vertical FOV", &fovY, 0.1f, format: "%.3f°")) camera.PerspectiveFovY = MathHelper.ToRadians(fovY); - StartNewProperty("Near"); + ImGui.PropertyTableStartNewProperty("Near"); float near = camera.PerspectiveNearPlane; if (ImGui.DragFloat("##Near", &near, 0.1f)) camera.PerspectiveNearPlane = near; } else if (camera.ProjectionType == .Orthographic) { - StartNewProperty("Size"); + ImGui.PropertyTableStartNewProperty("Size"); float size = camera.OrthographicHeight; if (ImGui.DragFloat("##Size", &size, 0.1f)) camera.OrthographicHeight = size; - StartNewProperty("Near"); + ImGui.PropertyTableStartNewProperty("Near"); float near = camera.OrthographicNearPlane; if (ImGui.DragFloat("##Near", &near, 0.1f)) camera.OrthographicNearPlane = near; - StartNewProperty("Far"); + ImGui.PropertyTableStartNewProperty("Far"); float far = camera.OrthographicFarPlane; if (ImGui.DragFloat("##Far", &far, 0.1f)) camera.OrthographicFarPlane = far; } - StartNewProperty("Fixed Aspect Ratio"); + ImGui.PropertyTableStartNewProperty("Fixed Aspect Ratio"); bool fixedAspectRatio = camera.FixedAspectRatio; if (ImGui.Checkbox("##Fixed Aspect Ratio", &fixedAspectRatio)) camera.FixedAspectRatio = fixedAspectRatio; if (fixedAspectRatio) { - StartNewProperty("Aspect Ratio"); + ImGui.PropertyTableStartNewProperty("Aspect Ratio"); float aspect = camera.AspectRatio; if (ImGui.DragFloat("##Aspect Ratio", &aspect, 0.1f)) camera.AspectRatio = aspect; @@ -400,11 +373,11 @@ namespace GlitchyEditor.EditWindows private static void ShowSpriteRendererComponentEditor(Entity entity, SpriteRendererComponent* spriteRendererComponent) { ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color); - StartNewProperty("Color"); + ImGui.PropertyTableStartNewProperty("Color"); if (ImGui.ColorEdit4("##Color", ref spriteColor)) spriteRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor); - StartNewProperty("Texture"); + ImGui.PropertyTableStartNewProperty("Texture"); ImGui.Button("..."); if (ImGui.BeginDragDropTarget()) @@ -423,18 +396,18 @@ namespace GlitchyEditor.EditWindows ImGui.EndDragDropTarget(); } - StartNewProperty("UV Transform"); + ImGui.PropertyTableStartNewProperty("UV Transform"); ImGui.Float4Editor("##UV Transform", ref spriteRendererComponent.UvTransform, resetValues: float4(0, 0, 1, 1)); } private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent) { ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(circleRendererComponent.Color); - StartNewProperty("Color"); + ImGui.PropertyTableStartNewProperty("Color"); if (ImGui.ColorEdit4("##Color", ref spriteColor)) circleRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor); - StartNewProperty("Texture"); + ImGui.PropertyTableStartNewProperty("Texture"); ImGui.Button("..."); if (ImGui.BeginDragDropTarget()) @@ -453,10 +426,10 @@ namespace GlitchyEditor.EditWindows ImGui.EndDragDropTarget(); } - StartNewProperty("UV Transform"); + ImGui.PropertyTableStartNewProperty("UV Transform"); ImGui.Float4Editor("##UV Transform", ref circleRendererComponent.UvTransform, resetValues: float4(0, 0, 1, 1)); - StartNewProperty("Inner Radius"); + ImGui.PropertyTableStartNewProperty("Inner Radius"); ImGui.DragFloat("##Inner Radius", &circleRendererComponent.InnerRadius, 0.1f, 0.0f, 1.0f); } @@ -479,7 +452,7 @@ namespace GlitchyEditor.EditWindows private static void ShowTextRendererComponentEditor(Entity entity, TextRendererComponent* textRendererComponent) { - StartNewProperty("Rich text"); + ImGui.PropertyTableStartNewProperty("Rich text"); ImGui.AttachTooltip("If checked, the text will be interpreted as rich text."); @@ -491,7 +464,7 @@ namespace GlitchyEditor.EditWindows textRendererComponent.NeedsRebuild = true; } - StartNewProperty("Text"); + ImGui.PropertyTableStartNewProperty("Text"); String text = textRendererComponent.[Friend]_text; @@ -516,21 +489,21 @@ namespace GlitchyEditor.EditWindows textRendererComponent.NeedsRebuild = true; } - StartNewProperty("Font Size"); + ImGui.PropertyTableStartNewProperty("Font Size"); if (ImGui.DragFloat("##fontSize", &textRendererComponent.FontSize)) { textRendererComponent.NeedsRebuild = true; } - StartNewProperty("Color"); + ImGui.PropertyTableStartNewProperty("Color"); if (ImGui.ColorEdit4("##color", ref textRendererComponent.Color)) { textRendererComponent.NeedsRebuild = true; } - StartNewProperty("Horizontal Alignment"); + ImGui.PropertyTableStartNewProperty("Horizontal Alignment"); if (ImGui.EnumCombo("##horAlign", ref textRendererComponent.HorizontalAlignment)) { @@ -540,7 +513,7 @@ namespace GlitchyEditor.EditWindows private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent) { - StartNewProperty("Material"); + ImGui.PropertyTableStartNewProperty("Material"); Material material = meshRendererComponent.Material; @@ -574,7 +547,7 @@ namespace GlitchyEditor.EditWindows const String[?] bodyTypeStrings = .("Static", "Dynamic", "Kinematic"); String bodyTypeName = bodyTypeStrings[rigidBodyComponent.BodyType.Underlying]; - StartNewProperty("Type"); + ImGui.PropertyTableStartNewProperty("Type"); if (ImGui.BeginCombo("##Type", bodyTypeName.CStr())) { for (int i = 0; i < 3; i++) @@ -594,14 +567,14 @@ namespace GlitchyEditor.EditWindows } bool isFixedRotation = rigidBodyComponent.FixedRotation; - StartNewProperty("Fixed Rotation"); + ImGui.PropertyTableStartNewProperty("Fixed Rotation"); if (ImGui.Checkbox("##Fixed Rotation", &isFixedRotation)) { rigidBodyComponent.FixedRotation = isFixedRotation; } float gravityScale = rigidBodyComponent.GravityScale; - StartNewProperty("Gravity Scale"); + ImGui.PropertyTableStartNewProperty("Gravity Scale"); if (ImGui.DragFloat("##Gravity Scale", &gravityScale)) { rigidBodyComponent.GravityScale = gravityScale; @@ -611,32 +584,32 @@ namespace GlitchyEditor.EditWindows private static void ShowBoxCollider2DComponentEditor(Entity entity, BoxCollider2DComponent* boxCollider) { float2 offset = boxCollider.Offset; - StartNewProperty("Offset"); + ImGui.PropertyTableStartNewProperty("Offset"); if (ImGui.Float2Editor("##Offset", ref offset, .Zero, 0.1f)) boxCollider.Offset = offset; float2 size = boxCollider.Size; - StartNewProperty("Size"); + ImGui.PropertyTableStartNewProperty("Size"); if (ImGui.Float2Editor("##Size", ref size, .Zero, 0.1f, float2(0.01f, 0.01f), float.PositiveInfinity.XX)) boxCollider.Size = size; float density = boxCollider.Density; - StartNewProperty("Density"); + ImGui.PropertyTableStartNewProperty("Density"); if (ImGui.DragFloat("##Density", &density, 0.0f, 0.1f)) boxCollider.Density = density; float friction = boxCollider.Friction; - StartNewProperty("Friction"); + ImGui.PropertyTableStartNewProperty("Friction"); if (ImGui.DragFloat("##Friction", &friction, 0.0f, 0.1f)) boxCollider.Friction = friction; float restitution = boxCollider.Restitution; - StartNewProperty("Restitution"); + ImGui.PropertyTableStartNewProperty("Restitution"); if (ImGui.DragFloat("##Restitution", &restitution, 0.0f, 0.1f)) boxCollider.Restitution = restitution; float restitutionThreshold = boxCollider.RestitutionThreshold; - StartNewProperty("Restitution Threshold"); + ImGui.PropertyTableStartNewProperty("Restitution Threshold"); if (ImGui.DragFloat("##RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f)) boxCollider.RestitutionThreshold = restitutionThreshold; } @@ -644,32 +617,32 @@ namespace GlitchyEditor.EditWindows private static void ShowCircleCollider2DComponentEditor(Entity entity, CircleCollider2DComponent* circleCollider) { float2 offset = circleCollider.Offset; - StartNewProperty("Offset"); + ImGui.PropertyTableStartNewProperty("Offset"); if (ImGui.Float2Editor("##Offset", ref offset, .Zero, 0.1f)) circleCollider.Offset = offset; float radius = circleCollider.Radius; - StartNewProperty("Radius"); + ImGui.PropertyTableStartNewProperty("Radius"); if (ImGui.DragFloat("##Radius", &radius, 0.0f, 0.1f)) circleCollider.Radius = radius; float density = circleCollider.Density; - StartNewProperty("Density"); + ImGui.PropertyTableStartNewProperty("Density"); if (ImGui.DragFloat("##Density", &density, 0.0f, 0.1f)) circleCollider.Density = density; float friction = circleCollider.Friction; - StartNewProperty("Friction"); + ImGui.PropertyTableStartNewProperty("Friction"); if (ImGui.DragFloat("##Friction", &friction, 0.0f, 0.1f)) circleCollider.Friction = friction; float restitution = circleCollider.Restitution; - StartNewProperty("Restitution"); + ImGui.PropertyTableStartNewProperty("Restitution"); if (ImGui.DragFloat("##Restitution", &restitution, 0.0f, 0.1f)) circleCollider.Restitution = restitution; float restitutionThreshold = circleCollider.RestitutionThreshold; - StartNewProperty("Restitution Threshold"); + ImGui.PropertyTableStartNewProperty("Restitution Threshold"); if (ImGui.DragFloat("##RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f)) circleCollider.RestitutionThreshold = restitutionThreshold; } @@ -689,11 +662,11 @@ namespace GlitchyEditor.EditWindows private static void ShowPolygonCollider2DComponentEditor(Entity entity, PolygonCollider2DComponent* polygonCollider) { float2 offset = polygonCollider.Offset; - StartNewProperty("Offset"); + ImGui.PropertyTableStartNewProperty("Offset"); if (ImGui.Float2Editor("Offset", ref offset, .Zero, 0.1f)) polygonCollider.Offset = offset; - StartNewRow(); + ImGui.PropertyTableStartNewRow(); bool isOpen = ImGui.TreeNodeEx("Vertices", .AllowOverlap | .SpanAllColumns); @@ -727,12 +700,12 @@ namespace GlitchyEditor.EditWindows // If isOpen is true, show list of vertices if (isOpen) { - StartNewProperty("Show Vertex gizmos"); + ImGui.PropertyTableStartNewProperty("Show Vertex gizmos"); ImGui.Checkbox("##Show Vertex gizmos", &_editVerticesPolygonCollider2D); for (int i < polygonCollider.VertexCount) { - StartNewProperty(scope $"{i}"); + ImGui.PropertyTableStartNewProperty(scope $"{i}"); ImGui.Float2Editor(scope $"##{i}", ref polygonCollider.Vertices[i]); } @@ -740,22 +713,22 @@ namespace GlitchyEditor.EditWindows } float density = polygonCollider.Density; - StartNewProperty("Density"); + ImGui.PropertyTableStartNewProperty("Density"); if (ImGui.DragFloat("##Density", &density, 0.0f, 0.1f)) polygonCollider.Density = density; float friction = polygonCollider.Friction; - StartNewProperty("Friction"); + ImGui.PropertyTableStartNewProperty("Friction"); if (ImGui.DragFloat("##Friction", &friction, 0.0f, 0.1f)) polygonCollider.Friction = friction; float restitution = polygonCollider.Restitution; - StartNewProperty("Restitution"); + ImGui.PropertyTableStartNewProperty("Restitution"); if (ImGui.DragFloat("##Restitution", &restitution, 0.0f, 0.1f)) polygonCollider.Restitution = restitution; float restitutionThreshold = polygonCollider.RestitutionThreshold; - StartNewProperty("Restitution Threshold"); + ImGui.PropertyTableStartNewProperty("Restitution Threshold"); if (ImGui.DragFloat("##RestitutionThreshold", &restitutionThreshold, 0.0f, 0.1f)) polygonCollider.RestitutionThreshold = restitutionThreshold; } @@ -768,7 +741,7 @@ namespace GlitchyEditor.EditWindows char8* scriptLabel = scriptComponent.ScriptClassName.ToScopeCStr!() ?? "Select Script..."; - StartNewProperty("Script"); + ImGui.PropertyTableStartNewProperty("Script"); if (ImGui.Button(scriptLabel)) ImGui.OpenPopup("SelectScript"); @@ -803,7 +776,7 @@ namespace GlitchyEditor.EditWindows String typeName = strings[light.LightType.Underlying]; - StartNewProperty("Type"); + ImGui.PropertyTableStartNewProperty("Type"); if (ImGui.BeginCombo("##Type", typeName.CStr())) { for (int i = 0; i < 3; i++) @@ -823,19 +796,19 @@ namespace GlitchyEditor.EditWindows } ColorRGB color = ColorRGB.LinearToSRGB(light.Color); - StartNewProperty("Color"); + ImGui.PropertyTableStartNewProperty("Color"); if (ImGui.ColorEdit3("##Color", ref color)) light.Color = ColorRGB.SRgbToLinear(color); float illuminance = light.Illuminance; - StartNewProperty("Illuminance"); + ImGui.PropertyTableStartNewProperty("Illuminance"); if (ImGui.DragFloat("##Illuminance", &illuminance, 0.1f, 0.0f, float.MaxValue)) light.Illuminance = illuminance; } private static void ShowMeshComponentEditor(Entity entity, MeshComponent* meshComponent) { - StartNewProperty("Mesh"); + ImGui.PropertyTableStartNewProperty("Mesh"); GeometryBinding mesh = meshComponent.Mesh; diff --git a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf index 7259fe2..b6d7b45 100644 --- a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf +++ b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf @@ -67,7 +67,34 @@ class PropertiesWindow : EditorWindow { AssetFile assetFile = GetCurrentAssetFile(); - if (_currentPropertiesEditor?.Asset != assetFile) + if (assetFile == null) + return; + + if (ImGui.BeginPropertyTable("asset_properties", ImGui.GetID("asset_properties"))) + { + assetFile.AssetConfig?.ImporterConfig?.ShowEditor(); + assetFile.AssetConfig?.ProcessorConfig?.ShowEditor(); + assetFile.AssetConfig?.ExporterConfig?.ShowEditor(); + + ImGui.EndTable(); + + ImGui.Separator(); + } + + bool hasChanges = (assetFile.AssetConfig?.ImporterConfig?.Changed ?? false) || (assetFile.AssetConfig?.ProcessorConfig?.Changed ?? false) || (assetFile.AssetConfig?.ExporterConfig?.Changed ?? false); + + if (!hasChanges) + ImGui.BeginDisabled(); + + if (ImGui.Button("Apply")) + { + assetFile.SaveAssetConfig(); + } + + if (!hasChanges) + ImGui.EndDisabled(); + + /*if (_currentPropertiesEditor?.Asset != assetFile) { delete _currentPropertiesEditor; _currentPropertiesEditor = _editor.ContentManager.GetNewPropertiesEditor(assetFile); @@ -81,7 +108,7 @@ class PropertiesWindow : EditorWindow // We need the actual asset for preview and sometimes for editing if (asset?.Identifier != assetFile.AssetFile.Identifier) { - _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.AssetFile.Identifier); + //_currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.AssetFile.Identifier); } // TODO: allow changing AssetLoader @@ -96,7 +123,7 @@ class PropertiesWindow : EditorWindow ShowPropertiesEditor(assetFile); ImGui.Separator(); - + */ // TODO: preview asset } diff --git a/GlitchyEditor/src/EditorApp.bf b/GlitchyEditor/src/EditorApp.bf index dd97b68..5134a82 100644 --- a/GlitchyEditor/src/EditorApp.bf +++ b/GlitchyEditor/src/EditorApp.bf @@ -3,6 +3,8 @@ using GlitchyEngine; using GlitchyEngine.Content; using GlitchyEditor.Assets; using GlitchyEditor.Assets.Importers; +using GlitchyEditor.Assets.Processors; +using GlitchyEditor.Assets.Exporters; namespace GlitchyEditor { diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index 855c031..102122f 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -156,6 +156,10 @@ class EditorContentManager : IContentManager for (let (placeholder, loadedAsset) in _newFinishedEntries) { + Log.EngineLogger.Trace($"Dequeue: {Internal.UnsafeCastToPtr(placeholder)} {placeholder.AssetHandle} {placeholder.RefCount}"); + + placeholder.LoadingTask.Wait(); + delete placeholder.LoadingTask; placeholder.LoadingTask = null; @@ -537,6 +541,11 @@ class EditorContentManager : IContentManager AssetHandle = assetHandle; PlaceholderType = placeholderType; } + + public ~this() + { + Log.EngineLogger.Trace($"Deleted: {Internal.UnsafeCastToPtr(this)}"); + } } private append Monitor _finishedEntriesLock = .(); @@ -623,15 +632,18 @@ class EditorContentManager : IContentManager loadedAsset = placeholder; } } + + using (_finishedEntriesLock.Enter()) + { + loadedAsset.Identifier = assetNode.Identifier; + loadedAsset.[Friend]_contentManager = this; + loadedAsset.[Friend]_handle = handle; + + _handleToAsset.Add(handle, loadedAsset); + _identiferToHandle.Add(loadedAsset.Identifier, handle); - loadedAsset.Identifier = assetNode.Identifier; - loadedAsset.[Friend]_contentManager = this; - loadedAsset.[Friend]_handle = handle; - - _handleToAsset.Add(handle, loadedAsset); - _identiferToHandle.Add(loadedAsset.Identifier, handle); - - file.[Friend]_loadedAsset = loadedAsset; + SetReference!(file.[Friend]_loadedAsset, loadedAsset); + } return handle; } @@ -1050,6 +1062,7 @@ class EditorContentManager : IContentManager if (loader.Load(dataStream) case .Ok(let loadedAsset)) { + Log.EngineLogger.Trace($"Loaded asset"); return loadedAsset; } else @@ -1063,7 +1076,8 @@ class EditorContentManager : IContentManager TreeNode assetNode = TrySilent!(AssetHierarchy.GetNodeFromAssetHandle(handle)); Log.EngineLogger.Error($"Failed to load asset \"{assetNode->Identifier}\" ({cachedAsset.Handle}): Could not open stream to cached file."); } - + + Log.EngineLogger.Trace($"Made error placeholder"); NewPlaceholderAsset placeholder = new NewPlaceholderAsset(asset.Handle, .Error); return placeholder; @@ -1074,11 +1088,15 @@ class EditorContentManager : IContentManager else { NewPlaceholderAsset placeholder = new NewPlaceholderAsset(asset.Handle, .Loading); + Log.EngineLogger.Trace($"Created: {Internal.UnsafeCastToPtr(placeholder)} {placeholder.AssetHandle} {placeholder.RefCount}"); + placeholder.LoadingTask = new Task(new () => { + Log.EngineLogger.Trace($"Load: {Internal.UnsafeCastToPtr(placeholder)} {placeholder.AssetHandle} {placeholder.RefCount}"); Asset loadedAsset = InternalLoad(asset); using (_finishedEntriesLock.Enter()) { + Log.EngineLogger.Trace($"Enqueue: {Internal.UnsafeCastToPtr(placeholder)} {placeholder.AssetHandle} {placeholder.RefCount}"); _newFinishedEntries.Add((placeholder, loadedAsset)); } }); diff --git a/GlitchyEditor/src/ImGuiExtension.bf b/GlitchyEditor/src/ImGuiExtension.bf index bdc4cd6..c2f31f2 100644 --- a/GlitchyEditor/src/ImGuiExtension.bf +++ b/GlitchyEditor/src/ImGuiExtension.bf @@ -35,4 +35,43 @@ extension ImGui { return SetDragDropPayload(type.GetName(), data, sz, cond); } + + /// Starts a new row in the table and enters the first column. + public static bool BeginPropertyTable(char8* name, uint32 tableId) + { + return ImGui.BeginTableEx(name, tableId, 2, .SizingStretchSame | .BordersInner | .Resizable); + } + + /// Starts a new row in the table and enters the first column. + public static void PropertyTableStartNewRow() + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + } + + /// Starts a new property by creating a new table row, writing the name in the first column and entering the second column. + public static void PropertyTableStartNewProperty(StringView propertyName) + { + PropertyTableStartNewRow(); + + bool isFirstTableRow = ImGui.TableGetRowIndex() == 0; + + if (isFirstTableRow) + ImGui.PushItemWidth(-1); + + ImGui.TextUnformatted(propertyName); + + ImGui.AttachTooltip(propertyName); + + ImGui.TableSetColumnIndex(1); + + if (isFirstTableRow) + ImGui.PushItemWidth(-1); + } + + public static void PropertyTableStartNewProperty(StringView propertyName, StringView tooltip) + { + PropertyTableStartNewProperty(propertyName); + AttachTooltip(tooltip); + } }