mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Model and Material loading via content manager
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user