Model and Material loading via content manager

This commit is contained in:
Simon Lübeß
2023-01-07 12:15:14 +01:00
parent ee30920b56
commit 3da60355df
25 changed files with 859 additions and 267 deletions
@@ -0,0 +1,4 @@
{
AssetLoader = "ModelAssetLoader",
Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){}
}
@@ -0,0 +1,4 @@
{
AssetLoader = "ModelAssetLoader",
Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){}
}
@@ -0,0 +1,7 @@
{
Effect = "content/Shaders/myEffect.hlsl",
Textures =
[
"AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png"
]
}
@@ -0,0 +1,4 @@
{
AssetLoader = "MaterialAssetLoader",
Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){}
}
@@ -1,9 +1,9 @@
{
AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_isSrgb = true,
_samplerStateDescription = {
MinFilter = .Linear,
MagFilter = .Point,
MipFilter = .Linear,
ComparisonFunction = .Never,
AddressModeU = .Clamp,
@@ -1,10 +1,9 @@
{
AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_isSrgb = true,
_samplerStateDescription = {
MinFilter = .Linear,
MagFilter = .Linear,
MipFilter = .Linear,
ComparisonFunction = .Never,
AddressModeU = .Clamp,
AddressModeV = .Clamp,
@@ -0,0 +1,15 @@
namespace GlitchyEditor.Assets;
abstract class AssetPropertiesEditor
{
private AssetFile _asset;
public AssetFile Asset => _asset;
public this(AssetFile asset)
{
_asset = asset;
}
public abstract void ShowEditor();
}
@@ -0,0 +1,8 @@
using System.IO;
namespace GlitchyEditor.Assets;
interface IReloadingAssetLoader
{
public void ReloadAsset(AssetFile assetFile, Stream data);
}
@@ -0,0 +1,106 @@
using Bon;
using GlitchyEngine.Content;
using System;
using System.Collections;
using System.IO;
using GlitchyEngine;
using GlitchyEngine.Renderer;
namespace GlitchyEditor.Assets;
class MaterialAssetPropertiesEditor : AssetPropertiesEditor
{
public this(AssetFile asset) : base(asset)
{
}
public override void ShowEditor()
{
}
public static AssetPropertiesEditor Factory(AssetFile assetFile)
{
return new Self(assetFile);
}
}
[BonTarget, BonPolyRegister]
class MaterialAssetLoaderConfig : AssetLoaderConfig
{
}
[BonTarget]
class MaterialFile
{
public String Effect ~ delete _;
public Dictionary<String, String> Textures ~ DeleteDictionaryAndKeysAndValues!(_);
//public Dictionary<String, Object> Variables;
}
class MaterialAssetLoader : IAssetLoader //, IReloadingAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".mat"} ~ delete _;
public static List<StringView> FileExtensions => _fileExtensions;
public AssetLoaderConfig GetDefaultConfig()
{
return new ModelAssetLoaderConfig();
}
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView? subAsset, IContentManager contentManager)
{
StreamReader reader = scope .(file);
String text = scope .();
reader.ReadToEnd(text);
MaterialFile materialFile = scope .();
var result = Bon.Deserialize<MaterialFile>(ref materialFile, text);
if (result case .Err)
{
Log.EngineLogger.Error("Failed to load material.");
return null;
// TODO: return error material
}
Effect fx = new Effect(materialFile.Effect);
Material material = new Material(fx);
for (let (slotName, textureIdentifier) in materialFile.Textures)
{
Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture;
if (texture == null)
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
}
material.SetTexture(slotName, texture);
}
fx.ReleaseRef();
/*for (let (slotName, textureIdentifier) in materialFile.Variables)
{
if (texture == null)
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
}
material.SetVariable(slotName, );
}*/
return material; //ModelLoader.LoadMesh(file, subAsset.Value, 0);
}
}
@@ -0,0 +1,51 @@
using Bon;
using GlitchyEngine.Content;
using System;
using System.Collections;
using System.IO;
using GlitchyEngine;
namespace GlitchyEditor.Assets;
class ModelAssetPropertiesEditor : AssetPropertiesEditor
{
public this(AssetFile asset) : base(asset)
{
}
public override void ShowEditor()
{
}
public static AssetPropertiesEditor Factory(AssetFile assetFile)
{
return new ModelAssetPropertiesEditor(assetFile);
}
}
[BonTarget, BonPolyRegister]
class ModelAssetLoaderConfig : AssetLoaderConfig
{
}
class ModelAssetLoader : IAssetLoader //, IReloadingAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".gltf", ".glb"} ~ delete _;
public static List<StringView> FileExtensions => _fileExtensions;
public AssetLoaderConfig GetDefaultConfig()
{
return new ModelAssetLoaderConfig();
}
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView? subAsset, IContentManager contentManager)
{
Log.EngineLogger.Assert(subAsset != null);
return ModelLoader.LoadMesh(file, subAsset.Value, 0);
}
}
+1 -21
View File
@@ -11,21 +11,6 @@ using ImGui;
namespace GlitchyEditor.Assets;
abstract class AssetPropertiesEditor
{
private AssetFile _asset;
public AssetFile Asset => _asset;
public this(AssetFile asset)
{
_asset = asset;
}
public abstract void ShowEditor();
}
class TextureAssetPropertiesEditor : AssetPropertiesEditor
{
EditorTextureAssetLoaderConfig _textureConfig;
@@ -179,11 +164,6 @@ class EditorTextureAssetLoaderConfig : AssetLoaderConfig
}
}
interface IReloadingAssetLoader
{
public void ReloadAsset(AssetFile assetFile, Stream data);
}
class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp"
@@ -195,7 +175,7 @@ class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
return new EditorTextureAssetLoaderConfig();
}
public IRefCounted LoadAsset(Stream data, AssetLoaderConfig config)
public Asset LoadAsset(Stream data, AssetLoaderConfig config, StringView? subAsset, IContentManager contentManager)
{
var config;
@@ -287,10 +287,31 @@ namespace GlitchyEditor.EditWindows
{
// TODO: Editing material options obviously shouldn't be part of the meshrenderer-ui
ImGui.Button("Drag Material here!");
if (ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM");
if (payload != null)
{
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
using (Material material = Content.LoadAsset<Material>(fullpath))
{
meshRendererComponent.Material = material;
}
}
ImGui.EndDragDropTarget();
}
Material material = meshRendererComponent.Material;
Effect effect = material.Effect;
Effect effect = material?.Effect;
if (effect == null)
return;
bool TryGetValue(Dictionary<String, Variant> parameters, String name, out Variant value)
{
if (parameters.TryGetValue(name, let param))
@@ -568,7 +589,7 @@ namespace GlitchyEditor.EditWindows
{
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
int idx = fullpath.IndexOf('#');
/*int idx = fullpath.IndexOf('#');
if (idx == -1)
{
@@ -577,13 +598,18 @@ namespace GlitchyEditor.EditWindows
}
StringView filePath = fullpath.Substring(0, idx);
StringView meshName = fullpath.Substring(idx + 1);
StringView meshName = fullpath.Substring(idx + 1);*/
using (GeometryBinding geometry = Content.LoadAsset<GeometryBinding>(fullpath))
{
meshComponent.Mesh = geometry;
}
// TODO: support multiple primitives (treat every primitive as a single mesh?)
using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0))
/*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0))
{
meshComponent.Mesh = binding;
}
}*/
//ModelLoader.LoadModel(scope .(path), )
@@ -648,6 +674,8 @@ namespace GlitchyEditor.EditWindows
ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D");
ShowComponentButton<BoxCollider2DComponent>("Box collider 2D");
ShowComponentButton<CircleCollider2DComponent>("Circle collider 2D");
ShowComponentButton<MeshComponent>("Mesh");
ShowComponentButton<MeshRendererComponent>("Mesh Renderer");
ImGui.EndCombo();
}
@@ -73,7 +73,8 @@ namespace GlitchyEditor.EditWindows
{
if (ImGui.MenuItem("Open in file browser..."))
{
Path.OpenFolder(_currentDirectory);
if (Path.OpenFolder(_currentDirectory) case .Err)
Log.EngineLogger.Error("Failed to open directory in file browser.");
}
}
@@ -294,11 +295,24 @@ namespace GlitchyEditor.EditWindows
/// Shows the context menu for the given file/folder.
private void ShowItemContextMenu(TreeNode<AssetNode> fileOrFolder)
{
bool isFile = !fileOrFolder->IsDirectory;
if (ImGui.MenuItem("Show in file browser..."))
{
Path.OpenFolderAndSelectItem(fileOrFolder->Path);
if (Path.OpenFolderAndSelectItem(fileOrFolder->Path) case .Err)
{
Log.EngineLogger.Error("Failed to show path in file browser.");
}
}
if (isFile && ImGui.MenuItem("Open file with..."))
{
if (Path.OpenWithDialog(fileOrFolder->Path) case .Err)
{
Log.EngineLogger.Error("Failed to show \"Open with...\" dialog.");
}
}
if (ImGui.MenuItem("Delete"))
{
ImGui.OpenPopup("Delete?");
@@ -311,6 +325,11 @@ namespace GlitchyEditor.EditWindows
{
_currentDirectory.Set(entry->Path);
}
else
{
if (Path.OpenFolder(entry->Path) case .Err)
Log.EngineLogger.Error("Failed to open directory in file browser.");
}
}
}
}
+65 -16
View File
@@ -82,7 +82,7 @@ public class AssetNode
public AssetFile AssetFile ~ delete _;
public List<Asset> SubAssets ~ {
public List<SubAsset> SubAssets ~ {
SubAssets?.ClearAndDeleteItems();
delete SubAssets;
}
@@ -90,7 +90,7 @@ public class AssetNode
public Texture2D PreviewImage ~ _?.ReleaseRef();
}
public class Asset
public class SubAsset
{
public AssetNode Asset;
public String Name ~ delete _;
@@ -404,16 +404,13 @@ class AssetHierarchy
class EditorContentManager : IContentManager
{
// TODO: Get from workspace
//const String ContentDirectory = "./content";
private append String _contentDirectory = .();
public StringView ContentDirectory => _contentDirectory;
private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
//private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
private append Dictionary<StringView, IRefCounted> _loadedAssets = .(); // Check if all resources are unloaded
private append Dictionary<StringView, Asset> _loadedAssets = .(); // Check if all resources are unloaded
private append AssetHierarchy _assetHierarchy = .(this);
@@ -424,8 +421,15 @@ class EditorContentManager : IContentManager
_assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged);
}
public ~this()
{
UnmanageAllAssets();
}
private void OnFileContentChanged(AssetNode assetNode)
{
// TODO: Subassets break reloading because we can't find them when we only receive the file that changed...
// Asset isn't loaded so we don't need to reload it.
if (assetNode.AssetFile.LoadedAsset == null)
return;
@@ -565,15 +569,21 @@ class EditorContentManager : IContentManager
return _loadedAssets.ContainsKey(identifier);
}
public IRefCounted LoadAsset(StringView identifier)
public Asset LoadAsset(StringView identifier)
{
if (_loadedAssets.TryGetValue(identifier, let asset))
{
return asset..AddRef();
}
String filePath = scope String(identifier.Length + _contentDirectory.Length + 2);
Path.Combine(filePath, _contentDirectory, identifier);
// Find subasset name
int poundIndex = identifier.IndexOf('#');
StringView resourceName = poundIndex == -1 ? identifier : identifier.Substring(0, poundIndex);
StringView? subassetName = identifier.Substring(poundIndex + 1);
String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2);
Path.Combine(filePath, _contentDirectory, resourceName);
Path.Fixup(filePath);
@@ -606,14 +616,19 @@ class EditorContentManager : IContentManager
Stream stream = GetStream(filePath);
IRefCounted loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config);
String identifierString = new .(identifier);
_identifiers.Add(identifierString);
_loadedAssets[identifierString] = loadedAsset;
Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, subassetName, this);
delete stream;
//String identifierString = new .(identifier);
//_identifiers.Add(identifierString);
//_loadedAssets[identifierString] = loadedAsset;
loadedAsset.Identifier = identifier;
ManageAsset(loadedAsset);
file.[Friend]_loadedAsset = loadedAsset;
return loadedAsset;
@@ -627,4 +642,38 @@ class EditorContentManager : IContentManager
return fs;
}
public void ManageAsset(Asset asset)
{
_loadedAssets.Add(asset.Identifier, asset);
asset.[Friend]_contentManager = this;
}
public void UnmanageAsset(Asset asset)
{
_loadedAssets.Remove(asset.Identifier);
asset.[Friend]_contentManager = null;
}
/// This will unregister all assets from this content manager.
/// Note: This will not release any assets.
private void UnmanageAllAssets()
{
for (let (_, asset) in _loadedAssets)
{
UnmanageAsset(asset);
}
}
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier)
{
if (oldIdentifier == newIdentifier)
return;
Log.EngineLogger.Assert(_loadedAssets.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager.");
// Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again.
UnmanageAsset(asset);
ManageAsset(asset);
}
}
+10 -2
View File
@@ -88,11 +88,19 @@ namespace GlitchyEditor
private void InitContentManager()
{
_contentManager = new EditorContentManager();
_contentManager.SetContentDirectory("./content");
_contentManager.RegisterAssetLoader<EditorTextureAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EditorTextureAssetLoader>(".png", ".dds");
_contentManager.SetAssetPropertiesEditor<EditorTextureAssetLoader>(=> TextureAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<ModelAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<ModelAssetLoader>(".glb", ".gltf");
_contentManager.SetAssetPropertiesEditor<ModelAssetLoader>(=> ModelAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<MaterialAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<MaterialAssetLoader>(".mat");
_contentManager.SetAssetPropertiesEditor<MaterialAssetLoader>(=> MaterialAssetPropertiesEditor.Factory);
_contentManager.SetContentDirectory("./content");
// Todo: Sketchy...
Application.Get().[Friend]_contentManager = _contentManager;