Convert texture asset and save to cache

This commit is contained in:
Simon Lübeß
2024-05-20 01:08:18 +02:00
parent 2725801db8
commit 1a43ff2971
5 changed files with 223 additions and 26 deletions
+83 -7
View File
@@ -4,6 +4,7 @@ using System.IO;
using System.Collections;
using GlitchyEngine.Core;
using GlitchyEngine.Content;
using System.IO;
namespace GlitchyEditor.Assets;
@@ -21,7 +22,9 @@ class CachedAsset
public AssetType AssetType;
public int64 CompressedByteCount;
public int64 UncompressedByteCount;
public String AssetIdentifier ~ delete _;
public AssetIdentifier AssetIdentifier ~ delete _;
public int32 DataOffset;
public const String CacheFileExtension = ".laf";
}
@@ -93,6 +96,7 @@ class AssetCache
Log.EngineLogger.Error($"Failed to read cached asset file \"filePath\".");
delete cachedAsset;
continue;
}
_assets.Add(cachedAsset.Handle, cachedAsset);
@@ -129,29 +133,101 @@ class AssetCache
cachedAsset.CompressedByteCount = Try!(stream.Read<int64>());
cachedAsset.UncompressedByteCount = Try!(stream.Read<int64>());
int32 assetIdentifierByteCount = Try!(stream.Read<int32>());
int16 assetIdentifierByteCount = Try!(stream.Read<int16>());
cachedAsset.AssetIdentifier = new String(assetIdentifierByteCount);
cachedAsset.AssetIdentifier.PadLeft(assetIdentifierByteCount);
Span<char8> charSpan = cachedAsset.AssetIdentifier;
String assetIdentifier = scope String(assetIdentifierByteCount);
assetIdentifier.PadLeft(assetIdentifierByteCount);
Span<char8> charSpan = assetIdentifier;
int readBytes = Try!(stream.TryRead(Span<uint8>((uint8*)charSpan.Ptr, charSpan.Length)));
cachedAsset.AssetIdentifier = new AssetIdentifier(assetIdentifier);
if (readBytes != assetIdentifierByteCount)
{
Log.EngineLogger.Warning($"Expected to read {assetIdentifierByteCount} bytes for the asset identifier, but read {readBytes} instead.");
}
cachedAsset.DataOffset = (.)stream.Position;
return .Ok;
}
public CachedAsset GetCacheEntry(AssetHandle id)
public CachedAsset GetCacheEntry(AssetHandle assetHandle)
{
if (!_cacheLoaded)
ReloadCache();
if (_assets.TryGetValue(id, let cachedAsset))
if (_assets.TryGetValue(assetHandle, let cachedAsset))
return cachedAsset;
return null;
}
private void GetCacheFilePath(AssetHandle handle, String outFilePath)
{
outFilePath.Append(_directory);
outFilePath.Append(Path.DirectorySeparatorChar);
outFilePath.Append(handle);
outFilePath.Append(CachedAsset.CacheFileExtension);
}
public Result<void> SaveAsset(CachedAsset assetInfo, Span<uint8> data)
{
CachedAsset asset = GetCacheEntry(assetInfo.Handle);
if (asset == null)
{
asset = new CachedAsset();
asset.Handle = assetInfo.Handle;
asset.FilePath = new String(128);
GetCacheFilePath(asset.Handle, asset.FilePath);
_assets[asset.Handle] = asset;
}
delete asset.AssetIdentifier;
asset.AssetIdentifier = new AssetIdentifier(assetInfo.AssetIdentifier);
asset.FormatVersion = FormatVersion;
asset.Compression = assetInfo.Compression;
asset.AssetType = assetInfo.AssetType;
asset.CreationTimestamp = assetInfo.CreationTimestamp;
asset.UncompressedByteCount = data.Length;
Span<uint8> dataToWrite;
switch (asset.Compression)
{
case .L4Z:
// TODO: L4Z
Runtime.NotImplemented();
// asset.CompressedByteCount = compressedData.Length;
case .None:
asset.CompressedByteCount = data.Length;
dataToWrite = data;
}
FileStream writer = scope .();
Try!(writer.Create(asset.FilePath, .Write, .None));
Try!(writer.Write<char8[3]>(CachedAsset.MagicWord));
Try!(writer.Write(FormatVersion));
Try!(writer.Write(asset.Handle));
Try!(writer.Write(asset.CreationTimestamp));
Try!(writer.Write(asset.Compression));
Try!(writer.Write(asset.AssetType));
Try!(writer.Write<int64>(asset.CompressedByteCount));
Try!(writer.Write<int64>(asset.UncompressedByteCount));
Try!(writer.Write<int16>((int16)asset.AssetIdentifier.FullIdentifier.Length));
Try!(writer.Write(asset.AssetIdentifier.FullIdentifier));
asset.DataOffset = (.)writer.Position;
Try!(writer.Write(dataToWrite));
// TODO: Notify content manager!
return .Ok;
}
}
+59 -3
View File
@@ -1,12 +1,15 @@
using System.Collections;
using GlitchyEditor.Assets.Importers;
using GlitchyEngine;
using System;
using System.IO;
using GlitchyEngine.Content;
namespace GlitchyEditor.Assets;
class AssetConverter
{
private append Queue<AssetFile> _queue = .() ~ delete:append _;
private append Queue<AssetFile> _queue = .();
private EditorContentManager _contentManager;
@@ -36,15 +39,68 @@ class AssetConverter
private void Process(AssetFile assetFile)
{
IAssetImporter importer = _contentManager.GetAssetImporter(assetFile);
IAssetProcessor processor = _contentManager.GetAssetProcessor(assetFile);
IAssetExporter exporter = _contentManager.GetAssetExporter(assetFile);
if (importer == null)
{
Log.EngineLogger.Error("Importer is null!");
return;
}
ImportedResource importedResource = importer.Import(assetFile.AssetFile.Path,
if (processor == null)
{
Log.EngineLogger.Error("Processor is null!");
return;
}
if (exporter == null)
{
Log.EngineLogger.Error("Exporter is null!");
return;
}
Result<ImportedResource> importResult = importer.Import(assetFile.AssetFile.Path,
assetFile.AssetFile.Identifier, assetFile.AssetConfig.ImporterConfig);
delete importedResource;
if (importResult case .Err)
{
Log.EngineLogger.Error("Failed to import asset!");
return;
}
defer { delete importResult.Value; }
Result<ProcessedResource> processResult = processor.Process(importResult.Value, assetFile.AssetConfig.ProcessorConfig);
if (processResult case .Err)
{
Log.EngineLogger.Error("Failed to process asset!");
return;
}
defer { delete processResult.Value; }
MemoryStream memoryStream = scope .();
Result<void> exportResult = exporter.Export(memoryStream, processResult.Value, assetFile.AssetConfig.ExporterConfig);
if (exportResult case .Err)
{
Log.EngineLogger.Error("Failed to export asset!");
return;
}
CachedAsset assetInfo = scope CachedAsset();
assetInfo.Handle = assetFile.AssetConfig.AssetHandle;
assetInfo.CreationTimestamp = DateTime.UtcNow;
assetInfo.Compression = assetFile.AssetConfig.ExporterConfig.Compression;
assetInfo.AssetIdentifier = new AssetIdentifier(assetFile.AssetFile.Identifier);
assetInfo.AssetType = processResult.Value.AssetType;
if (_contentManager.AssetCache.SaveAsset(assetInfo, memoryStream.Memory) case .Err)
{
Log.EngineLogger.Error("Failed to write asset to cache.");
}
}
}
@@ -18,7 +18,7 @@ interface IAssetProcessor
{
AssetProcessorConfig CreateDefaultConfig();
Result<Object> Process(ImportedResource importedResource, AssetProcessorConfig config);
Result<ProcessedResource> Process(ImportedResource importedResource, AssetProcessorConfig config);
}
interface IAssetExporter
@@ -186,6 +186,9 @@ class TextureImporter : IAssetImporter
surface.SlicePitch = 0;
surface.ArrayIndex = 0;
surface.MipLevel = 0;
surface.Width = width;
surface.Height = height;
surface.Depth = 1;
surfaces.Add(surface);
@@ -238,12 +241,14 @@ class TextureProcessorConfig : AssetProcessorConfig
}
}
class ProcessedResource
abstract class ProcessedResource
{
private AssetIdentifier _assetIdentifier ~ delete _;
public AssetIdentifier AssetIdentifier => _assetIdentifier;
public abstract AssetType AssetType {get;}
public this(AssetIdentifier ownAssetIdentifier)
{
_assetIdentifier = ownAssetIdentifier;
@@ -262,6 +267,8 @@ class ProcessedTexture : ProcessedResource
public int Height = -1;
public int Depth = -1;
public override AssetType AssetType => .Texture;
public class TextureSurface
{
public uint8[] PixelData;
@@ -335,17 +342,23 @@ class ProcessedTexture : ProcessedResource
public void SetSurfaceCount(int arraySize, int mipMapCount)
{
Log.EngineLogger.AssertDebug(arraySize > Surfaces.GetLength(0));
Log.EngineLogger.AssertDebug(mipMapCount > Surfaces.GetLength(1));
Log.EngineLogger.AssertDebug(Surfaces == null || arraySize > Surfaces.GetLength(0));
Log.EngineLogger.AssertDebug(Surfaces == null ||mipMapCount > Surfaces.GetLength(1));
ArraySize = arraySize;
MipMapCount = mipMapCount;
TextureSurface[,] oldSurfaces = Surfaces;
Surfaces = new TextureSurface[arraySize, mipMapCount];
for (int i < oldSurfaces.GetLength(0))
if (oldSurfaces != null)
{
for (int j < oldSurfaces.GetLength(1))
for (int i < oldSurfaces.GetLength(0))
{
Surfaces[i, j] = oldSurfaces[i, j];
for (int j < oldSurfaces.GetLength(1))
{
Surfaces[i, j] = oldSurfaces[i, j];
}
}
}
}
@@ -358,20 +371,25 @@ class TextureProcessor : IAssetProcessor
return new TextureProcessorConfig();
}
public Result<Object> Process(ImportedResource importedObject, AssetProcessorConfig config)
public Result<ProcessedResource> Process(ImportedResource importedObject, AssetProcessorConfig config)
{
Log.EngineLogger.AssertDebug(config is TextureProcessorConfig);
Log.EngineLogger.AssertDebug(importedObject is ImportedTexture);
Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig));
return .Ok(null);
return Try!(ProcessTexture(importedObject as ImportedTexture, config as TextureProcessorConfig));
}
private Result<void> ProcessTexture(ImportedTexture importedTexture, TextureProcessorConfig config)
private Result<ProcessedTexture> 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.SetSurfaceCount(importedTexture.TextureInfo.ArraySize, importedTexture.TextureInfo.MipMapCount);
for (LoadedSurface loadedSurface in importedTexture.Surfaces)
@@ -390,7 +408,7 @@ class TextureProcessor : IAssetProcessor
// TODO: Pack to BC-Format or what ever was selected.
return .Ok;
return processedTexture;
}
private int CalculateMipMapCount(int width, int height, int depth)
+48 -1
View File
@@ -27,7 +27,7 @@ class EditorContentManager : IContentManager
private append AssetHierarchy _assetHierarchy = .(this);
private append AssetCache _assetCache = .() ~ delete:append _;
private append AssetConverter _assetConverter = .(this) ~ delete:append _;
private append AssetConverter _assetConverter = .(this);
private append List<AssetHandle> _reloadQueue = .();
@@ -470,6 +470,13 @@ class EditorContentManager : IContentManager
return .Invalid;
}
CachedAsset cacheEntry = _assetCache.GetCacheEntry(handle);
if (cacheEntry != null)
{
// TODO: Load asset with new loaders
}
String filePath = scope String(resultNode->Value.Path);
AssetNode assetNode = resultNode->Value;
@@ -715,6 +722,46 @@ class EditorContentManager : IContentManager
return result;
}
public IAssetProcessor GetAssetProcessor(AssetFile file)
{
IAssetProcessor result = null;
String typeName = scope .(128);
for (IAssetProcessor processor in _assetProcessors)
{
processor.GetType().GetName(typeName..Clear());
if (typeName == file.AssetConfig.Processor)
{
result = processor;
break;
}
}
return result;
}
public IAssetExporter GetAssetExporter(AssetFile file)
{
IAssetExporter result = null;
String typeName = scope .(128);
for (IAssetExporter exporter in _assetExporters)
{
exporter.GetType().GetName(typeName..Clear());
if (typeName == file.AssetConfig.Exporter)
{
result = exporter;
break;
}
}
return result;
}
public enum SaveAssetError
{
case Unknown;