EditorContentManager Part 1

This commit is contained in:
Simon Lübeß
2023-01-01 16:53:45 +01:00
parent dffd43a36f
commit c389a77274
29 changed files with 3774 additions and 81 deletions
+111
View File
@@ -0,0 +1,111 @@
using System;
using GlitchyEngine;
using System.IO;
using Bon;
using GlitchyEngine.Content;
namespace GlitchyEditor;
[BonTarget]
class AssetConfig
{
[BonIgnore]
public bool IgnoreFile = false;
[BonInclude]
public String AssetLoader ~ delete _;
[BonInclude]
public AssetLoaderConfig Config ~ delete _;
}
class AssetFile
{
private EditorContentManager _contentManager;
private String _path;
private String _assetConfigPath;
private AssetConfig _assetConfig ~ delete _;
private bool _isDirectory;
public bool IsDirectory => _isDirectory;
public StringView FilePath => _path;
public const String ConfigFileExtension = ".ass";
public AssetConfig AssetConfig => _assetConfig;
[AllowAppend]
public this(EditorContentManager contentManager, StringView path, bool isDirectory)
{
String pathBuffer = append String(path);
String configPathBuffer = append String(path.Length + ConfigFileExtension.Length);
_path = pathBuffer;
configPathBuffer..Append(path).Append(ConfigFileExtension);
_assetConfigPath = configPathBuffer;
_contentManager = contentManager;
_isDirectory = isDirectory;
Log.EngineLogger.AssertDebug(File.Exists(_path), "File doesn't exist.");
FindAssetConfig();
}
// Loads the asset config (.ass) file or creates it.
private void FindAssetConfig()
{
if (File.Exists(_assetConfigPath))
{
LoadAssetConfig();
}
else
{
CreateDefaultAssetLoader();
}
}
private void CreateDefaultAssetLoader()
{
String fileExtension = Path.GetExtension(_path, .. scope .());
_assetConfig = new AssetConfig();
var assetLoader = _contentManager.GetDefaultAssetLoader(fileExtension);
// We don't have a loader -> we don't need a config
if (assetLoader == null)
return;
_assetConfig.AssetLoader = new String();
assetLoader.GetType().GetName(_assetConfig.AssetLoader);
_assetConfig.Config = assetLoader?.GetDefaultConfig();
_assetConfig.Config?.[Friend]_changed = true;
SaveAssetConfig();
}
private void LoadAssetConfig()
{
if (Bon.DeserializeFromFile(ref _assetConfig, _assetConfigPath) case .Err)
{
Log.EngineLogger.Error($"Failed to load asset config {_assetConfigPath}");
// TODO: Handle failure of asset config loading
Runtime.NotImplemented();
}
}
private void SaveAssetConfig()
{
gBonEnv.serializeFlags |= .Verbose;
Bon.SerializeIntoFile(_assetConfig, _assetConfigPath);
}
}
@@ -0,0 +1,154 @@
using System;
using System.Collections;
using Bon;
using System.IO;
using GlitchyEngine;
using GlitchyEngine.Content;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using DirectXTK;
namespace GlitchyEditor.Assets;
[BonTarget, BonPolyRegister]
class EditorTextureAssetLoaderConfig : AssetLoaderConfig
{
[BonInclude]
private bool _generateMipMaps;
[BonInclude]
private SamplerStateDescription _samplerStateDescription = .();
public bool GenerateMipMaps
{
get => _generateMipMaps;
set => SetIfChanged(ref _generateMipMaps, value);
}
public SamplerStateDescription SamplerStateDescription
{
get => _samplerStateDescription;
set => SetIfChanged(ref _samplerStateDescription, value);
}
}
class EditorTextureAssetLoader : IAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp"
public static List<StringView> FileExtensions => _fileExtensions;
public AssetLoaderConfig GetDefaultConfig()
{
return new EditorTextureAssetLoaderConfig();
}
public IRefCounted LoadAsset(Stream data, AssetLoaderConfig config)
{
var config;
if (config == null)
{
config = GetDefaultConfig();
defer:: delete config;
}
Log.EngineLogger.AssertDebug(config is EditorTextureAssetLoaderConfig, "config has wrong type.");
return LoadTexture(data, (EditorTextureAssetLoaderConfig)config);
}
const String PngMagicWord = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
const String DdsMagicWord = "DDS ";
private static Texture LoadTexture(Stream data, EditorTextureAssetLoaderConfig config)
{
Debug.Profiler.ProfileResourceFunction!();
var readResult = data.Read<char8[8]>();
data.Position = 0;
char8[8] magicWord;
Texture texture = null;
if (readResult case .Ok(out magicWord))
{
StringView strView = .(&magicWord, magicWord.Count);
if (strView.StartsWith(PngMagicWord))
{
texture = LoadPng(data, config);
}
else if (strView.StartsWith(DdsMagicWord))
{
texture = LoadDds(data, config);
}
else
{
Runtime.FatalError("Unknown image format.");
}
}
Log.EngineLogger.AssertDebug(texture != null);
/*SamplerStateDescription samplerDesc = .()
{
MinFilter = config.MinFilter,
MagFilter = config.MagFilter,
MipFilter = config.MipFilter,
AddressModeU = config.WrapModeU,
AddressModeV = config.WrapModeV,
AddressModeW = config.WrapModeW
};*/
SamplerState sam = SamplerStateManager.GetSampler(config.SamplerStateDescription);
texture.SamplerState = sam;
sam.ReleaseRef();
return texture;
}
private static Texture LoadPng(Stream data, EditorTextureAssetLoaderConfig config)
{
Debug.Profiler.ProfileResourceFunction!();
uint8[] pngData = new:ScopedAlloc! uint8[data.Length];
var result = data.TryRead(pngData);
if (result case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}");
}
uint8* rawData = ?;
uint32 width = 0, height = 0;
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count);
Log.EngineLogger.Assert(errorCode == 0, "Failed to load png File");
// TODO: load as SRGB because PNGs are usually not stored as linear
//Texture2DDesc desc = .(width, height, srgb? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
Texture2DDesc desc = .(width, height, .R8G8B8A8_UNorm, 1, 1, .Immutable);
Texture2D texture = new Texture2D(desc);
texture.SetData<Color>((.)rawData);
// TODO: Generate mip maps
LodePng.LodePng.Free(rawData);
return texture;
}
private static Texture LoadDds(Stream data, EditorTextureAssetLoaderConfig config)
{
Texture2D texture = new [Friend]Texture2D(data);
return texture;
}
}
@@ -268,7 +268,7 @@ namespace GlitchyEditor.EditWindows
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
Texture2D texture = new Texture2D(path, true);
Texture2D texture = Content.LoadAsset<Texture2D>(path);//new Texture2D(path, true);
spriteRendererComponent.Sprite?.ReleaseRef();
spriteRendererComponent.Sprite = texture;
@@ -319,7 +319,7 @@ namespace GlitchyEditor.EditWindows
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
using (Texture2D newTexture = new Texture2D(path, true))
using (Texture2D newTexture = Content.LoadAsset<Texture2D>(path))//new Texture2D(path, true))
{
newTexture.SamplerState = SamplerStateManager.AnisotropicWrap;
material.SetTexture(texture.key, newTexture);
@@ -16,16 +16,16 @@ namespace GlitchyEditor.EditWindows
const String ContentDirectory = "./content";
//private String _currentDirectory ~ delete _;
private TreeNode<EditorContentManager.AssetNode> _currentDirectoryNode;
private TreeNode<AssetNode> _currentDirectoryNode;
public static SubTexture2D s_FolderTexture;
public static SubTexture2D s_FileTexture;
public EditorContentManager _manager ~ delete _;
public EditorContentManager _manager;
public this()
public this(EditorContentManager contentManager)
{
_manager = new EditorContentManager();
_manager = contentManager;
}
protected override void InternalShow()
+4 -1
View File
@@ -5,6 +5,7 @@ using System.Collections;
using GlitchyEngine.Collections;
using GlitchyEditor.EditWindows;
using GlitchyEngine;
using GlitchyEditor.Assets;
namespace GlitchyEditor
{
@@ -15,7 +16,7 @@ namespace GlitchyEditor
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
private ComponentEditWindow _componentEditWindow ~ delete _;
private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _;
private ContentBrowserWindow _contentBrowserWindow = new .() ~ delete _;
private ContentBrowserWindow _contentBrowserWindow ~ delete _;
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
@@ -43,6 +44,8 @@ namespace GlitchyEditor
CurrentScene = scene;
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
_contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager);
}
public void Update()
+180 -35
View File
@@ -5,6 +5,9 @@ using System.Collections;
using GlitchyEngine.Renderer;
using System.Threading;
using GlitchyEngine.Content;
using GlitchyEditor.Assets;
using GlitchyEngine;
using System.Linq;
namespace GlitchyEditor;
@@ -70,52 +73,71 @@ namespace GlitchyEditor;
}
}*/
class EditorContentManager
public class AssetNode
{
// TODO: Get from project
const String ContentDirectory = "./content";
public String Name ~ delete _;
public String Path ~ delete _;
public bool IsDirectory;
public AssetFile AssetFile ~ delete _;
public List<Asset> SubAssets ~ {
SubAssets?.ClearAndDeleteItems();
delete SubAssets;
}
public Texture2D PreviewImage ~ _?.ReleaseRef();
}
public class Asset
{
public AssetNode Asset;
public String Name ~ delete _;
//public String AssetInternalPath ~ delete _;
public Texture2D PreviewImage ~ _?.ReleaseRef();
}
class EditorContentManager : IContentManager
{
// TODO: Get from workspace
//const String ContentDirectory = "./content";
FileSystemWatcher fsw ~ {
_.StopRaisingEvents();
delete _;
};
bool _fileSystemDirty = true;
public class AssetNode
{
public String Name ~ delete _;
public String Path ~ delete _;
public bool IsDirectory;
public List<Asset> SubAssets ~ {
SubAssets?.ClearAndDeleteItems();
delete SubAssets;
}
public Texture2D PreviewImage ~ _?.ReleaseRef();
}
public class Asset
{
public AssetNode Asset;
public String Name ~ delete _;
//public String AssetInternalPath ~ delete _;
public Texture2D PreviewImage ~ _?.ReleaseRef();
}
bool _fileSystemDirty = false;
internal TreeNode<AssetNode> _assetHierarchy = new TreeNode<AssetNode>() ~ DeleteTreeAndChildren!(_);
private append String _contentDirectory = .();
public StringView ContentDirectory => _contentDirectory;
private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
private append Dictionary<StringView, IRefCounted> _loadedAssets = .(); // Check if all resources are unloaded
public this()
{
}
public void SetContentDirectory(StringView contentDirectory)
{
_contentDirectory.Clear();
_contentDirectory.Append(contentDirectory);
_fileSystemDirty = true;
SetupFileSystemWatcher();
}
/// Initializes the FSW for the current ContentDirectory and registers the events.
private void SetupFileSystemWatcher()
{
delete fsw;
fsw = new FileSystemWatcher(ContentDirectory);
fsw.IncludeSubdirectories = true;
@@ -140,6 +162,7 @@ class EditorContentManager
public void Update()
{
// TODO: do we really need to do this in the update loop?
if (_fileSystemDirty)
{
UpdateFiles();
@@ -148,24 +171,26 @@ class EditorContentManager
private void UpdateFiles()
{
// TODO: we don't need to rebuild the entire tree!
DeleteTreeAndChildren!(_assetHierarchy);
_assetHierarchy = new TreeNode<AssetNode>(new AssetNode());
_assetHierarchy->Path = new String(ContentDirectory);
_assetHierarchy->Name = new String("Content");
String str = scope .(ContentDirectory);
void GrabSubAssets(TreeNode<AssetNode> assetNode)
/*void GrabSubAssets(TreeNode<AssetNode> assetNode)
{
// TODO: Dedicated asset loaders that register their extensions, etc....!
if (assetNode->Name.EndsWith(".png", .OrdinalIgnoreCase))
{
assetNode->SubAssets = new List<Asset>();
assetNode->SubAssets.Add(new Asset()
assetNode->SubAssets = new:allocator List<Asset>();
assetNode->SubAssets.Add(new:allocator Asset()
{
Asset = assetNode.Value,
Name = new String(assetNode->Name)
Name = new:allocator String(assetNode->Name)
});
}
else if (assetNode->Name.EndsWith(".gltf", .OrdinalIgnoreCase) || assetNode->Name.EndsWith(".glb", .OrdinalIgnoreCase))
@@ -173,11 +198,11 @@ class EditorContentManager
List<String> meshNames = scope List<String>();
ModelLoader.GetMeshNames(assetNode->Path, meshNames);
assetNode->SubAssets = new List<Asset>();
assetNode->SubAssets = new:allocator List<Asset>();
for (var meshName in meshNames)
{
assetNode->SubAssets.Add(new Asset()
assetNode->SubAssets.Add(new:allocator Asset()
{
Asset = assetNode.Value,
// Pass ownership of meshName to SubAsset -> save a copy and delete
@@ -185,6 +210,11 @@ class EditorContentManager
});
}
}
}*/
void HandleFile(AssetNode node)
{
node.AssetFile = new AssetFile(this, node.Path, node.IsDirectory);
}
void AddFilesOfDirectory(TreeNode<AssetNode> directory)
@@ -192,11 +222,18 @@ class EditorContentManager
String filter = scope $"{directory->Path}/*";
String buffer = scope String();
String extensionBuffer = scope String();
for (var entry in Directory.Enumerate(filter, .Files))
{
entry.GetFilePath(buffer..Clear());
Path.GetExtension(buffer, extensionBuffer..Clear());
// Don't add meta-files
if (extensionBuffer == ".ass")
continue;
AssetNode e = new AssetNode();
e.Name = new String();
Path.GetFileName(buffer, e.Name);
@@ -206,7 +243,8 @@ class EditorContentManager
e.IsDirectory = entry.IsDirectory;
var node = directory.AddChild(e);
GrabSubAssets(node);
//GrabSubAssets(node);
HandleFile(node.Value);
}
}
@@ -244,4 +282,111 @@ class EditorContentManager
_fileSystemDirty = false;
}
public IAssetLoader GetDefaultAssetLoader(StringView fileExtension)
{
if (_defaultAssetLoaders.TryGetValue(fileExtension, let value))
return value;
return null;
}
private append List<String> _supportedExtensions = .() ~ ClearAndDeleteItems!(_);
private append List<IAssetLoader> _assetLoaders = .() ~ ClearAndDeleteItems!(_);
private append Dictionary<StringView, IAssetLoader> _defaultAssetLoaders = .();
public void RegisterAssetLoader<T>() where T : new, class, IAssetLoader
{
//Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered.");
_assetLoaders.Add(new T());
for (StringView ext in T.FileExtensions)
_supportedExtensions.Add(new String(ext));
}
public void SetAsDefaultAssetLoader<T>(params StringView[] fileExtensions) where T : IAssetLoader
{
for (var ext in fileExtensions)
{
// Find file extension in registered file extensions
String foundExtension = null;
for (var supportedExt in _supportedExtensions)
{
if (supportedExt == ext)
{
foundExtension = supportedExt;
break;
}
}
Log.EngineLogger.Assert(foundExtension != null, "File Extension is not registered.");
for (var loader in _assetLoaders)
{
if (loader.GetType() == typeof(T))
{
_defaultAssetLoaders[foundExtension] = loader;
break;
}
}
}
}
public bool IsLoaded(StringView identifier)
{
return _loadedAssets.ContainsKey(identifier);
}
public IRefCounted LoadAsset(StringView identifier)
{
if (_loadedAssets.TryGetValue(identifier, let asset))
{
return asset..AddRef();
}
String filePath = scope String(identifier.Length + _contentDirectory.Length + 2);
Path.InternalCombine(filePath, _contentDirectory, identifier);
AssetFile file = scope .(this, filePath, false);
IAssetLoader assetLoader = null;
String loaderTypeName = scope .(128);
for (IAssetLoader loader in _assetLoaders)
{
loader.GetType().GetName(loaderTypeName..Clear());
if (loaderTypeName == file.AssetConfig.AssetLoader)
{
assetLoader = loader;
break;
}
}
Log.EngineLogger.AssertDebug(assetLoader != null);
Stream stream = GetStream(filePath);
IRefCounted loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config);
String identifierString = new .(identifier);
_identifiers.Add(identifierString);
_loadedAssets[identifierString] = loadedAsset;
delete stream;
return loadedAsset;
}
// TODO: probably not needed
public Stream GetStream(StringView assetIdentifier)
{
FileStream fs = new FileStream();
var result = fs.Open(assetIdentifier, .Open, .Read, .ReadWrite);
return fs;
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ namespace GlitchyEditor
public this(String texturePath, Vector2 iconSize)
{
_texture = new Texture2D(texturePath);
_texture = Content.LoadAsset<Texture2D>(texturePath);//new Texture2D(texturePath);
Vector2 pen = .();
+16 -2
View File
@@ -12,6 +12,7 @@ using System.Collections;
using GlitchyEngine.Renderer.Animation;
using System.IO;
using GlitchyEngine.Core;
using GlitchyEditor.Assets;
namespace GlitchyEditor
{
@@ -70,6 +71,8 @@ namespace GlitchyEditor
{
Application.Get().Window.IsVSync = false;
InitContentLoader();
InitGraphics();
_camera = EditorCamera(Vector3(3.5f, 1.25f, 2.75f), Quaternion.FromEulerAngles(MathHelper.ToRadians(40), MathHelper.ToRadians(25), 0), MathHelper.ToRadians(75), 0.1f, 1);
@@ -80,6 +83,17 @@ namespace GlitchyEditor
NewScene();
}
private void InitContentLoader()
{
EditorContentManager contentManager = new EditorContentManager();
contentManager.SetContentDirectory("./content");
contentManager.RegisterAssetLoader<EditorTextureAssetLoader>();
contentManager.SetAsDefaultAssetLoader<EditorTextureAssetLoader>(".png", ".dds");
Application.Get().[Friend]_contentManager = contentManager;
}
private void InitGraphics()
{
_context = Application.Get().Window.Context..AddRef();
@@ -327,7 +341,7 @@ namespace GlitchyEditor
// Just for testing
private void TestEntitiesWithModels()
{
{
/*{
var lightNtt = _scene.CreateEntity("My Sexy Sun 2");
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
@@ -449,7 +463,7 @@ namespace GlitchyEditor
}
ClearAndReleaseItems!(clips);
}
}*/
}
/// Creates a new scene.