diff --git a/GlitchyEditor/content/Textures/TestMaterial.mat b/GlitchyEditor/content/Textures/TestMaterial.mat index f877d23..aa4142c 100644 --- a/GlitchyEditor/content/Textures/TestMaterial.mat +++ b/GlitchyEditor/content/Textures/TestMaterial.mat @@ -1,7 +1,10 @@ { - Effect = "content/Shaders/myEffect.hlsl", + Effect = "Shaders/myEffect.hlsl", Textures = [ - "AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png" + "AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png", + "NormalTexture": "Textures\\TestMat\\rustediron2_normal.png", + "MetallicTexture": "", + "RoughnessTexture": "" ], Variables = [ "AlbedoColor": .ColorRGBA{ diff --git a/GlitchyEditor/src/AssetFile.bf b/GlitchyEditor/src/AssetFile.bf index 56e9d38..75c814e 100644 --- a/GlitchyEditor/src/AssetFile.bf +++ b/GlitchyEditor/src/AssetFile.bf @@ -31,7 +31,7 @@ class AssetFile private bool _isDirectory; - private Object _loadedAsset; + private Asset _loadedAsset; public bool IsDirectory => _isDirectory; @@ -42,7 +42,7 @@ class AssetFile public AssetConfig AssetConfig => _assetConfig; - public Object LoadedAsset => _loadedAsset; + public Asset LoadedAsset => _loadedAsset; [AllowAppend] public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory) diff --git a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf index 2ab9688..9433146 100644 --- a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf +++ b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf @@ -87,7 +87,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor AssetHandle newTexture = Content.LoadAsset(path);//new Texture2D(path, true)) newTexture.Get().SamplerState = SamplerStateManager.AnisotropicWrap; - //material.SetTexture(texture.key, newTexture); + material.SetTexture(texture.key, newTexture); // TODO!!! } @@ -344,25 +344,22 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader // TODO: return error material } - Effect fx = new Effect(materialFile.Effect); + Effect fx = Content.GetAsset(contentManager.LoadAsset(materialFile.Effect), contentManager);//new Effect(materialFile.Effect); Material material = new Material(fx); for (let (slotName, textureIdentifier) in materialFile.Textures) { - using (Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture) + Texture texture = Content.GetAsset(contentManager.LoadAsset(textureIdentifier), contentManager); + + if (texture == null) { - if (texture == null) - { - Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\"."); - // TODO: LoadAsset should return an error texture. - } - - material.SetTexture(slotName, texture); + Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\"."); + // TODO: LoadAsset should return an error texture. } - } - fx.ReleaseRef(); + material.SetTexture(slotName, texture); + } for (let (slotName, variableValue) in materialFile.Variables) { @@ -409,10 +406,9 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader //material.SetTexture(); - for (let (slotName, textureViewBinding) in material.[Friend]_textures) + for (let (slotName, texture) in material.[Friend]_textures) { - //materialFile.Textures.Add(slotName, textureViewBinding.) - + materialFile.Textures.Add(new String(slotName), new String(texture?.Identifier ?? "")); } Effect effect = material.Effect; @@ -513,7 +509,7 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader }*/ } - materialFile.Variables.Add(name, variableValue); + materialFile.Variables.Add(new String(name), variableValue); } String text = scope .(); diff --git a/GlitchyEditor/src/Assets/TextureAssetLoader.bf b/GlitchyEditor/src/Assets/TextureAssetLoader.bf index b1000fb..dff5855 100644 --- a/GlitchyEditor/src/Assets/TextureAssetLoader.bf +++ b/GlitchyEditor/src/Assets/TextureAssetLoader.bf @@ -164,7 +164,7 @@ class EditorTextureAssetLoaderConfig : AssetLoaderConfig } } -class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader +class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader { private static readonly List _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp" diff --git a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf index ec279af..09bdf42 100644 --- a/GlitchyEditor/src/EditWindows/PropertiesWindow.bf +++ b/GlitchyEditor/src/EditWindows/PropertiesWindow.bf @@ -19,7 +19,7 @@ class PropertiesWindow : EditorWindow private append String _selectedFileName = .(); private AssetHandle _currentAssetHandle; - private Asset _currentAsset; + //private Asset _currentAsset; public this(Editor editor) { @@ -74,14 +74,22 @@ class PropertiesWindow : EditorWindow if (assetFile == null) return; + + Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle); // We need the actual asset for preview and sometimes for editing - if (_currentAsset?.Identifier != assetFile.Identifier) + if (asset?.Identifier != assetFile.Identifier) + { + _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.Identifier); + } + + /*if (asset != _currentAsset) { _currentAsset?.ReleaseRef(); - _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.Identifier); - _currentAsset = _editor.ContentManager.GetAsset(null, _currentAssetHandle); - } + _currentAsset = asset; + _currentAsset?.AddRef(); + }*/ + // TODO: allow changing AssetLoader // assetFile.AssetConfig.AssetLoade @@ -108,7 +116,8 @@ class PropertiesWindow : EditorWindow if (ImGui.Button("Save Asset")) { - _editor.ContentManager.SaveAsset(_currentAsset); + Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle); + _editor.ContentManager.SaveAsset(asset); } if (!assetFile.AssetConfig.Config.Changed) diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index 0db13cc..86d30e2 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -81,7 +81,7 @@ class EditorContentManager : IContentManager //private append List _identifiers = .() ~ _.ClearAndDeleteItems(); - private append Dictionary _handles = .(); // TODO: Check if all resources are unloaded + private append Dictionary _identiferToHandle = .(); // TODO: Check if all resources are unloaded private append Dictionary _handleToAsset = .(); @@ -89,6 +89,8 @@ class EditorContentManager : IContentManager public AssetHierarchy AssetHierarchy => _assetHierarchy; + private append List _reloadQueue = .(); + public this() { _assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged); @@ -109,7 +111,9 @@ class EditorContentManager : IContentManager if (assetNode.AssetFile.LoadedAsset == null) return; - String neededAssetLoaderName = assetNode.AssetFile.AssetConfig?.AssetLoader; + _reloadQueue.Add(assetNode.AssetFile.LoadedAsset.Handle); + + /*String neededAssetLoaderName = assetNode.AssetFile.AssetConfig?.AssetLoader; if (String.IsNullOrWhiteSpace(neededAssetLoaderName)) return; @@ -133,16 +137,18 @@ class EditorContentManager : IContentManager { Log.EngineLogger.Error($"Could not find asset loader \"{neededAssetLoaderName}\""); return; - } + }*/ - if (var assetReloader = assetLoader as IReloadingAssetLoader) + /*if (var assetReloader = assetLoader as IReloadingAssetLoader) { Stream stream = GetStream(assetNode.Path); - assetReloader.ReloadAsset(assetNode.AssetFile, stream); + // TODO: reload asset + + //assetReloader.ReloadAsset(assetNode.AssetFile, stream); delete stream; - } + }*/ } public void SetContentDirectory(StringView contentDirectory) @@ -156,6 +162,15 @@ class EditorContentManager : IContentManager public void Update() { + if (!_reloadQueue.IsEmpty) + { + for (AssetHandle handle in _reloadQueue) + { + ReloadAsset(handle); + } + _reloadQueue.Clear(); + } + _assetHierarchy.Update(); } @@ -241,7 +256,7 @@ class EditorContentManager : IContentManager public bool IsLoaded(StringView identifier) { - return _handles.ContainsKey(identifier); + return _identiferToHandle.ContainsKey(identifier); } public Asset GetAsset(Type assetType, AssetHandle handle) @@ -254,7 +269,7 @@ class EditorContentManager : IContentManager { return asset; } - else if (asset.GetType() == assetType) + else if (asset?.GetType().IsSubtypeOf(assetType) ?? false) { return asset; } @@ -266,9 +281,74 @@ class EditorContentManager : IContentManager } } + private void ReloadAsset(AssetHandle handle) + { + Asset asset = null; + + if (!_handleToAsset.TryGetValue(handle, out asset)) + { + Log.EngineLogger.Error("Can't reload! No asset exists for handle."); + + return; + } + + Log.EngineLogger.AssertDebug(asset != null); + + StringView oldIdentifier = asset.Identifier; + + // Find subasset name + int poundIndex = oldIdentifier.IndexOf('#'); + + StringView resourceName = poundIndex == -1 ? oldIdentifier : oldIdentifier.Substring(0, poundIndex); + StringView? subassetName = oldIdentifier.Substring(poundIndex + 1); + + String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2); + Path.Combine(filePath, _contentDirectory, resourceName); + + Path.Fixup(filePath); + + //filePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + Result> resultNode = AssetHierarchy.GetNodeFromPath(filePath); + + if (resultNode case .Err) + { + Log.EngineLogger.Error($"Could not find asset \"{filePath}\"."); + return; + } + + AssetFile file = resultNode->Value.AssetFile; + + IAssetLoader assetLoader = GetAssetLoader(file); + + Log.EngineLogger.AssertDebug(assetLoader != null); + + Stream stream = GetStream(filePath); + + Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); + + delete stream; + + if (loadedAsset == null) + return; + + loadedAsset.Identifier = oldIdentifier; + loadedAsset.[Friend]_handle = handle; + + // Remove asset + _identiferToHandle.Remove(oldIdentifier); + Asset oldAsset = _handleToAsset[handle]; + oldAsset.ReleaseRef(); + + _handleToAsset[handle] = loadedAsset; + _identiferToHandle.Add(loadedAsset.Identifier, handle); + + file.[Friend]_loadedAsset = loadedAsset; + } + public AssetHandle LoadAsset(StringView identifier) { - if (_handles.TryGetValue(identifier, let asset)) + if (_identiferToHandle.TryGetValue(identifier, let asset)) { return asset; } @@ -296,20 +376,7 @@ class EditorContentManager : IContentManager AssetFile file = resultNode->Value.AssetFile; - 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; - } - } + IAssetLoader assetLoader = GetAssetLoader(file); Log.EngineLogger.AssertDebug(assetLoader != null); @@ -329,12 +396,38 @@ class EditorContentManager : IContentManager loadedAsset.Identifier = identifier; AssetHandle handle = ManageAsset(loadedAsset); + // ManageAsset increases RefCount + loadedAsset.ReleaseRef(); + + // Add to Identifier -> Handle map + _identiferToHandle.Add(loadedAsset.Identifier, handle); file.[Friend]_loadedAsset = loadedAsset; return handle; } + /// Gets the asset loader that has to be used for the given file. + IAssetLoader GetAssetLoader(AssetFile file) + { + 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; + } + } + + return assetLoader; + } + /// Saves the asset. public Result SaveAsset(Asset asset) { @@ -355,20 +448,7 @@ class EditorContentManager : IContentManager AssetFile file = assetNode->AssetFile; - 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; - } - } + IAssetLoader assetLoader = GetAssetLoader(file); IAssetSaver assetSaver = assetLoader as IAssetSaver; @@ -382,6 +462,7 @@ class EditorContentManager : IContentManager assetSaver.EditorSaveAsset(stream, asset, file.AssetConfig.Config, resourceName, subassetName, this); + stream.SetLength(stream.Position); delete stream; return .Ok; @@ -403,8 +484,8 @@ class EditorContentManager : IContentManager FileMode fileMode = openOnly ? FileMode.Open : FileMode.OpenOrCreate; - if (truncate) - fileMode |= .Truncate; + /*if (truncate) + fileMode |= .Truncate;*/ var result = fs.Open(assetIdentifier, fileMode, openOnly ? .Read : .ReadWrite, .ReadWrite); @@ -440,26 +521,39 @@ class EditorContentManager : IContentManager public AssetHandle ManageAsset(Asset asset) { - AssetHandle handle = .(asset.Identifier); + Log.EngineLogger.AssertDebug(asset.Handle == .Invalid, "Asset is already managed."); + Log.EngineLogger.AssertDebug(asset.ContentManager == null, "Asset is already managed."); - // TODO: to ensure that no two assets with the same handle exist. + AssetHandle handle = .(); - _handles.Add(asset.Identifier, handle); + // Generate until we find a unique key (shouldn't happen too often) + while (_handleToAsset.ContainsKey(handle)) + { + handle = .(); + // TODO: perhaps test how often this happens. + // If this happens too often we could use a different random generator + } + + //_handles.Add(asset.Identifier, handle); _handleToAsset.Add(handle, asset); asset.[Friend]_contentManager = this; asset.[Friend]_handle = handle; + asset.AddRef(); return handle; } public void UnmanageAsset(AssetHandle handle) { - Log.EngineLogger.AssertDebug(_handles.ContainsValue(handle), "Handle isn't managed by this content manager."); + //Log.EngineLogger.AssertDebug(_handles.ContainsValue(handle), "Handle isn't managed by this content manager."); Log.EngineLogger.AssertDebug(_handleToAsset.ContainsKey(handle), "Handle doesn't correspond to an asset."); Asset asset = _handleToAsset[handle]; - _handles.Remove(asset.Identifier); + + if (_identiferToHandle.ContainsKey(asset.Identifier)) + _identiferToHandle.Remove(asset.Identifier); + _handleToAsset.Remove(handle); asset.[Friend]_contentManager = null; @@ -470,7 +564,7 @@ class EditorContentManager : IContentManager /// Note: This will not release any assets. private void UnmanageAllAssets() { - for (let (_, assetHandle) in _handles) + for (let (_, assetHandle) in _identiferToHandle) { UnmanageAsset(assetHandle); } @@ -485,7 +579,7 @@ class EditorContentManager : IContentManager if (oldIdentifier == newIdentifier) return; - Log.EngineLogger.Assert(_handles.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager."); + Log.EngineLogger.Assert(_identiferToHandle.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); diff --git a/GlitchyEngine/src/Content/Asset.bf b/GlitchyEngine/src/Content/Asset.bf index efd0ffb..3e57a0f 100644 --- a/GlitchyEngine/src/Content/Asset.bf +++ b/GlitchyEngine/src/Content/Asset.bf @@ -10,7 +10,7 @@ namespace GlitchyEngine.Content; [BonTarget] class Asset : RefCounter { - internal AssetHandle _handle; + internal AssetHandle _handle = .Invalid; private append String _identifier; @@ -60,16 +60,13 @@ class Asset : RefCounter static Result AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state) { - // TODO!!! - - return .Err; - /*Log.EngineLogger.Assert(value.type == typeof(Asset)); + Log.EngineLogger.Assert(value.type == typeof(Asset)); String identifier = scope .(); Deserialize.String!(reader, ref identifier, environment); - Asset asset = Application.Get().ContentManager.LoadAsset(identifier); + Asset asset = Content.GetAsset(Content.LoadAsset(identifier)); if (asset != null) { @@ -82,7 +79,7 @@ class Asset : RefCounter else { Deserialize.Error!("Invalid resource path", reader, value.type); - }*/ + } } //gBonEnv.typeHandlers.Add(typeof(Resource<>), diff --git a/GlitchyEngine/src/Content/AssetHandle.bf b/GlitchyEngine/src/Content/AssetHandle.bf index d855402..e8e1b2d 100644 --- a/GlitchyEngine/src/Content/AssetHandle.bf +++ b/GlitchyEngine/src/Content/AssetHandle.bf @@ -3,16 +3,25 @@ using xxHash; using System.Collections; using System.Reflection; using System.Diagnostics; +using GlitchyEngine.Core; namespace GlitchyEngine.Content; -struct AssetHandle : uint64 +struct AssetHandle : IHashable { - /// Defines an asset that is invalid. E.g. because it couldn't be loaded. - public const AssetHandle Invalid = (.)0; + private UUID _uuid; - public this(StringView name) + /// Defines an asset that is invalid. + public const AssetHandle Invalid = .(UUID(0xAAAA'AAAA'AAAA'AAAA)); + + /// Create a new random AssetHandle + public this() { - this = (uint64)xxHash.ComputeHash(name); + _uuid = UUID(); + } + + private this(UUID uuid) + { + _uuid = uuid; } [Inline] @@ -20,6 +29,8 @@ struct AssetHandle : uint64 { return Content.GetAsset(this, contentManager); } + + public int GetHashCode() => _uuid.GetHashCode(); } struct AssetHandle where T : Asset @@ -30,25 +41,27 @@ struct AssetHandle where T : Asset * We don't increment/decrement the reference counter since we guarantee that we query for the asset every frame. */ private T _asset; - private uint8 _currentFrame; + //private uint8 _currentFrame; + //private uint64 _actualCurrentFrame = 0; public const Self Invalid = .(); public this(AssetHandle handle, IContentManager contentManager = null) { _handle = handle; - _contentManager = contentManager; _asset = handle.Get(contentManager); - _contentManager = _asset.ContentManager; - _currentFrame = (uint8)Application.Get().GameTime.FrameCount; + _contentManager = _asset?.ContentManager; + if (_contentManager == null) + _contentManager = contentManager; + //_currentFrame = (uint8)Application.Get().GameTime.FrameCount; } // Creates a new invalid asset handle private this() { _handle = .Invalid; - _currentFrame = 0; + //_currentFrame = 0; _contentManager = null; _asset = null; } @@ -71,11 +84,14 @@ struct AssetHandle where T : Asset public T Get(IContentManager contentManager = null) mut { // We only care whether we are in a different frame -> we only compare the lower 8 bits. - uint8 actualFrame = (uint8)Application.Get().GameTime.FrameCount; + //uint8 actualFrame = (uint8)Application.Get().GameTime.FrameCount; + //var actualActualFrame = Application.Get().GameTime.FrameCount; - if (actualFrame != _currentFrame) + //if (actualFrame != _currentFrame) { _asset = Content.GetAsset(_handle, contentManager == null ? _contentManager : contentManager); + //_currentFrame = actualFrame; + //_actualCurrentFrame = Application.Get().GameTime.FrameCount; } return _asset; @@ -234,4 +250,4 @@ struct AssetHandle where T : Asset Compiler.EmitTypeBody(typeof(Self), code); } } -} \ No newline at end of file +} diff --git a/GlitchyEngine/src/Content/ContentManager.bf b/GlitchyEngine/src/Content/ContentManager.bf index 36c5f26..1850662 100644 --- a/GlitchyEngine/src/Content/ContentManager.bf +++ b/GlitchyEngine/src/Content/ContentManager.bf @@ -86,6 +86,26 @@ namespace GlitchyEngine.Content return (T)asset; } + + public static AssetHandle ManageAsset(Asset asset, IContentManager contentManager = null) + { + var contentManager; + + if (contentManager == null) + contentManager = Application.Get().ContentManager; + + return contentManager.ManageAsset(asset); + } + + /*public static AssetHandle ManageAsset(T asset, IContentManager contentManager = null) where T : Asset + { + var contentManager; + + if (contentManager == null) + contentManager = Application.Get().ContentManager; + + contentManager.ManageAsset(asset); + }*/ } interface IContentManager diff --git a/GlitchyEngine/src/Content/ModelLoader.bf b/GlitchyEngine/src/Content/ModelLoader.bf index 569203a..69eec26 100644 --- a/GlitchyEngine/src/Content/ModelLoader.bf +++ b/GlitchyEngine/src/Content/ModelLoader.bf @@ -229,7 +229,7 @@ namespace GlitchyEngine.Content return .Success; } - public static EcsEntity LoadModel(String filename, Material material, EcsWorld world, + /*public static EcsEntity LoadModel(String filename, Material material, EcsWorld world, List outClips, StringView entityName = StringView()) { CGLTF.Options options = .(); @@ -252,7 +252,7 @@ namespace GlitchyEngine.Content CGLTF.Free(data); return entity; - } + }*/ private static (EcsEntity Entity, TransformComponent* Transform) CreateEntity(EcsWorld world, StringView? name, EcsEntity parent) { @@ -280,7 +280,7 @@ namespace GlitchyEngine.Content return (entity, childTransform); } - private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Material material, List clips) + /*private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Material material, List clips) { (EcsEntity entity, TransformComponent* childTransform) = CreateEntity(world, node.Name == null ? null : StringView(node.Name), parentEntity); @@ -328,13 +328,13 @@ namespace GlitchyEngine.Content using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0])) { - mesh.Mesh = geo; + mesh.Mesh = Content.ManageAsset(geo); } if(skeleton == null) { var meshRenderer = world.AssignComponent(entity); - meshRenderer.Material = material; + meshRenderer.Material = material.Handle; } else { @@ -352,14 +352,18 @@ namespace GlitchyEngine.Content var meshParent = world.AssignComponent(meshEntity); meshParent.Entity = entity; - + var mesh = world.AssignComponent(meshEntity); - mesh.Mesh = PrimitiveToGeoBinding(primitive); + + using (var geo = PrimitiveToGeoBinding(primitive)) + { + mesh.Mesh = Content.ManageAsset(geo); + } if(skeleton == null) { var meshRenderer = world.AssignComponent(meshEntity); - meshRenderer.Material = material; + meshRenderer.Material = material.Handle; } else { @@ -377,7 +381,7 @@ namespace GlitchyEngine.Content { NodesToEntities(data, child, entity, world, material, clips); } - } + }*/ public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive) { diff --git a/GlitchyEngine/src/Renderer/Effect.bf b/GlitchyEngine/src/Renderer/Effect.bf index 961a401..6639a2f 100644 --- a/GlitchyEngine/src/Renderer/Effect.bf +++ b/GlitchyEngine/src/Renderer/Effect.bf @@ -212,6 +212,9 @@ public class Effect : Asset { Debug.Profiler.ProfileRendererFunction!(); + if (texture == null) + return; + [Inline]InternalSetTexture(name, texture.GetViewBinding()); } diff --git a/GlitchyEngine/src/Renderer/Material.bf b/GlitchyEngine/src/Renderer/Material.bf index 20859f6..4f62000 100644 --- a/GlitchyEngine/src/Renderer/Material.bf +++ b/GlitchyEngine/src/Renderer/Material.bf @@ -14,7 +14,7 @@ public class Material : Asset private uint8[] _rawVariables ~ delete _; - private Dictionary _textures = new .(); + private Dictionary _textures = new .(); private Dictionary _variables = new .() ~ delete _; @@ -26,12 +26,14 @@ public class Material : Asset // TODO: get variables from effect + // Get texture slots from effect for(let (name, entry) in _effect.Textures) { - var texture = entry.BoundTexture; - texture.AddRef(); + // TODO: Do we want to be able to define textures in the shader? + /*var texture = entry.BoundTexture; + texture.AddRef();*/ - _textures.Add(name, texture); + _textures.Add(name, null); } InitRawData(); @@ -41,7 +43,7 @@ public class Material : Asset { for(let (name, texture) in _textures) { - texture.Release(); + texture?.ReleaseRef(); } delete _textures; @@ -92,9 +94,9 @@ public class Material : Asset { if(_textures.TryGetValue(name, var entry)) { - entry.Release(); - _textures[name] = texture.GetViewBinding(); - //texture?.AddRef(); + entry?.ReleaseRef(); + _textures[name] = texture; + texture?.AddRef(); } else { diff --git a/GlitchyEngine/src/Renderer/MeshComponent.bf b/GlitchyEngine/src/Renderer/MeshComponent.bf index 648c5d3..b1be528 100644 --- a/GlitchyEngine/src/Renderer/MeshComponent.bf +++ b/GlitchyEngine/src/Renderer/MeshComponent.bf @@ -6,7 +6,7 @@ namespace GlitchyEngine.Renderer { public struct MeshComponent// : IDisposableComponent { - public AssetHandle Mesh {get; set mut;} + public AssetHandle Mesh {get; set mut;} = .Invalid; /* private GeometryBinding _mesh; public AssetHandle Mesh diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index 19706f2..3343170 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -469,6 +469,9 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileRendererFunction!(); + if (geometry == null || material == null) + return; + _queue.Add(SubmittedMesh(geometry, material, transform, entity.[Friend]Index)); } diff --git a/GlitchyEngine/src/Renderer/Renderer2D.bf b/GlitchyEngine/src/Renderer/Renderer2D.bf index abcec07..24f3a21 100644 --- a/GlitchyEngine/src/Renderer/Renderer2D.bf +++ b/GlitchyEngine/src/Renderer/Renderer2D.bf @@ -876,9 +876,9 @@ namespace GlitchyEngine.Renderer public static void DrawSprite(Matrix transform, SpriterRendererComponent* spriteRenderer, uint32 entityId) { if (spriteRenderer.IsCircle) - DrawCircle(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, 1.0f, spriteRenderer.UvTransform, entityId); + DrawCircle(transform, spriteRenderer.Sprite.Get() ?? s_whiteTexture, spriteRenderer.Color, 1.0f, spriteRenderer.UvTransform, entityId); else - DrawQuad(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId); + DrawQuad(transform, spriteRenderer.Sprite.Get() ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId); } // Textured quad pivot diff --git a/GlitchyEngine/src/World/MeshRendererComponent.bf b/GlitchyEngine/src/World/MeshRendererComponent.bf index 474e461..11ebada 100644 --- a/GlitchyEngine/src/World/MeshRendererComponent.bf +++ b/GlitchyEngine/src/World/MeshRendererComponent.bf @@ -7,7 +7,7 @@ namespace GlitchyEngine.World /// A component that allows to render a mesh. public struct MeshRendererComponent// : IDisposableComponent { - private AssetHandle _material; + private AssetHandle _material = .Invalid; public AssetHandle Material { diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 806bcf7..1d0666a 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -445,7 +445,7 @@ namespace GlitchyEngine.World for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate()) { - if (mesh.Mesh == null || meshRenderer.Material == null) + if (mesh.Mesh == .Invalid || meshRenderer.Material == .Invalid) continue; Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform); diff --git a/GlitchyEngine/src/World/SceneSerializer.bf b/GlitchyEngine/src/World/SceneSerializer.bf index 2cbd4c4..e23d8be 100644 --- a/GlitchyEngine/src/World/SceneSerializer.bf +++ b/GlitchyEngine/src/World/SceneSerializer.bf @@ -9,591 +9,596 @@ using System.Collections; using GlitchyEngine.Renderer; using GlitchyEngine.Content; -namespace GlitchyEngine.World +namespace GlitchyEngine.World; + +using internal GlitchyEngine.World; + +class SceneSerializer { - using internal GlitchyEngine.World; + private Scene _scene; - class SceneSerializer + // Maps from ParentID to ChildEntity + private Dictionary _parentIdToChild; + + private List<(Entity Entity, UUID ParentId)> _entitiesMissingParent; + + public this(Scene scene) { - private Scene _scene; + _scene = scene; + } - // Maps from ParentID to ChildEntity - private Dictionary _parentIdToChild; + public void Serialize(StringView filePath) + { + Debug.Profiler.ProfileResourceFunction!(); - private List<(Entity Entity, UUID ParentId)> _entitiesMissingParent; + String buffer = scope String(); + let writer = scope BonWriter(buffer, true); + var length = Serialize.Start(writer); - public this(Scene scene) + gBonEnv.serializeFlags |= .IncludeDefault | .Verbose; + + using (writer.ObjectBlock()) { - _scene = scene; - } - - public void Serialize(StringView filePath) - { - Debug.Profiler.ProfileResourceFunction!(); - - String buffer = scope String(); - let writer = scope BonWriter(buffer, true); - var length = Serialize.Start(writer); - - gBonEnv.serializeFlags |= .IncludeDefault | .Verbose; - - using (writer.ObjectBlock()) - { - // TODO: Scene name goes here! - Serialize.Value(writer, "Name", "Scene name here pls!!!"); - - writer.Identifier("Entities"); - - using (writer.ArrayBlock()) - { - for (EcsEntity e in _scene._ecsWorld.Enumerate()) - { - Entity entity = .(e, _scene); - - // TODO: also serialize object with EditorComponent (e.g. to save the location of the editor camera) - if (entity.HasComponent()) - continue; - - SerializeEntity(writer, entity); - } - } - - writer.EntryEnd(); - } - - Serialize.End(writer, length); - - String targetDirectory = Path.GetDirectoryPath(filePath, .. scope String()); - Directory.CreateDirectory(targetDirectory); - - File.WriteAllText(filePath, buffer); - } - - void SerializeEntity(BonWriter writer, Entity entity) - { - writer.EntryStart(); - - using (writer.ObjectBlock()) - { - Serialize.Value(writer, "Id", entity.UUID); - - SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); - - SerializeComponent(writer, entity, "NameComponent", scope (component) => - { - Serialize.Value(writer, "Name", component.DebugName); - }); - - SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => - { - Serialize.Value(writer, "Color", component.Color); - Serialize.Value(writer, "IsCircle", component.IsCircle); - Serialize.Value(writer, "Sprite", component.Sprite.Get().Identifier); - Serialize.Value(writer, "UvTransform", component.UvTransform); - }); - - SerializeComponent(writer, entity, "TransformComponent", scope (component) => - { - // TODO: Use GUIDs - if (component.Parent != .InvalidEntity) - { - Entity parent = Entity(component.Parent, _scene); - Serialize.Value(writer, "ParentId", parent.UUID); - } - - Serialize.Value(writer, "Position", component.Position); - Serialize.Value(writer, "Rotation", component.Rotation); - Serialize.Value(writer, "Scale", component.Scale); - - Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); - }); - - SerializeComponent(writer, entity, "CameraComponent", scope (component) => - { - SceneCamera camera = component.Camera; - - Serialize.Value(writer, "Primary", component.Primary); - - // TODO: Render target - - Serialize.Value(writer, "ProjectionType", camera.ProjectionType); - - Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); - Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); - Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); - Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); - Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); - Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); - Serialize.Value(writer, "AspectRatio", camera.AspectRatio); - Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); - }); - - // TODO: native script component - - SerializeComponent(writer, entity, "LightComponent", scope (component) => - { - SceneLight light = component.SceneLight; - - Serialize.Value(writer, "LightType", light.LightType); - - Serialize.Value(writer, "Illuminance", light.Illuminance); - - Serialize.Value(writer, "Color", light.Color); - }); - - SerializeComponent(writer, entity, "Rigidbody2D", scope (component) => - { - Serialize.Value(writer, "BodyType", component.BodyType); - - Serialize.Value(writer, "FixedRotation", component.FixedRotation); - }); - - SerializeComponent(writer, entity, "BoxCollider2D", scope (component) => - { - Serialize.Value(writer, "Offset", component.Offset); - Serialize.Value(writer, "Size", component.Size); - - Serialize.Value(writer, "Density", component.Density); - Serialize.Value(writer, "Friction", component.Friction); - Serialize.Value(writer, "Restitution", component.Restitution); - Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); - }); - - SerializeComponent(writer, entity, "CircleCollider2D", scope (component) => - { - Serialize.Value(writer, "Offset", component.Offset); - Serialize.Value(writer, "Radius", component.Radius); - - Serialize.Value(writer, "Density", component.Density); - Serialize.Value(writer, "Friction", component.Friction); - Serialize.Value(writer, "Restitution", component.Restitution); - Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); - }); - - SerializeComponent(writer, entity, "MeshComponent", scope (component) => - { - Serialize.Value(writer, "Mesh", component.Mesh.Identifier); - }); - - SerializeComponent(writer, entity, "MeshRendererComponent", scope (component) => - { - Serialize.Value(writer, "Material", component.Material.Identifier); - }); - } - - writer.EntryEnd(); - } - - static void SerializeComponent(BonWriter writer, Entity entity, String identifier, delegate void(T* component) serialize) where T : struct, new - { - if (!entity.HasComponent()) - return; - - var component = entity.GetComponent(); - - writer.Identifier(identifier); - - using (writer.ObjectBlock()) - { - serialize(component); - } - writer.EntryEnd(); - } - - public void SerializeRuntime(StringView filePath) - { - Runtime.NotImplemented(); - } - - public Result Deserialize(StringView filePath) - { - Debug.Profiler.ProfileResourceFunction!(); - - _parentIdToChild = scope Dictionary(); - _entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>(); - - String buffer = scope String(); - File.ReadAllText(filePath, buffer); - - let reader = scope BonReader(); - Try!(reader.Setup(buffer)); - Try!(Deserialize.Start(reader)); - - Try!(reader.ObjectBlock()); - // TODO: Scene name goes here! - String testName; - Deserialize.Value(reader, "Name", out testName); - delete testName; + Serialize.Value(writer, "Name", "Scene name here pls!!!"); + + writer.Identifier("Entities"); - Try!(reader.EntryEnd()); - - if (Try!(reader.Identifier()) != "Entities") - return .Err; - - Try!(reader.ArrayBlock()); - - bool first = true; - while (reader.ArrayHasMore()) + using (writer.ArrayBlock()) { - if (!first) + for (EcsEntity e in _scene._ecsWorld.Enumerate()) { - Try!(reader.EntryEnd()); + Entity entity = .(e, _scene); + + // TODO: also serialize object with EditorComponent (e.g. to save the location of the editor camera) + if (entity.HasComponent()) + continue; + + SerializeEntity(writer, entity); } + } + + writer.EntryEnd(); + } + + Serialize.End(writer, length); + + String targetDirectory = Path.GetDirectoryPath(filePath, .. scope String()); + Directory.CreateDirectory(targetDirectory); + + File.WriteAllText(filePath, buffer); + } + + void SerializeEntity(BonWriter writer, Entity entity) + { + writer.EntryStart(); + + using (writer.ObjectBlock()) + { + Serialize.Value(writer, "Id", entity.UUID); + + SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); + + SerializeComponent(writer, entity, "NameComponent", scope (component) => + { + Serialize.Value(writer, "Name", component.DebugName); + }); + + SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => + { + Serialize.Value(writer, "Color", component.Color); + Serialize.Value(writer, "IsCircle", component.IsCircle); + Serialize.Value(writer, "Sprite", component.Sprite.Get().Identifier); + Serialize.Value(writer, "UvTransform", component.UvTransform); + }); + + SerializeComponent(writer, entity, "TransformComponent", scope (component) => + { + // TODO: Use GUIDs + if (component.Parent != .InvalidEntity) + { + Entity parent = Entity(component.Parent, _scene); + Serialize.Value(writer, "ParentId", parent.UUID); + } + + Serialize.Value(writer, "Position", component.Position); + Serialize.Value(writer, "Rotation", component.Rotation); + Serialize.Value(writer, "Scale", component.Scale); + + Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); + }); + + SerializeComponent(writer, entity, "CameraComponent", scope (component) => + { + SceneCamera camera = component.Camera; + + Serialize.Value(writer, "Primary", component.Primary); + + // TODO: Render target - Try!(DeserializeEntity(reader)); + Serialize.Value(writer, "ProjectionType", camera.ProjectionType); + + Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); + Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); + Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); + Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); + Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); + Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); + Serialize.Value(writer, "AspectRatio", camera.AspectRatio); + Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); + }); - first = false; - } - - Try!(reader.ArrayBlockEnd()); - - Try!(reader.ObjectBlockEnd()); - - Try!(Deserialize.End(reader)); - - // Find parents for entities that don't have their parent yet - for ((Entity Entity, UUID ParentId) entry in _entitiesMissingParent) + // TODO: native script component + + SerializeComponent(writer, entity, "LightComponent", scope (component) => { - let parentResult = _scene.GetEntityByID(entry.ParentId); + SceneLight light = component.SceneLight; - Log.EngineLogger.Assert(parentResult case .Ok, "Parent entity does not exist."); + Serialize.Value(writer, "LightType", light.LightType); - if (parentResult case .Ok(let parent)) - { - entry.Entity.Parent = parent; - } - } + Serialize.Value(writer, "Illuminance", light.Illuminance); - return .Ok; + Serialize.Value(writer, "Color", light.Color); + }); + + SerializeComponent(writer, entity, "Rigidbody2D", scope (component) => + { + Serialize.Value(writer, "BodyType", component.BodyType); + + Serialize.Value(writer, "FixedRotation", component.FixedRotation); + }); + + SerializeComponent(writer, entity, "BoxCollider2D", scope (component) => + { + Serialize.Value(writer, "Offset", component.Offset); + Serialize.Value(writer, "Size", component.Size); + + Serialize.Value(writer, "Density", component.Density); + Serialize.Value(writer, "Friction", component.Friction); + Serialize.Value(writer, "Restitution", component.Restitution); + Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); + }); + + SerializeComponent(writer, entity, "CircleCollider2D", scope (component) => + { + Serialize.Value(writer, "Offset", component.Offset); + Serialize.Value(writer, "Radius", component.Radius); + + Serialize.Value(writer, "Density", component.Density); + Serialize.Value(writer, "Friction", component.Friction); + Serialize.Value(writer, "Restitution", component.Restitution); + Serialize.Value(writer, "RestitutionThreshold", component.RestitutionThreshold); + }); + + SerializeComponent(writer, entity, "MeshComponent", scope (component) => + { + Serialize.Value(writer, "Mesh", component.Mesh.Identifier); + }); + + SerializeComponent(writer, entity, "MeshRendererComponent", scope (component) => + { + Serialize.Value(writer, "Material", component.Material.Identifier); + }); } - private Result DeserializeEntity(BonReader reader) + writer.EntryEnd(); + } + + static void SerializeComponent(BonWriter writer, Entity entity, String identifier, delegate void(T* component) serialize) where T : struct, new + { + if (!entity.HasComponent()) + return; + + var component = entity.GetComponent(); + + writer.Identifier(identifier); + + using (writer.ObjectBlock()) { - mixin DeserializeAsset(StringView identifier) where T : Asset - { - Asset asset = null; + serialize(component); + } + writer.EntryEnd(); + } + + public void SerializeRuntime(StringView filePath) + { + Runtime.NotImplemented(); + } + + public Result Deserialize(StringView filePath) + { + Debug.Profiler.ProfileResourceFunction!(); - Try!(Deserialize.Value(reader, identifier, out asset)); + _parentIdToChild = scope Dictionary(); + _entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>(); - if (asset != null && !(asset is T)) - { - Log.EngineLogger.Error($"Asset {asset.Identifier} is not a {nameof(T)}."); - return .Err; - } + String buffer = scope String(); + File.ReadAllText(filePath, buffer); - (T)asset - } + let reader = scope BonReader(); + Try!(reader.Setup(buffer)); + Try!(Deserialize.Start(reader)); - Try!(reader.ObjectBlock()); - - Deserialize.Value(reader, "Id", let uuid); + Try!(reader.ObjectBlock()); + + // TODO: Scene name goes here! + String testName; + Deserialize.Value(reader, "Name", out testName); + delete testName; - Entity entity = _scene.CreateEntity("", UUID(uuid)); + Try!(reader.EntryEnd()); - while(reader.ObjectHasMore()) + if (Try!(reader.Identifier()) != "Entities") + return .Err; + + Try!(reader.ArrayBlock()); + + bool first = true; + while (reader.ArrayHasMore()) + { + if (!first) { Try!(reader.EntryEnd()); + } + + Try!(DeserializeEntity(reader)); - StringView identifier = Try!(reader.Identifier()); + first = false; + } - switch(identifier) - { - case "EditorComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => { return .Ok; })); - case "NameComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - String name; + Try!(reader.ArrayBlockEnd()); - Deserialize.Value(reader, "Name", out name); + Try!(reader.ObjectBlockEnd()); - component.SetName(name); + Try!(Deserialize.End(reader)); - delete name; + // Find parents for entities that don't have their parent yet + for ((Entity Entity, UUID ParentId) entry in _entitiesMissingParent) + { + let parentResult = _scene.GetEntityByID(entry.ParentId); - return .Ok; - })); - case "SpriterRendererComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - Try!(Deserialize.Value(reader, "Color", out component.Color)); - reader.EntryEnd(); - Try!(Deserialize.Value(reader, "IsCircle", out component.IsCircle)); - reader.EntryEnd(); + Log.EngineLogger.Assert(parentResult case .Ok, "Parent entity does not exist."); - using (Texture2D sprite = DeserializeAsset!("Sprite")) - { - component.Sprite = (Texture2D)sprite; - } - reader.EntryEnd(); + if (parentResult case .Ok(let parent)) + { + entry.Entity.Parent = parent; + } + } - Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); + return .Ok; + } - return .Ok; - })); - case "TransformComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - let nextId = Try!(reader.Identifier()); - if (nextId == "ParentId") - { - UUID pId; - Deserialize.Value(reader, out pId); - reader.EntryEnd(); + private Result DeserializeEntity(BonReader reader) + { + /*mixin DeserializeAsset(StringView identifier) where T : Asset + { + Asset asset = null; - var parentEntity = _scene.GetEntityByID(pId); + Try!(Deserialize.Value(reader, identifier, out asset)); - if (parentEntity case .Ok(let parent)) - { - component.Parent = parent.Handle; - } - else - { - _entitiesMissingParent.Add((entity, pId)); - } - - Deserialize.Value(reader, "Position", out component.[Friend]_position); - reader.EntryEnd(); - } - else if (nextId == "Position") - { - Deserialize.Value(reader, out component.[Friend]_position); - reader.EntryEnd(); - } - else - { - return .Err; - } - - Deserialize.Value(reader, "Rotation", out component.[Friend]_rotation); - reader.EntryEnd(); - Deserialize.Value(reader, "Scale", out component.[Friend]_scale); - reader.EntryEnd(); - - Deserialize.Value(reader, "EditorEulerRotation", out component.[Friend]_editorRotationEuler); - - component.IsDirty = true; - - return .Ok; - })); - case "CameraComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - SceneCamera camera = component.Camera; - - Deserialize.Value(reader, "Primary", out component.Primary); - reader.EntryEnd(); - - // TODO: Render target - - Deserialize.Value(reader, "ProjectionType", out camera.[Friend]_projectionType); - reader.EntryEnd(); - - Deserialize.Value(reader, "PerspectiveFovY", out camera.[Friend]_perspectiveFovY); - reader.EntryEnd(); - Deserialize.Value(reader, "PerspectiveNearPlane", out camera.[Friend]_perspectiveNearPlane); - reader.EntryEnd(); - Deserialize.Value(reader, "PerspectiveFarPlane", out camera.[Friend]_perspectiveFarPlane); - reader.EntryEnd(); - Deserialize.Value(reader, "OrthographicHeight", out camera.[Friend]_orthographicHeight); - reader.EntryEnd(); - Deserialize.Value(reader, "OrthographicNearPlane", out camera.[Friend]_orthographicNearPlane); - reader.EntryEnd(); - Deserialize.Value(reader, "OrthographicFarPlane", out camera.[Friend]_orthographicFarPlane); - reader.EntryEnd(); - Deserialize.Value(reader, "AspectRatio", out camera.[Friend]_aspectRatio); - reader.EntryEnd(); - Deserialize.Value(reader, "FixedAspectRatio", out camera.[Friend]_fixedAspectRatio); - - camera.[Friend]CalculateProjection(); - - return .Ok; - })); - // TODO: native script component - case "LightComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - ref SceneLight light = ref component.SceneLight; - - Deserialize.Value(reader, "LightType", out light.[Friend]_type); - reader.EntryEnd(); - - Deserialize.Value(reader, "Illuminance", out light.[Friend]_illuminance); - reader.EntryEnd(); - - Deserialize.Value(reader, "Color", out light.[Friend]_color); - return .Ok; - })); - case "Rigidbody2D": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - Deserialize.Value(reader, "BodyType", out component.BodyType); - reader.EntryEnd(); - - Deserialize.Value(reader, "FixedRotation", out component.FixedRotation); - - return .Ok; - })); - case "BoxCollider2D": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - Deserialize.Value(reader, "Offset", out component.Offset); - reader.EntryEnd(); - Deserialize.Value(reader, "Size", out component.Size); - reader.EntryEnd(); - - Deserialize.Value(reader, "Density", out component.Density); - reader.EntryEnd(); - Deserialize.Value(reader, "Friction", out component.Friction); - reader.EntryEnd(); - Deserialize.Value(reader, "Restitution", out component.Restitution); - reader.EntryEnd(); - Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); - - return .Ok; - })); - case "CircleCollider2D": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - Deserialize.Value(reader, "Offset", out component.Offset); - reader.EntryEnd(); - Deserialize.Value(reader, "Radius", out component.Radius); - reader.EntryEnd(); - - Deserialize.Value(reader, "Density", out component.Density); - reader.EntryEnd(); - Deserialize.Value(reader, "Friction", out component.Friction); - reader.EntryEnd(); - Deserialize.Value(reader, "Restitution", out component.Restitution); - reader.EntryEnd(); - Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); - - return .Ok; - })); - case "MeshComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - using (GeometryBinding mesh = DeserializeAsset!("Mesh")) - { - component.Mesh = mesh; - } - - return .Ok; - })); - case "MeshRendererComponent": - Try!(DeserializeComponent(reader, entity, scope (component) => - { - using (Material material = DeserializeAsset!("Material")) - { - component.Material = material; - } - - return .Ok; - })); - default: - Log.EngineLogger.AssertDebug(false, "Unknown component type"); - //return .Err; - } + if (asset != null && !(asset is T)) + { + Log.EngineLogger.Error($"Asset {asset.Identifier} is not a {nameof(T)}."); + return .Err; } - Try!(reader.ObjectBlockEnd()); + (T)asset + }*/ - return .Ok; + mixin DeserializeAssetHandle(StringView identifier) where T : Asset + { + Asset asset = null; - /*writer.EntryStart(); + Try!(Deserialize.Value(reader, identifier, out asset)); - using (writer.ObjectBlock()) + if (asset != null && !(asset is T)) { - // TODO: Entity GUID goes here! - Serialize.Value(writer, "Id", entity.Handle.Index); - - SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); + Log.EngineLogger.Error($"Asset {asset.Identifier} is not a {nameof(T)}."); + return .Err; + } - SerializeComponent(writer, entity, "NameComponent", scope (component) => + asset?.Handle ?? .Invalid + } + + Try!(reader.ObjectBlock()); + + Deserialize.Value(reader, "Id", let uuid); + + Entity entity = _scene.CreateEntity("", UUID(uuid)); + + while(reader.ObjectHasMore()) + { + Try!(reader.EntryEnd()); + + StringView identifier = Try!(reader.Identifier()); + + switch(identifier) + { + case "EditorComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { return .Ok; })); + case "NameComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { - Serialize.Value(writer, "Name", component.DebugName); - }); + String name; - SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => + Deserialize.Value(reader, "Name", out name); + + component.SetName(name); + + delete name; + + return .Ok; + })); + case "SpriterRendererComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { - // TODO: Texture + Try!(Deserialize.Value(reader, "Color", out component.Color)); + reader.EntryEnd(); + Try!(Deserialize.Value(reader, "IsCircle", out component.IsCircle)); + reader.EntryEnd(); - Serialize.Value(writer, "Color", component.Color); - }); + component.Sprite = DeserializeAssetHandle!("Sprite"); + reader.EntryEnd(); - SerializeComponent(writer, entity, "TransformComponent", scope (component) => + Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); + + return .Ok; + })); + case "TransformComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { - // TODO: Use GUIDs - if (component.Parent != .InvalidEntity) - Serialize.Value(writer, "ParentId", component.Parent.Index); + let nextId = Try!(reader.Identifier()); + if (nextId == "ParentId") + { + UUID pId; + Deserialize.Value(reader, out pId); + reader.EntryEnd(); - Serialize.Value(writer, "Position", component.Position); - Serialize.Value(writer, "Rotation", component.Rotation); - Serialize.Value(writer, "Scale", component.Scale); + var parentEntity = _scene.GetEntityByID(pId); - Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); - }); + if (parentEntity case .Ok(let parent)) + { + component.Parent = parent.Handle; + } + else + { + _entitiesMissingParent.Add((entity, pId)); + } - SerializeComponent(writer, entity, "CameraComponent", scope (component) => + Deserialize.Value(reader, "Position", out component.[Friend]_position); + reader.EntryEnd(); + } + else if (nextId == "Position") + { + Deserialize.Value(reader, out component.[Friend]_position); + reader.EntryEnd(); + } + else + { + return .Err; + } + + Deserialize.Value(reader, "Rotation", out component.[Friend]_rotation); + reader.EntryEnd(); + Deserialize.Value(reader, "Scale", out component.[Friend]_scale); + reader.EntryEnd(); + + Deserialize.Value(reader, "EditorEulerRotation", out component.[Friend]_editorRotationEuler); + + component.IsDirty = true; + + return .Ok; + })); + case "CameraComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { SceneCamera camera = component.Camera; - Serialize.Value(writer, "Primary", component.Primary); + Deserialize.Value(reader, "Primary", out component.Primary); + reader.EntryEnd(); // TODO: Render target - - Serialize.Value(writer, "ProjectionType", camera.ProjectionType); - - Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); - Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); - Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); - Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); - Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); - Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); - Serialize.Value(writer, "AspectRatio", camera.AspectRatio); - Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); - }); - // TODO: native script component - + Deserialize.Value(reader, "ProjectionType", out camera.[Friend]_projectionType); + reader.EntryEnd(); - SerializeComponent(writer, entity, "LightComponent", scope (component) => + Deserialize.Value(reader, "PerspectiveFovY", out camera.[Friend]_perspectiveFovY); + reader.EntryEnd(); + Deserialize.Value(reader, "PerspectiveNearPlane", out camera.[Friend]_perspectiveNearPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "PerspectiveFarPlane", out camera.[Friend]_perspectiveFarPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicHeight", out camera.[Friend]_orthographicHeight); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicNearPlane", out camera.[Friend]_orthographicNearPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "OrthographicFarPlane", out camera.[Friend]_orthographicFarPlane); + reader.EntryEnd(); + Deserialize.Value(reader, "AspectRatio", out camera.[Friend]_aspectRatio); + reader.EntryEnd(); + Deserialize.Value(reader, "FixedAspectRatio", out camera.[Friend]_fixedAspectRatio); + + camera.[Friend]CalculateProjection(); + + return .Ok; + })); + // TODO: native script component + case "LightComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => { - SceneLight light = component.SceneLight; + ref SceneLight light = ref component.SceneLight; - Serialize.Value(writer, "LightType", light.LightType); + Deserialize.Value(reader, "LightType", out light.[Friend]_type); + reader.EntryEnd(); - Serialize.Value(writer, "Illuminance", light.Illuminance); + Deserialize.Value(reader, "Illuminance", out light.[Friend]_illuminance); + reader.EntryEnd(); - Serialize.Value(writer, "Color", light.Color); - }); + Deserialize.Value(reader, "Color", out light.[Friend]_color); + return .Ok; + })); + case "Rigidbody2D": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + Deserialize.Value(reader, "BodyType", out component.BodyType); + reader.EntryEnd(); + + Deserialize.Value(reader, "FixedRotation", out component.FixedRotation); + + return .Ok; + })); + case "BoxCollider2D": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + Deserialize.Value(reader, "Offset", out component.Offset); + reader.EntryEnd(); + Deserialize.Value(reader, "Size", out component.Size); + reader.EntryEnd(); + + Deserialize.Value(reader, "Density", out component.Density); + reader.EntryEnd(); + Deserialize.Value(reader, "Friction", out component.Friction); + reader.EntryEnd(); + Deserialize.Value(reader, "Restitution", out component.Restitution); + reader.EntryEnd(); + Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); + + return .Ok; + })); + case "CircleCollider2D": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + Deserialize.Value(reader, "Offset", out component.Offset); + reader.EntryEnd(); + Deserialize.Value(reader, "Radius", out component.Radius); + reader.EntryEnd(); + + Deserialize.Value(reader, "Density", out component.Density); + reader.EntryEnd(); + Deserialize.Value(reader, "Friction", out component.Friction); + reader.EntryEnd(); + Deserialize.Value(reader, "Restitution", out component.Restitution); + reader.EntryEnd(); + Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); + + return .Ok; + })); + case "MeshComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + component.Mesh = DeserializeAssetHandle!("Mesh"); + + return .Ok; + })); + case "MeshRendererComponent": + Try!(DeserializeComponent(reader, entity, scope (component) => + { + component.Material = DeserializeAssetHandle!("Material"); + + return .Ok; + })); + default: + Log.EngineLogger.AssertDebug(false, "Unknown component type"); + //return .Err; } - - writer.EntryEnd();*/ } - static Result DeserializeComponent(BonReader reader, Entity entity, delegate Result(T* component) deserialize) where T : struct, new + Try!(reader.ObjectBlockEnd()); + + return .Ok; + + /*writer.EntryStart(); + + using (writer.ObjectBlock()) { - Try!(reader.ObjectBlock()); + // TODO: Entity GUID goes here! + Serialize.Value(writer, "Id", entity.Handle.Index); - T* component; + SerializeComponent(writer, entity, "EditorComponent", scope (component) => {}); - if (entity.HasComponent()) - component = entity.GetComponent(); - else - component = entity.AddComponent(); + SerializeComponent(writer, entity, "NameComponent", scope (component) => + { + Serialize.Value(writer, "Name", component.DebugName); + }); - Try!(deserialize(component)); + SerializeComponent(writer, entity, "SpriterRendererComponent", scope (component) => + { + // TODO: Texture - Try!(reader.ObjectBlockEnd()); + Serialize.Value(writer, "Color", component.Color); + }); - return .Ok; + SerializeComponent(writer, entity, "TransformComponent", scope (component) => + { + // TODO: Use GUIDs + if (component.Parent != .InvalidEntity) + Serialize.Value(writer, "ParentId", component.Parent.Index); + + Serialize.Value(writer, "Position", component.Position); + Serialize.Value(writer, "Rotation", component.Rotation); + Serialize.Value(writer, "Scale", component.Scale); + + Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler); + }); + + SerializeComponent(writer, entity, "CameraComponent", scope (component) => + { + SceneCamera camera = component.Camera; + + Serialize.Value(writer, "Primary", component.Primary); + + // TODO: Render target + + Serialize.Value(writer, "ProjectionType", camera.ProjectionType); + + Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY); + Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane); + Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane); + Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight); + Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane); + Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane); + Serialize.Value(writer, "AspectRatio", camera.AspectRatio); + Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio); + }); + + // TODO: native script component + + + SerializeComponent(writer, entity, "LightComponent", scope (component) => + { + SceneLight light = component.SceneLight; + + Serialize.Value(writer, "LightType", light.LightType); + + Serialize.Value(writer, "Illuminance", light.Illuminance); + + Serialize.Value(writer, "Color", light.Color); + }); } - public bool DeserializeRuntime(StringView filePath) - { - Runtime.NotImplemented(); - } + writer.EntryEnd();*/ } -} \ No newline at end of file + + static Result DeserializeComponent(BonReader reader, Entity entity, delegate Result(T* component) deserialize) where T : struct, new + { + Try!(reader.ObjectBlock()); + + T* component; + + if (entity.HasComponent()) + component = entity.GetComponent(); + else + component = entity.AddComponent(); + + Try!(deserialize(component)); + + Try!(reader.ObjectBlockEnd()); + + return .Ok; + } + + public bool DeserializeRuntime(StringView filePath) + { + Runtime.NotImplemented(); + } +} diff --git a/Sandbox/src/ExampleLayer2D.bf b/Sandbox/src/ExampleLayer2D.bf index 5e31229..27b4677 100644 --- a/Sandbox/src/ExampleLayer2D.bf +++ b/Sandbox/src/ExampleLayer2D.bf @@ -1,4 +1,4 @@ -using System; +/*using System; using GlitchyEngine; using GlitchyEngine.Events; using System.Diagnostics; @@ -12,6 +12,7 @@ using GlitchyEngine.Renderer.Text; using System.IO; using msdfgen; using System.Collections; +using GlitchyEngine.Content; namespace Sandbox { @@ -282,4 +283,4 @@ namespace Sandbox return false; } } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/Sandbox/src/SandboxApp.bf b/Sandbox/src/SandboxApp.bf index 5564465..5072df0 100644 --- a/Sandbox/src/SandboxApp.bf +++ b/Sandbox/src/SandboxApp.bf @@ -21,7 +21,7 @@ namespace Sandbox #if GAMMA_TEST PushLayer(new GammaTestLayer()); #elif SANDBOX_2D - PushLayer(new ExampleLayer2D()); + //PushLayer(new ExampleLayer2D()); #else PushLayer(new ExampleLayer()); #endif