From 3da60355df60c955138ca1e789fd806ccfae79f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sat, 7 Jan 2023 12:15:14 +0100 Subject: [PATCH] Model and Material loading via content manager --- GlitchyEditor/content/Models/plane.glb.ass | 4 + GlitchyEditor/content/Models/sphere.glb.ass | 4 + .../content/Textures/TestMaterial.mat | 7 + .../content/Textures/TestMaterial.mat.ass | 4 + GlitchyEditor/content/Textures/rocket.dds.ass | 2 +- GlitchyEditor/content/Textures/rocket.png.ass | 3 +- .../src/Assets/AssetPropertiesEditor.bf | 15 + .../src/Assets/IReloadingAssetLoader.bf | 8 + .../src/Assets/MaterialAssetLoader.bf | 106 +++++ GlitchyEditor/src/Assets/ModelAssetLoader.bf | 51 +++ .../src/Assets/TextureAssetLoader.bf | 22 +- .../src/EditWindows/ComponentEditWindow.bf | 40 +- .../src/EditWindows/ContentBrowserWindow.bf | 23 +- GlitchyEditor/src/EditorContentManager.bf | 81 +++- GlitchyEditor/src/EditorLayer.bf | 12 +- GlitchyEngine/src/Content/Asset.bf | 37 ++ GlitchyEngine/src/Content/ContentManager.bf | 32 +- GlitchyEngine/src/Content/ModelLoader.bf | 163 ++++++++ GlitchyEngine/src/Core/RefCounter.bf | 3 +- GlitchyEngine/src/Extension/System/IO/Path.bf | 37 +- .../src/Platform/Windows/System/IO/Path.bf | 90 +++++ GlitchyEngine/src/Renderer/GeometryBinding.bf | 4 +- GlitchyEngine/src/Renderer/Material.bf | 370 +++++++++--------- GlitchyEngine/src/Renderer/Texture.bf | 5 +- GlitchyEngine/src/World/Scene.bf | 3 + 25 files changed, 859 insertions(+), 267 deletions(-) create mode 100644 GlitchyEditor/content/Models/plane.glb.ass create mode 100644 GlitchyEditor/content/Models/sphere.glb.ass create mode 100644 GlitchyEditor/content/Textures/TestMaterial.mat create mode 100644 GlitchyEditor/content/Textures/TestMaterial.mat.ass create mode 100644 GlitchyEditor/src/Assets/AssetPropertiesEditor.bf create mode 100644 GlitchyEditor/src/Assets/IReloadingAssetLoader.bf create mode 100644 GlitchyEditor/src/Assets/MaterialAssetLoader.bf create mode 100644 GlitchyEditor/src/Assets/ModelAssetLoader.bf create mode 100644 GlitchyEngine/src/Content/Asset.bf create mode 100644 GlitchyEngine/src/Platform/Windows/System/IO/Path.bf diff --git a/GlitchyEditor/content/Models/plane.glb.ass b/GlitchyEditor/content/Models/plane.glb.ass new file mode 100644 index 0000000..d5fb47f --- /dev/null +++ b/GlitchyEditor/content/Models/plane.glb.ass @@ -0,0 +1,4 @@ +{ + AssetLoader = "ModelAssetLoader", + Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){} +} \ No newline at end of file diff --git a/GlitchyEditor/content/Models/sphere.glb.ass b/GlitchyEditor/content/Models/sphere.glb.ass new file mode 100644 index 0000000..d5fb47f --- /dev/null +++ b/GlitchyEditor/content/Models/sphere.glb.ass @@ -0,0 +1,4 @@ +{ + AssetLoader = "ModelAssetLoader", + Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){} +} \ No newline at end of file diff --git a/GlitchyEditor/content/Textures/TestMaterial.mat b/GlitchyEditor/content/Textures/TestMaterial.mat new file mode 100644 index 0000000..92d0d96 --- /dev/null +++ b/GlitchyEditor/content/Textures/TestMaterial.mat @@ -0,0 +1,7 @@ +{ + Effect = "content/Shaders/myEffect.hlsl", + Textures = + [ + "AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png" + ] +} \ No newline at end of file diff --git a/GlitchyEditor/content/Textures/TestMaterial.mat.ass b/GlitchyEditor/content/Textures/TestMaterial.mat.ass new file mode 100644 index 0000000..105823c --- /dev/null +++ b/GlitchyEditor/content/Textures/TestMaterial.mat.ass @@ -0,0 +1,4 @@ +{ + AssetLoader = "MaterialAssetLoader", + Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){} +} \ No newline at end of file diff --git a/GlitchyEditor/content/Textures/rocket.dds.ass b/GlitchyEditor/content/Textures/rocket.dds.ass index eef758c..6392b9b 100644 --- a/GlitchyEditor/content/Textures/rocket.dds.ass +++ b/GlitchyEditor/content/Textures/rocket.dds.ass @@ -1,9 +1,9 @@ { AssetLoader = "EditorTextureAssetLoader", Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ + _isSrgb = true, _samplerStateDescription = { MinFilter = .Linear, - MagFilter = .Point, MipFilter = .Linear, ComparisonFunction = .Never, AddressModeU = .Clamp, diff --git a/GlitchyEditor/content/Textures/rocket.png.ass b/GlitchyEditor/content/Textures/rocket.png.ass index 1f70168..ffe1195 100644 --- a/GlitchyEditor/content/Textures/rocket.png.ass +++ b/GlitchyEditor/content/Textures/rocket.png.ass @@ -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, diff --git a/GlitchyEditor/src/Assets/AssetPropertiesEditor.bf b/GlitchyEditor/src/Assets/AssetPropertiesEditor.bf new file mode 100644 index 0000000..96e6535 --- /dev/null +++ b/GlitchyEditor/src/Assets/AssetPropertiesEditor.bf @@ -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(); +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/IReloadingAssetLoader.bf b/GlitchyEditor/src/Assets/IReloadingAssetLoader.bf new file mode 100644 index 0000000..284ac59 --- /dev/null +++ b/GlitchyEditor/src/Assets/IReloadingAssetLoader.bf @@ -0,0 +1,8 @@ +using System.IO; + +namespace GlitchyEditor.Assets; + +interface IReloadingAssetLoader +{ + public void ReloadAsset(AssetFile assetFile, Stream data); +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/MaterialAssetLoader.bf b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf new file mode 100644 index 0000000..37fad61 --- /dev/null +++ b/GlitchyEditor/src/Assets/MaterialAssetLoader.bf @@ -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 Textures ~ DeleteDictionaryAndKeysAndValues!(_); + //public Dictionary Variables; +} + +class MaterialAssetLoader : IAssetLoader //, IReloadingAssetLoader +{ + private static readonly List _fileExtensions = new .(){".mat"} ~ delete _; + + public static List 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(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); + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/ModelAssetLoader.bf b/GlitchyEditor/src/Assets/ModelAssetLoader.bf new file mode 100644 index 0000000..2b680a4 --- /dev/null +++ b/GlitchyEditor/src/Assets/ModelAssetLoader.bf @@ -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 _fileExtensions = new .(){".gltf", ".glb"} ~ delete _; + + public static List 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); + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Assets/TextureAssetLoader.bf b/GlitchyEditor/src/Assets/TextureAssetLoader.bf index 41bd6c6..670b87a 100644 --- a/GlitchyEditor/src/Assets/TextureAssetLoader.bf +++ b/GlitchyEditor/src/Assets/TextureAssetLoader.bf @@ -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 _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; diff --git a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf index ef610c0..cb4f62d 100644 --- a/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf +++ b/GlitchyEditor/src/EditWindows/ComponentEditWindow.bf @@ -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(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 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(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("Rigidbody 2D"); ShowComponentButton("Box collider 2D"); ShowComponentButton("Circle collider 2D"); + ShowComponentButton("Mesh"); + ShowComponentButton("Mesh Renderer"); ImGui.EndCombo(); } diff --git a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf index 8a22d26..d2c20f9 100644 --- a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf +++ b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf @@ -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 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."); + } } } } \ No newline at end of file diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index e027fe1..5d32df3 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -82,7 +82,7 @@ public class AssetNode public AssetFile AssetFile ~ delete _; - public List SubAssets ~ { + public List 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 _identifiers = .() ~ _.ClearAndDeleteItems(); + //private append List _identifiers = .() ~ _.ClearAndDeleteItems(); - private append Dictionary _loadedAssets = .(); // Check if all resources are unloaded + private append Dictionary _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); + } } \ No newline at end of file diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 5cdcd0d..4cff6c0 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -88,11 +88,19 @@ namespace GlitchyEditor private void InitContentManager() { _contentManager = new EditorContentManager(); - _contentManager.SetContentDirectory("./content"); - _contentManager.RegisterAssetLoader(); _contentManager.SetAsDefaultAssetLoader(".png", ".dds"); _contentManager.SetAssetPropertiesEditor(=> TextureAssetPropertiesEditor.Factory); + + _contentManager.RegisterAssetLoader(); + _contentManager.SetAsDefaultAssetLoader(".glb", ".gltf"); + _contentManager.SetAssetPropertiesEditor(=> ModelAssetPropertiesEditor.Factory); + + _contentManager.RegisterAssetLoader(); + _contentManager.SetAsDefaultAssetLoader(".mat"); + _contentManager.SetAssetPropertiesEditor(=> MaterialAssetPropertiesEditor.Factory); + + _contentManager.SetContentDirectory("./content"); // Todo: Sketchy... Application.Get().[Friend]_contentManager = _contentManager; diff --git a/GlitchyEngine/src/Content/Asset.bf b/GlitchyEngine/src/Content/Asset.bf new file mode 100644 index 0000000..48b5b49 --- /dev/null +++ b/GlitchyEngine/src/Content/Asset.bf @@ -0,0 +1,37 @@ +using GlitchyEngine.Core; +using System; + +namespace GlitchyEngine.Content; + +class Asset : RefCounter +{ + private append String _identifier; + + internal IContentManager _contentManager; + + /// Gets the identifier of this asset. + /// @remarks The identifier is the name with which the asset was registered in the content manager. + /// This identifier can be used to request the Asset from the content manager. + public StringView Identifier + { + get => _identifier; + set + { + _contentManager?.UpdateAssetIdentifier(this, _identifier, value); + + _identifier.Set(value); + // TODO: do we need to tell the content manager, that the name changed? + } + } + + // TODO: do we need unmanaged assets? Probably not... + /// Gets the content manager that manages this asset; or null if this asset isn't managed. + public IContentManager ContentManager => _contentManager; + + protected ~this() + { + // TODO: crash when _contentManager is deleted first... + // TODO: unregister from content manager + _contentManager?.UnmanageAsset(this); + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/Content/ContentManager.bf b/GlitchyEngine/src/Content/ContentManager.bf index 08123f9..9905237 100644 --- a/GlitchyEngine/src/Content/ContentManager.bf +++ b/GlitchyEngine/src/Content/ContentManager.bf @@ -54,8 +54,9 @@ namespace GlitchyEngine.Content /// Loads the asset from the given data stream with the specified config. /// @param file The stream containing the asset. /// @param config The configuration which specifies the settings used to load the asset. + /// @param contentManager The content manager used to load the asset. /// @returns The loaded asset. - IRefCounted LoadAsset(Stream file, AssetLoaderConfig config); + Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView? subAsset, IContentManager contentManager); } static @@ -79,7 +80,17 @@ namespace GlitchyEngine.Content interface IContentManager { /// Loads the given asset. - IRefCounted LoadAsset(StringView assetIdentifier); + Asset LoadAsset(StringView assetIdentifier); + + /// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier) + void ManageAsset(Asset asset); + + /// The content manager will no longer manage the asset. + void UnmanageAsset(Asset asset); + + // TODO: Maybe calling UnmanageAsset -> ManageAsset is enough.... + /// Provides a method for the asset to tell its content manager that the identifer changed. + void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier); /// Returns a data stream for the given asset. Stream GetStream(StringView assetIdentifier); @@ -93,7 +104,22 @@ namespace GlitchyEngine.Content class RuntimeContentManager : IContentManager { - public IRefCounted LoadAsset(StringView identifier) + public Asset LoadAsset(StringView assetIdentifier) + { + Runtime.NotImplemented(); + } + + public void ManageAsset(Asset asset) + { + Runtime.NotImplemented(); + } + + public void UnmanageAsset(Asset asset) + { + Runtime.NotImplemented(); + } + + public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier) { Runtime.NotImplemented(); } diff --git a/GlitchyEngine/src/Content/ModelLoader.bf b/GlitchyEngine/src/Content/ModelLoader.bf index 3e9933b..569203a 100644 --- a/GlitchyEngine/src/Content/ModelLoader.bf +++ b/GlitchyEngine/src/Content/ModelLoader.bf @@ -5,6 +5,7 @@ using GlitchyEngine.Math; using GlitchyEngine.Renderer; using GlitchyEngine.Renderer.Animation; using GlitchyEngine.World; +using System.IO; namespace GlitchyEngine.Content { @@ -66,6 +67,168 @@ namespace GlitchyEngine.Content return geoBinding; } + public static GeometryBinding LoadMesh(Stream data, StringView meshName, int primitiveIndex) + { + // TODO: add a context to remember which buffers were loaded before so that we don't load the same data multiple times. + + uint8[] rawData = new:ScopedAlloc! uint8[data.Length]; + + var dataReadResult = data.TryRead(rawData); + + if (dataReadResult case .Err(let err)) + { + Log.EngineLogger.Error($"Failed to read data from stream. Error: {err}"); + } + + CGLTF.Options options = .(); + CGLTF.Data* modelData; + CGLTF.Result result = CGLTF.Parse(options, (Span)rawData, out modelData); + + if (!(result case .Success)) + return null; + + // TODO: one buffer can be used by multiple primitives, the content manager could manage the buffers + + // TODO: load with content manager + + result = CGLTF.LoadBuffers(options, modelData, (char8*)null); + //result = LoadBuffersWithContentManager(options, modelData, meshName, Application.Get().ContentManager); + + GeometryBinding geoBinding = null; + + for (var mesh in modelData.Meshes) + { + var name = StringView(mesh.Name); + + //if (name == meshName) + { + Log.EngineLogger.AssertDebug(primitiveIndex >= 0 && primitiveIndex < mesh.Primitives.Length); + + geoBinding = PrimitiveToGeoBinding(mesh.Primitives[primitiveIndex]); + break; + } + } + + CGLTF.Free(modelData); + + return geoBinding; + } + + private static CGLTF.Result LoadBuffersWithContentManager(CGLTF.Options options, CGLTF.Data* data, StringView fileName, IContentManager contentManager) + { + if (data.Buffers.Length > 0 && data.Buffers[0].Data == null && data.Buffers[0].Uri == null && !data.Bin.IsEmpty) + { + if ((uint)data.Bin.Length < data.Buffers[0].Size) + return .DataTooShort; + + data.Buffers[0].Data = data.Bin.Ptr; + data.Buffers[0].DataFreeMethod = .None; + } + + for (ref CGLTF.Buffer buffer in ref data.Buffers) + { + if (buffer.Data != null) + continue; + + if (buffer.Uri == null) + continue; + + StringView uri = StringView(buffer.Uri); + + if (uri.StartsWith("data:")) + { + int commaIndex = uri.IndexOf(','); + + //char* comma = strchr(uri, ','); + + if (commaIndex == -1 || commaIndex >= 7 || uri.StartsWith(";base64")) + return .UnknownFormat; + + StringView dataView = uri.Substring(commaIndex + 1); + +#unwarn + CGLTF.Result loadBufferResult = CGLTF.LoadBuffersBase64(&options, buffer.Size, dataView.Ptr, &buffer.Data); + buffer.DataFreeMethod = .MemoryFree; + + return loadBufferResult; + } + else + { + Runtime.NotImplemented(); + + // TODO: Request Buffer from Content Manager + + //int index = uri.IndexOf("://"); + + //if (index == -1) + // return .UnknownFormat; + + // TODO: load buffer file... + //CGLTF.Result res = //cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + //buffer.DataFreeMethod = cgltf_data_free_method_file_release; + + /*if (res != cgltf_result_success) + { + return res; + }*/ + } + } + + /* + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + if (data->buffers[i].data) + { + continue; + } + + const char* uri = data->buffers[i].uri; + + if (uri == NULL) + { + continue; + } + + if (strncmp(uri, "data:", 5) == 0) + { + const char* comma = strchr(uri, ','); + + if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) + { + cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + else if (strstr(uri, "://") == NULL && gltf_path) + { + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_file_release; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + */ + + return .Success; + } + public static EcsEntity LoadModel(String filename, Material material, EcsWorld world, List outClips, StringView entityName = StringView()) { diff --git a/GlitchyEngine/src/Core/RefCounter.bf b/GlitchyEngine/src/Core/RefCounter.bf index 392644a..e2f2d8e 100644 --- a/GlitchyEngine/src/Core/RefCounter.bf +++ b/GlitchyEngine/src/Core/RefCounter.bf @@ -7,11 +7,10 @@ namespace GlitchyEngine.Core * Implements the IDisposable interface so that it can be used with a using-Block so that the counter * will be decremented automatically after leaving the block. */ - public class RefCounter : System.RefCounted, IDisposable + public class RefCounter : RefCounted, IDisposable { protected ~this() { - } public void Dispose() diff --git a/GlitchyEngine/src/Extension/System/IO/Path.bf b/GlitchyEngine/src/Extension/System/IO/Path.bf index d609f96..0096e5d 100644 --- a/GlitchyEngine/src/Extension/System/IO/Path.bf +++ b/GlitchyEngine/src/Extension/System/IO/Path.bf @@ -1,41 +1,28 @@ using System.Diagnostics; + namespace System.IO; extension Path { - /// Opens the file browser and selects the specified file. - /// @param path The path of the file to select. - public static void OpenFolderAndSelectItem(String path) + public static mixin GetScopedFullPath(String path) { - String fullPath = scope String(256); + String fullPath = scope String(Path.MaxPath); Path.GetFullPath(path, fullPath); -#if BF_PLATFORM_WINDOWS - ProcessStartInfo processInfo = scope .(); - processInfo.SetFileNameAndArguments(scope $"explorer /select,\"{fullPath}\""); - - scope SpawnedProcess().Start(processInfo); -#else - Runtime.NotImplemented(); -#endif + fullPath } + /// Opens the file browser and selects the specified file. + /// @param path The path of the file to select. + public static extern Result OpenFolderAndSelectItem(String path); + /// Opens the file browser in the given directory. /// @param directory The directory to show in the file browser. - public static void OpenFolder(String directory) - { - String fullPath = scope String(256); - Path.GetFullPath(directory, fullPath); + public static extern Result OpenFolder(String directory); -#if BF_PLATFORM_WINDOWS - ProcessStartInfo processInfo = scope .(); - processInfo.SetFileNameAndArguments(scope $"explorer \"{fullPath}\""); - - scope SpawnedProcess().Start(processInfo); -#else - Runtime.NotImplemented(); -#endif - } + /// Shows a dialog in which the user can select which program to open the given file with. + /// @param The Path of the file to open. + public static extern Result OpenWithDialog(String filePath); public static void Fixup(String path) { diff --git a/GlitchyEngine/src/Platform/Windows/System/IO/Path.bf b/GlitchyEngine/src/Platform/Windows/System/IO/Path.bf new file mode 100644 index 0000000..22bbb98 --- /dev/null +++ b/GlitchyEngine/src/Platform/Windows/System/IO/Path.bf @@ -0,0 +1,90 @@ +#if BF_PLATFORM_WINDOWS + +using DirectX.Common; +using DirectX.Windows; +using System; +using System.Diagnostics; +using DirectX.Windows.Winuser; + +namespace DirectX.Windows.Winuser +{ + enum OpenAsInfoFlags : uint32 + { + /// Enable the "always use this program" checkbox. If not passed, it will be disabled. + AllowRegistration = 0x1, + /// Do the registration after the user hits the OK button. + RegisterExtension = 0x2, + /// Execute file after registering. + Exec = 0x4, + ///Force the Always use this program checkbox to be checked. + /// Typically, you won't use the OAIF_ALLOW_REGISTRATION flag when you pass this value. + ForceRegistration = 0x8, + /// Introduced in Windows Vista. Hide the Always use this program checkbox. If this flag is specified, the OAIF_ALLOW_REGISTRATION and OAIF_FORCE_REGISTRATION flags will be ignored. + HideRegistration = 0x20, + /// Introduced in Windows Vista. The value for the extension that is passed is actually a protocol, so the Open With dialog box should show applications that are registered as capable of handling that protocol. + UrlProtocol = 0x40, + /// Introduced in Windows 8. The location pointed to by the pcszFile parameter is given as a URI. + FileIsUri = 0x80 + } + + struct OpenAsInfo + { + public LPCWSTR File; + public LPCWSTR Class; + public OpenAsInfoFlags Flags; + } + + static + { + [Import("user32.lib"), CallingConvention(.Stdcall), CLink] + public extern static HResult SHOpenWithDialog(HWND hwndParent, OpenAsInfo* poainfo); + } +} + +namespace System.IO; + +extension Path +{ + /// Opens the file browser and selects the specified file. + /// @param path The path of the file to select. + public static override Result OpenFolderAndSelectItem(String path) + { + String fullPath = GetScopedFullPath!(path); + + ProcessStartInfo processInfo = scope .(); + processInfo.SetFileNameAndArguments(scope $"explorer /select,\"{fullPath}\""); + + return scope SpawnedProcess().Start(processInfo); + } + + /// Opens the file browser in the given directory. + /// @param directory The directory to show in the file browser. + public static override Result OpenFolder(String directory) + { + String fullPath = GetScopedFullPath!(directory); + + ProcessStartInfo processInfo = scope .(); + processInfo.SetFileNameAndArguments(scope $"explorer \"{fullPath}\""); + + return scope SpawnedProcess().Start(processInfo); + } + + public static override Result OpenWithDialog(String filePath) + { + String fullPath = GetScopedFullPath!(filePath); + + OpenAsInfo info = .(); + info.File = fullPath.ToScopedNativeWChar!(); + info.Class = null; + info.Flags = .Exec; + + HResult result = SHOpenWithDialog(0, &info); + + if (result.Succeeded) + return .Ok; + else + return .Err; + } +} + +#endif diff --git a/GlitchyEngine/src/Renderer/GeometryBinding.bf b/GlitchyEngine/src/Renderer/GeometryBinding.bf index 3aafd20..485a8d0 100644 --- a/GlitchyEngine/src/Renderer/GeometryBinding.bf +++ b/GlitchyEngine/src/Renderer/GeometryBinding.bf @@ -1,10 +1,12 @@ using System; using System.Collections; +using GlitchyEngine.Content; using GlitchyEngine.Core; namespace GlitchyEngine.Renderer { - public class GeometryBinding : RefCounter + // Todo: Rename to Mesh? + public class GeometryBinding : Asset { internal List _vertexBuffers = new .() ~ delete _; internal IndexBuffer _indexBuffer ~ _?.ReleaseRef(); diff --git a/GlitchyEngine/src/Renderer/Material.bf b/GlitchyEngine/src/Renderer/Material.bf index 270a9ae..20859f6 100644 --- a/GlitchyEngine/src/Renderer/Material.bf +++ b/GlitchyEngine/src/Renderer/Material.bf @@ -1,237 +1,239 @@ -using System; -using System.Collections; +using GlitchyEngine.Content; using GlitchyEngine.Core; using GlitchyEngine.Math; +using System; +using System.Collections; using internal GlitchyEngine.Renderer; -namespace GlitchyEngine.Renderer +namespace GlitchyEngine.Renderer; + +public class Material : Asset { - public class Material : RefCounter + private Effect _effect ~ _?.ReleaseRef(); + + private uint8[] _rawVariables ~ delete _; + + private Dictionary _textures = new .(); + + private Dictionary _variables = new .() ~ delete _; + + public Effect Effect => _effect; + + public this(Effect effect) { - private Effect _effect ~ _?.ReleaseRef(); + _effect = effect..AddRef(); - private uint8[] _rawVariables ~ delete _; + // TODO: get variables from effect - private Dictionary _textures = new .(); - - private Dictionary _variables = new .() ~ delete _; - - public Effect Effect => _effect; - - public this(Effect effect) + for(let (name, entry) in _effect.Textures) { - _effect = effect..AddRef(); + var texture = entry.BoundTexture; + texture.AddRef(); - // TODO: get variables from effect - - for(let (name, entry) in _effect.Textures) - { - var texture = entry.BoundTexture; - texture.AddRef(); - - _textures.Add(name, texture); - } - - InitRawData(); + _textures.Add(name, texture); } - public ~this() - { - for(let (name, texture) in _textures) - { - texture.Release(); - } + InitRawData(); + } - delete _textures; + public ~this() + { + for(let (name, texture) in _textures) + { + texture.Release(); } - /** @brief Initializes the raw data array for the variables. - */ - private void InitRawData() + delete _textures; + } + + /** @brief Initializes the raw data array for the variables. + */ + private void InitRawData() + { + uint32 bufferSize = 0; + + for(let variable in _effect.Variables) { - uint32 bufferSize = 0; + _variables.Add(variable.Name, (bufferSize, variable)); - for(let variable in _effect.Variables) - { - _variables.Add(variable.Name, (bufferSize, variable)); - - bufferSize += variable._sizeInBytes; - } - - _rawVariables = new uint8[bufferSize]; + bufferSize += variable._sizeInBytes; } - /** - * Binds the materials Shaders and Parameters to the given context. - */ - public void Bind() + _rawVariables = new uint8[bufferSize]; + } + + /** + * Binds the materials Shaders and Parameters to the given context. + */ + public void Bind() + { + Debug.Profiler.ProfileRendererFunction!(); + + for(let (name, texture) in _textures) { - Debug.Profiler.ProfileRendererFunction!(); - - for(let (name, texture) in _textures) - { - _effect.SetTexture(name, texture); - } - - for(let (name, variable) in _variables) - { - variable.Variable.SetRawData(RawPointer!(variable.Offset)); - } - - _effect.ApplyChanges(); - _effect.Bind(); + _effect.SetTexture(name, texture); } - /** @brief Sets a texture of the material. - * @param name The name of the texture to set. - * @param texture The texture to bind to the effect. - */ - public void SetTexture(String name, Texture texture) + for(let (name, variable) in _variables) { - if(_textures.TryGetValue(name, var entry)) - { - entry.Release(); - _textures[name] = texture.GetViewBinding(); - //texture?.AddRef(); - } - else - { - Log.EngineLogger.Assert(false); - } + variable.Variable.SetRawData(RawPointer!(variable.Offset)); } - private mixin RawPointer(uint32 offset) + _effect.ApplyChanges(); + _effect.Bind(); + } + + /** @brief Sets a texture of the material. + * @param name The name of the texture to set. + * @param texture The texture to bind to the effect. + */ + public void SetTexture(String name, Texture texture) + { + if(_textures.TryGetValue(name, var entry)) { - (T*)(&_rawVariables[offset]) + entry.Release(); + _textures[name] = texture.GetViewBinding(); + //texture?.AddRef(); } - - [Inline] - private void SetVariable(String name, T value) where T : struct + else { - Debug.Profiler.ProfileRendererFunction!(); - - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - *RawPointer!(entry.Offset) = value; - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } + Log.EngineLogger.Assert(false); } + } - public void SetVariable(String name, float value) => SetVariable(name, value); - public void SetVariable(String name, Vector2 value) => SetVariable(name, value); - public void SetVariable(String name, Vector3 value) => SetVariable(name, value); - public void SetVariable(String name, Vector4 value) => SetVariable(name, value); - - public void SetVariable(String name, int32 value) => SetVariable(name, value); - public void SetVariable(String name, Int2 value) => SetVariable(name, value); - public void SetVariable(String name, Int3 value) => SetVariable(name, value); - public void SetVariable(String name, Int4 value) => SetVariable(name, value); + private mixin RawPointer(uint32 offset) + { + (T*)(&_rawVariables[offset]) + } - public void SetVariable(String name, uint32 value) => SetVariable(name, value); + [Inline] + private void SetVariable(String name, T value) where T : struct + { + Debug.Profiler.ProfileRendererFunction!(); - public void SetVariable(String name, Color value) => SetVariable(name, (ColorRGBA)value); - public void SetVariable(String name, ColorRGB value) => SetVariable(name, value); - public void SetVariable(String name, ColorRGBA value) => SetVariable(name, value); - - public void SetVariable(String name, Matrix3x3 value) + if(_variables.TryGetValue(name, let entry)) { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - // TODO: I'm not sure how to handle Matrix3x3 - // It seems to be 44 Bytes (11 Floats) large. - Log.EngineLogger.AssertDebug(entry.Variable._sizeInBytes == 44, "Made wrong assumption about the size of float3x3 in a hlsl constant-buffer."); + entry.Variable.EnsureTypeMatch(); + + *RawPointer!(entry.Offset) = value; + } + else + { + Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); + } + } + + public void SetVariable(String name, float value) => SetVariable(name, value); + public void SetVariable(String name, Vector2 value) => SetVariable(name, value); + public void SetVariable(String name, Vector3 value) => SetVariable(name, value); + public void SetVariable(String name, Vector4 value) => SetVariable(name, value); + + public void SetVariable(String name, int32 value) => SetVariable(name, value); + public void SetVariable(String name, Int2 value) => SetVariable(name, value); + public void SetVariable(String name, Int3 value) => SetVariable(name, value); + public void SetVariable(String name, Int4 value) => SetVariable(name, value); + + public void SetVariable(String name, uint32 value) => SetVariable(name, value); + + public void SetVariable(String name, Color value) => SetVariable(name, (ColorRGBA)value); + public void SetVariable(String name, ColorRGB value) => SetVariable(name, value); + public void SetVariable(String name, ColorRGBA value) => SetVariable(name, value); + + public void SetVariable(String name, Matrix3x3 value) + { + if(_variables.TryGetValue(name, let entry)) + { + entry.Variable.EnsureTypeMatch(); + + // TODO: I'm not sure how to handle Matrix3x3 + // It seems to be 44 Bytes (11 Floats) large. + Log.EngineLogger.AssertDebug(entry.Variable._sizeInBytes == 44, "Made wrong assumption about the size of float3x3 in a hlsl constant-buffer."); #unwarn - *RawPointer!(entry.Offset) = *(float[11]*)&Matrix4x3(value); - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } + *RawPointer!(entry.Offset) = *(float[11]*)&Matrix4x3(value); } - - public void SetVariable(String name, Matrix3x3[] values) + else { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - int count = Math.Min(values.Count, entry.Variable._elements); - - for(int i < count) - { - (RawPointer!(entry.Offset))[i] = Matrix4x3(values[i]); - } - } - else - { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } + Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); } - - public void SetVariable(String name, Matrix4x3 value) => SetVariable(name, value); - public void SetVariable(String name, Matrix value) => SetVariable(name, value); - - public void SetVariable(String name, Matrix[] values) + } + + public void SetVariable(String name, Matrix3x3[] values) + { + if(_variables.TryGetValue(name, let entry)) { - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); + entry.Variable.EnsureTypeMatch(); - Internal.MemCpy(RawPointer!(entry.Offset), values.Ptr, sizeof(Matrix) * Math.Min(values.Count, entry.Variable._elements)); - } - else + int count = Math.Min(values.Count, entry.Variable._elements); + + for(int i < count) { - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); + (RawPointer!(entry.Offset))[i] = Matrix4x3(values[i]); } } - - /** - * Sets the raw data of the variable. - * @param rawData The pointer to the raw data. If rawData is null the raw data will be set to zero. - */ - internal void SetRawData(uint32 offset, void* rawData, uint32 byteCount) + else { - if(rawData != null) - Internal.MemCpy(&_rawVariables + offset, rawData, byteCount); - else - Internal.MemSet(&_rawVariables + offset, 0, byteCount); + Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); } + } - // public void Set(String name, VALUE)... - - public void GetVariable(String name, out T value) where T : struct + public void SetVariable(String name, Matrix4x3 value) => SetVariable(name, value); + public void SetVariable(String name, Matrix value) => SetVariable(name, value); + + public void SetVariable(String name, Matrix[] values) + { + if(_variables.TryGetValue(name, let entry)) { - Debug.Profiler.ProfileRendererFunction!(); + entry.Variable.EnsureTypeMatch(); - if(_variables.TryGetValue(name, let entry)) - { - entry.Variable.EnsureTypeMatch(); - - value = *RawPointer!(entry.Offset); - } - else - { - value = ?; - Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); - } + Internal.MemCpy(RawPointer!(entry.Offset), values.Ptr, sizeof(Matrix) * Math.Min(values.Count, entry.Variable._elements)); } + else + { + Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); + } + } - // Float, Float2, Float3, Float4 - // Color, ColorRGB, ColorRGBA - // Matrix3x3, Matrix4x3, Matrix - // Int, Int2, Int3, Int4 - // UInt, UInt2, UInt3, UInt4 - // Bool, Bool2, Bool3, Bool4 - // Half, Half2, Half3, Half4 - // Byte, Byte2, Byte3, Byte4 + // Supporeted types + // Float, Float2, Float3, Float4 + // Color, ColorRGB, ColorRGBA + // Int, Int2, Int3, Int4 + // UInt + // Matrix3x3, Matrix4x3, Matrix + + // TODO: Add missing variable types + // UInt2, UInt3, UInt4 + // Bool, Bool2, Bool3, Bool4 + // Half, Half2, Half3, Half4 + // Byte, Byte2, Byte3, Byte4 + + /** + * Sets the raw data of the variable. + * @param rawData The pointer to the raw data. If rawData is null the raw data will be set to zero. + */ + internal void SetRawData(uint32 offset, void* rawData, uint32 byteCount) + { + if(rawData != null) + Internal.MemCpy(&_rawVariables + offset, rawData, byteCount); + else + Internal.MemSet(&_rawVariables + offset, 0, byteCount); + } + + public void GetVariable(String name, out T value) where T : struct + { + Debug.Profiler.ProfileRendererFunction!(); + + if(_variables.TryGetValue(name, let entry)) + { + entry.Variable.EnsureTypeMatch(); + + value = *RawPointer!(entry.Offset); + } + else + { + value = ?; + Log.EngineLogger.Assert(false, scope $"The effect doesn't contain a variable named \"{name}\""); + } } } diff --git a/GlitchyEngine/src/Renderer/Texture.bf b/GlitchyEngine/src/Renderer/Texture.bf index 7658358..e25b365 100644 --- a/GlitchyEngine/src/Renderer/Texture.bf +++ b/GlitchyEngine/src/Renderer/Texture.bf @@ -1,12 +1,13 @@ -using System; +using GlitchyEngine.Content; using GlitchyEngine.Core; using GlitchyEngine.Math; +using System; using System.IO; using System.Diagnostics; namespace GlitchyEngine.Renderer { - public abstract class Texture : RefCounter + public abstract class Texture : Asset { protected SamplerState _samplerState ~ _?.ReleaseRef(); diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 3f50dae..33e089c 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -442,6 +442,9 @@ namespace GlitchyEngine.World for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate()) { + if (mesh.Mesh == null || meshRenderer.Material == null) + continue; + Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform); }