From 7cc41f2f60deeed67470d1404c2e40cf7d260bdd Mon Sep 17 00:00:00 2001 From: Jaremie Romer Date: Sat, 27 Dec 2025 08:15:21 -0600 Subject: [PATCH 1/3] Added support for loading runtime texture data into TextureAssetManager --- include/ncengine/asset/Assets.h | 16 ++- source/ncengine/asset/Assets.cpp | 17 +++ source/ncengine/asset/RuntimeTextureLoader.h | 33 +++++ .../asset/manager/TextureAssetManager.cpp | 37 ++++++ .../asset/manager/TextureAssetManager.h | 18 ++- source/ncengine/ui/editor/impl/EditorUI.cpp | 23 ++++ source/ncengine/ui/editor/impl/EditorUI.h | 2 + .../asset/TextureAssetManager_tests.cpp | 115 ++++++++++++++++++ 8 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 source/ncengine/asset/RuntimeTextureLoader.h diff --git a/include/ncengine/asset/Assets.h b/include/ncengine/asset/Assets.h index 325263a8a..fb085fb60 100644 --- a/include/ncengine/asset/Assets.h +++ b/include/ncengine/asset/Assets.h @@ -1,10 +1,18 @@ +/** + * @file Assets.h + * @copyright Jaremie Romer and McCallister Romer 2025 + */ + #pragma once #include "ncasset/AssetType.h" #include "ncengine/asset/AssetViews.h" +#include + namespace nc::asset { +struct Texture; /** Assets must be loaded before dependent objects are created and should be unloaded only * when they are no longer in use. * @@ -55,11 +63,17 @@ void UnloadAllMeshAssets(); auto AcquireMeshAsset(const std::string& path) -> MeshView; auto AcquireMeshAsset(AssetId id) -> MeshView; -/** Supported file types: .nca +/** Supported file types: .nca * @note Unloading textures invalidates all TextureViews. It is intended * to be done on scene change. */ bool LoadTextureAsset(const std::string& path); bool LoadTextureAssets(std::span paths); +bool LoadTextureFromMemory(const std::string& key, Texture texture); +bool LoadTextureFromRGBA(const std::string& key, + std::span rgbaData, + uint32_t width, + uint32_t height, + TextureFormat format = TextureFormat::RGBA8_UNORM); bool UnloadTextureAsset(const std::string& path); void UnloadAllTextureAssets(); auto AcquireTextureAsset(const std::string& path) -> TextureView; diff --git a/source/ncengine/asset/Assets.cpp b/source/ncengine/asset/Assets.cpp index 152d5a010..aa9f0517a 100644 --- a/source/ncengine/asset/Assets.cpp +++ b/source/ncengine/asset/Assets.cpp @@ -1,5 +1,8 @@ #include "asset/Assets.h" #include "AssetService.h" +#include "RuntimeTextureLoader.h" + +#include "ncasset/Assets.h" namespace nc::asset { @@ -193,6 +196,20 @@ bool LoadTextureAssets(std::span paths) return AssetService::Get()->Load(paths); } +bool LoadTextureFromMemory(const std::string& key, Texture texture) +{ + return RuntimeTextureLoaderService::Get()->LoadFromMemory(key, std::move(texture)); +} + +bool LoadTextureFromRGBA(const std::string& key, + std::span rgbaData, + uint32_t width, + uint32_t height, + TextureFormat format) +{ + return RuntimeTextureLoaderService::Get()->LoadFromRGBA(key, rgbaData, width, height, format); +} + bool UnloadTextureAsset(const std::string& path) { return AssetService::Get()->Unload(path); diff --git a/source/ncengine/asset/RuntimeTextureLoader.h b/source/ncengine/asset/RuntimeTextureLoader.h new file mode 100644 index 000000000..928ce8780 --- /dev/null +++ b/source/ncengine/asset/RuntimeTextureLoader.h @@ -0,0 +1,33 @@ +#pragma once + +#include "service/ServiceLocator.h" + +#include "ncasset/AssetType.h" + +#include +#include + +namespace nc::asset +{ +struct Texture; + +class IRuntimeTextureLoader +{ + public: + IRuntimeTextureLoader() + { + ServiceLocator::Register(this); + } + + virtual ~IRuntimeTextureLoader() = default; + + virtual auto LoadFromMemory(const std::string& key, Texture texture) -> bool = 0; + virtual auto LoadFromRGBA(const std::string& key, + std::span rgbaData, + uint32_t width, + uint32_t height, + TextureFormat format = TextureFormat::RGBA8_UNORM) -> bool = 0; +}; + +using RuntimeTextureLoaderService = ServiceLocator; +} // namespace nc::asset diff --git a/source/ncengine/asset/manager/TextureAssetManager.cpp b/source/ncengine/asset/manager/TextureAssetManager.cpp index dd058c505..491ff0a21 100644 --- a/source/ncengine/asset/manager/TextureAssetManager.cpp +++ b/source/ncengine/asset/manager/TextureAssetManager.cpp @@ -72,6 +72,43 @@ auto TextureAssetManager::Load(std::span paths) -> bool return true; } +auto TextureAssetManager::LoadFromMemory(const std::string& key, Texture texture) -> bool +{ + if (m_table.size() + 1 >= m_maxTextureCount) + { + throw NcError("Cannot exceed max texture count."); + } + + if (IsLoaded(key)) + { + return false; + } + + auto textureWithId = TextureWithId{std::move(texture), m_table.hash(key)}; + m_table.emplace(key); + m_onUpdate.Emit(TextureUpdateEventData{ + UpdateAction::Load, + std::span{&textureWithId, 1} + }); + + return true; +} + +auto TextureAssetManager::LoadFromRGBA(const std::string& key, + std::span rgbaData, + uint32_t width, + uint32_t height, + TextureFormat format) -> bool +{ + auto texture = Texture{ + .format = format, + .width = width, + .height = height, + .pixelData = std::vector(rgbaData.begin(), rgbaData.end()) + }; + return LoadFromMemory(key, std::move(texture)); +} + auto TextureAssetManager::Unload(const std::string& path) -> bool { if (!m_table.erase(path)) diff --git a/source/ncengine/asset/manager/TextureAssetManager.h b/source/ncengine/asset/manager/TextureAssetManager.h index 63dac1825..142c08bd2 100644 --- a/source/ncengine/asset/manager/TextureAssetManager.h +++ b/source/ncengine/asset/manager/TextureAssetManager.h @@ -1,24 +1,36 @@ #pragma once #include "asset/AssetService.h" +#include "asset/RuntimeTextureLoader.h" #include "utility/StringMap.h" #include "ncengine/utility/Signal.h" +#include "ncasset/AssetType.h" + +#include #include namespace nc::asset { +struct Texture; struct TextureWithId; struct TextureUpdateEventData; -class TextureAssetManager : public IAssetService +class TextureAssetManager : public IAssetService, + public IRuntimeTextureLoader { public: explicit TextureAssetManager(const std::string& texturesAssetDirectory, uint32_t maxTextures); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto LoadFromMemory(const std::string& key, Texture texture) -> bool override; + auto LoadFromRGBA(const std::string& key, + std::span rgbaData, + uint32_t width, + uint32_t height, + TextureFormat format = TextureFormat::RGBA8_UNORM) -> bool override; auto Unload(const std::string& path) -> bool override; void UnloadAll() override; auto Acquire(const std::string& path) const -> TextureView override; diff --git a/source/ncengine/ui/editor/impl/EditorUI.cpp b/source/ncengine/ui/editor/impl/EditorUI.cpp index 3efb8e0a7..d0ac231c9 100644 --- a/source/ncengine/ui/editor/impl/EditorUI.cpp +++ b/source/ncengine/ui/editor/impl/EditorUI.cpp @@ -45,6 +45,19 @@ void ToolbarLayout() const auto pos = ImVec2{xPadding + screenExtent.x * g_pivotCenter.x, yPadding}; ImGui::SetNextWindowPos(pos, ImGuiCond_FirstUseEver, g_pivotCenter); } + +// For runtime texture test +void LoadBlackTexture() +{ + std::vector blackPixels; + blackPixels.reserve(64); + for (int i = 0; i < 16; ++i) + { + blackPixels.insert(blackPixels.end(), {0, 0, 0, 255}); + } + + nc::asset::LoadTextureFromRGBA("editor::black", blackPixels, 4, 4); +} } // anonymous namespace namespace nc::ui::editor @@ -58,10 +71,19 @@ EditorUI::EditorUI(EditorContext& ctx) m_postProcessDialog{}, m_environmentDialog{} { + LoadBlackTexture(); } void EditorUI::Draw(EditorContext& ctx) { + if (!m_runtimeTextureLoaded) + { + auto& ncGraphics = *ctx.modules.Get(); + auto blackTextureAsset = nc::asset::AcquireTextureAsset("editor::black"); + m_runtimeTextureTest = static_cast(ncGraphics.GetTextureView(nc::TextureViewType::Asset, blackTextureAsset.index)); + m_runtimeTextureLoaded = true; + } + ctx.dimensions = ImVec2{window::GetDimensions()}; DrawOverlays(ctx.dimensions); auto& ncAsset = *ctx.modules.Get(); @@ -188,6 +210,7 @@ void EditorUI::DrawMenu(EditorContext& ctx) } if (ImGui::BeginMenu("NcGraphics")) { + ImGui::Image(m_runtimeTextureTest, ImVec2{5, 5}); if (ImGui::MenuItem("Post Process FX")) { m_postProcessDialog.Open(ctx.modules.Get(), ctx.modules.Get()); diff --git a/source/ncengine/ui/editor/impl/EditorUI.h b/source/ncengine/ui/editor/impl/EditorUI.h index 7640dc917..ee8252034 100644 --- a/source/ncengine/ui/editor/impl/EditorUI.h +++ b/source/ncengine/ui/editor/impl/EditorUI.h @@ -31,6 +31,8 @@ class EditorUI SceneGraph m_sceneGraph; Inspector m_inspector; bool m_open = false; + ImTextureRef m_runtimeTextureTest; + bool m_runtimeTextureLoaded = false; // overlays FpsOverlay m_fpsOverlay; diff --git a/test/ncengine/asset/TextureAssetManager_tests.cpp b/test/ncengine/asset/TextureAssetManager_tests.cpp index 614c6327a..cf0051393 100644 --- a/test/ncengine/asset/TextureAssetManager_tests.cpp +++ b/test/ncengine/asset/TextureAssetManager_tests.cpp @@ -2,8 +2,11 @@ #include "asset/AssetData.h" #include "asset/manager/TextureAssetManager.h" +#include "ncasset/Assets.h" + #include #include +#include using namespace nc::asset; @@ -161,3 +164,115 @@ TEST_F(TextureAssetManager_tests, GetPath_NotLoaded_Throws) assetManager->UnloadAll(); EXPECT_THROW(assetManager->GetPath(view.id), nc::NcError); } + +TEST_F(TextureAssetManager_tests, LoadFromMemory_NotLoaded_ReturnsTrue) +{ + auto texture = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 255) + }; + auto actual = assetManager->LoadFromMemory("test_key", std::move(texture)); + EXPECT_TRUE(actual); +} + +TEST_F(TextureAssetManager_tests, LoadFromMemory_IsLoaded_ReturnsFalse) +{ + auto texture1 = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 255) + }; + auto texture2 = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 128) + }; + assetManager->LoadFromMemory("test_key", std::move(texture1)); + auto actual = assetManager->LoadFromMemory("test_key", std::move(texture2)); + EXPECT_FALSE(actual); +} + +TEST_F(TextureAssetManager_tests, LoadFromMemory_CanAcquire) +{ + auto texture = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 255) + }; + assetManager->LoadFromMemory("test_key", std::move(texture)); + auto view = assetManager->Acquire("test_key"); + EXPECT_NE(view.id, NullAssetId); + EXPECT_EQ(view.index, 0u); +} + +TEST_F(TextureAssetManager_tests, LoadFromRGBA_NotLoaded_ReturnsTrue) +{ + std::vector rgbaData(16, 255); + auto actual = assetManager->LoadFromRGBA("test_key", rgbaData, 2, 2); + EXPECT_TRUE(actual); +} + +TEST_F(TextureAssetManager_tests, LoadFromRGBA_IsLoaded_ReturnsFalse) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("test_key", rgbaData, 2, 2); + auto actual = assetManager->LoadFromRGBA("test_key", rgbaData, 2, 2); + EXPECT_FALSE(actual); +} + +TEST_F(TextureAssetManager_tests, LoadFromRGBA_CanAcquire) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("test_key", rgbaData, 2, 2); + auto view = assetManager->Acquire("test_key"); + EXPECT_NE(view.id, NullAssetId); + EXPECT_EQ(view.index, 0u); +} + +TEST_F(TextureAssetManager_tests, LoadFromRGBA_WithFormat_CanAcquire) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("test_key", rgbaData, 2, 2, TextureFormat::RGBA8_UNORM_SRGB); + auto view = assetManager->Acquire("test_key"); + EXPECT_NE(view.id, NullAssetId); +} + +TEST_F(TextureAssetManager_tests, LoadFromMemory_CanUnload) +{ + auto texture = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 255) + }; + assetManager->LoadFromMemory("test_key", std::move(texture)); + auto actual = assetManager->Unload("test_key"); + EXPECT_TRUE(actual); + EXPECT_FALSE(assetManager->IsLoaded("test_key")); +} + +TEST_F(TextureAssetManager_tests, LoadFromMemory_MixedWithFileLoad_CorrectIndices) +{ + assetManager->Load(Texture_base); + auto texture = Texture{ + .format = TextureFormat::RGBA8_UNORM, + .width = 2, + .height = 2, + .pixelData = std::vector(16, 255) + }; + assetManager->LoadFromMemory("memory_texture", std::move(texture)); + assetManager->Load(Texture_normal); + + auto view1 = assetManager->Acquire(Texture_base); + auto view2 = assetManager->Acquire("memory_texture"); + auto view3 = assetManager->Acquire(Texture_normal); + + EXPECT_EQ(view1.index, 0u); + EXPECT_EQ(view2.index, 1u); + EXPECT_EQ(view3.index, 2u); +} From 6d56feb4a9b23255b295fc08ef707329e14d87a2 Mon Sep 17 00:00:00 2001 From: Jaremie Romer Date: Sat, 27 Dec 2025 08:17:39 -0600 Subject: [PATCH 2/3] comment --- include/ncengine/asset/Assets.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ncengine/asset/Assets.h b/include/ncengine/asset/Assets.h index fb085fb60..7cc966784 100644 --- a/include/ncengine/asset/Assets.h +++ b/include/ncengine/asset/Assets.h @@ -63,7 +63,7 @@ void UnloadAllMeshAssets(); auto AcquireMeshAsset(const std::string& path) -> MeshView; auto AcquireMeshAsset(AssetId id) -> MeshView; -/** Supported file types: .nca +/** Supported file types: .nca, raw texture data * @note Unloading textures invalidates all TextureViews. It is intended * to be done on scene change. */ bool LoadTextureAsset(const std::string& path); From df17222e4d0d4b69738fe54da9b43150b1a467ca Mon Sep 17 00:00:00 2001 From: Jaremie Romer Date: Sat, 27 Dec 2025 12:58:39 -0600 Subject: [PATCH 3/3] Ignore non-serializable assets --- include/ncengine/asset/NcAsset.h | 6 +- sample/source/scenes/SmokeTest.cpp | 2 +- source/ncengine/asset/AssetService.h | 6 +- source/ncengine/asset/NcAssetImpl.cpp | 6 +- source/ncengine/asset/NcAssetImpl.h | 2 +- .../asset/manager/AudioClipAssetManager.cpp | 3 +- .../asset/manager/AudioClipAssetManager.h | 20 ++-- .../asset/manager/ConvexHullAssetManager.cpp | 3 +- .../asset/manager/ConvexHullAssetManager.h | 22 ++-- .../asset/manager/CubeMapAssetManager.cpp | 3 +- .../asset/manager/CubeMapAssetManager.h | 22 ++-- .../asset/manager/FontAssetManager.cpp | 3 +- .../ncengine/asset/manager/FontAssetManager.h | 22 ++-- .../asset/manager/MeshAssetManager.cpp | 3 +- .../ncengine/asset/manager/MeshAssetManager.h | 24 ++-- .../manager/MeshColliderAssetManager.cpp | 3 +- .../asset/manager/MeshColliderAssetManager.h | 22 ++-- .../asset/manager/ShaderAssetManager.cpp | 3 +- .../asset/manager/ShaderAssetManager.h | 20 ++-- .../manager/SkeletalAnimationAssetManager.cpp | 3 +- .../manager/SkeletalAnimationAssetManager.h | 22 ++-- .../asset/manager/TextureAssetManager.cpp | 56 +++++++++- .../asset/manager/TextureAssetManager.h | 29 ++--- source/ncengine/ui/editor/impl/EditorUI.cpp | 4 +- test/ncengine/AssetServiceStub.h | 34 +++--- .../asset/TextureAssetManager_tests.cpp | 104 ++++++++++++++++++ .../SceneSerialization_unit_tests.cpp | 3 +- 27 files changed, 307 insertions(+), 143 deletions(-) diff --git a/include/ncengine/asset/NcAsset.h b/include/ncengine/asset/NcAsset.h index 2c31e2158..1bd828e11 100644 --- a/include/ncengine/asset/NcAsset.h +++ b/include/ncengine/asset/NcAsset.h @@ -61,8 +61,10 @@ class NcAsset : public Module /** @brief Load assets from an AssetMap. */ virtual void LoadAssets(const AssetMap& assets) = 0; - /** @brief Get the names of all loaded assets as an AssetMap. */ - virtual auto GetLoadedAssets() const noexcept -> AssetMap = 0; + /** @brief Get the names of all loaded assets as an AssetMap. + * @param serializableOnly If true, excludes runtime assets that are not from disk and cannot survive serialization. + */ + virtual auto GetLoadedAssets(bool serializableOnly = false) const noexcept -> AssetMap = 0; /** @brief Get the path to a loaded asset given its id. */ virtual auto GetAssetPath(AssetType type, size_t id) const -> std::string_view = 0; diff --git a/sample/source/scenes/SmokeTest.cpp b/sample/source/scenes/SmokeTest.cpp index 062d95309..e16743f7a 100644 --- a/sample/source/scenes/SmokeTest.cpp +++ b/sample/source/scenes/SmokeTest.cpp @@ -80,7 +80,7 @@ void SmokeTest::Load(ecs::Ecs world, ModuleProvider modules) { isSecondPass = true; auto ncAsset = modules.Get(); - ::SaveScene(world, ncAsset->GetLoadedAssets()); + ::SaveScene(world, ncAsset->GetLoadedAssets(true)); auto ncScene = modules.Get(); ncScene->Queue(std::make_unique(quit)); ncScene->ScheduleTransition(); diff --git a/source/ncengine/asset/AssetService.h b/source/ncengine/asset/AssetService.h index 2a6ab40e5..6f19211c3 100644 --- a/source/ncengine/asset/AssetService.h +++ b/source/ncengine/asset/AssetService.h @@ -15,9 +15,9 @@ class IAssetServiceBase public: virtual ~IAssetServiceBase() = default; - virtual auto GetAllLoaded() const -> std::vector = 0; - virtual auto GetPath(AssetId hash) const -> std::string_view = 0; - virtual auto GetAssetType() const -> asset::AssetType = 0; + virtual auto GetAllLoaded(bool serializableOnly = false) const -> std::vector = 0; + virtual auto GetPath(AssetId hash) const -> std::string_view = 0; + virtual auto GetAssetType() const -> asset::AssetType = 0; }; /** Interface for services that manage assets. */ diff --git a/source/ncengine/asset/NcAssetImpl.cpp b/source/ncengine/asset/NcAssetImpl.cpp index b83426b34..c6734ad20 100644 --- a/source/ncengine/asset/NcAssetImpl.cpp +++ b/source/ncengine/asset/NcAssetImpl.cpp @@ -102,7 +102,7 @@ void NcAssetImpl::LoadAssets(const AssetMap& assets) m_textureManager->Load(assets.at(asset::AssetType::Texture)); } -auto NcAssetImpl::GetLoadedAssets() const noexcept -> AssetMap +auto NcAssetImpl::GetLoadedAssets(bool serializableOnly) const noexcept -> AssetMap { const auto managers = std::array { @@ -116,9 +116,9 @@ auto NcAssetImpl::GetLoadedAssets() const noexcept -> AssetMap }; auto out = AssetMap{}; - std::ranges::for_each(managers, [&out](auto manager) mutable + std::ranges::for_each(managers, [&out, serializableOnly](auto manager) mutable { - auto assets = manager->GetAllLoaded() + auto assets = manager->GetAllLoaded(serializableOnly) | std::views::transform([](auto&& name){ return std::string{name}; }); out.emplace(manager->GetAssetType(), std::vector{assets.begin(), assets.end()}); diff --git a/source/ncengine/asset/NcAssetImpl.h b/source/ncengine/asset/NcAssetImpl.h index 20feb1497..02a1c0024 100644 --- a/source/ncengine/asset/NcAssetImpl.h +++ b/source/ncengine/asset/NcAssetImpl.h @@ -43,7 +43,7 @@ class NcAssetImpl : public NcAsset auto OnMeshColliderUpdate() noexcept -> Signal& override; auto OnFontUpdate() noexcept -> Signal<>& override; void LoadAssets(const AssetMap& assets) override; - auto GetLoadedAssets() const noexcept -> AssetMap override; + auto GetLoadedAssets(bool serializableOnly = false) const noexcept -> AssetMap override; auto GetAssetPath(AssetType type, size_t id) const -> std::string_view override; private: diff --git a/source/ncengine/asset/manager/AudioClipAssetManager.cpp b/source/ncengine/asset/manager/AudioClipAssetManager.cpp index b3fd740da..0597a2305 100644 --- a/source/ncengine/asset/manager/AudioClipAssetManager.cpp +++ b/source/ncengine/asset/manager/AudioClipAssetManager.cpp @@ -72,8 +72,9 @@ auto AudioClipAssetManager::Acquire(AssetId id) const -> AudioClipView }; } -auto AudioClipAssetManager::GetAllLoaded() const -> std::vector +auto AudioClipAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_audioClips.keys()); } } //namespace nc::asset diff --git a/source/ncengine/asset/manager/AudioClipAssetManager.h b/source/ncengine/asset/manager/AudioClipAssetManager.h index 5fb7cdce6..733f4e286 100644 --- a/source/ncengine/asset/manager/AudioClipAssetManager.h +++ b/source/ncengine/asset/manager/AudioClipAssetManager.h @@ -15,16 +15,16 @@ class AudioClipAssetManager : public IAssetService public: explicit AudioClipAssetManager(const std::string& assetDirectory); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> AudioClipView override; - auto Acquire(AssetId id) const -> AudioClipView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_audioClips.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_audioClips.key_at(id); } - auto GetAssetType() const -> AssetType override { return AssetType::AudioClip; } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> AudioClipView override; + auto Acquire(AssetId id) const -> AudioClipView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_audioClips.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_audioClips.key_at(id); } + auto GetAssetType() const -> AssetType override { return AssetType::AudioClip; } private: StringMap m_audioClips; diff --git a/source/ncengine/asset/manager/ConvexHullAssetManager.cpp b/source/ncengine/asset/manager/ConvexHullAssetManager.cpp index 308016b40..049c7f9dd 100644 --- a/source/ncengine/asset/manager/ConvexHullAssetManager.cpp +++ b/source/ncengine/asset/manager/ConvexHullAssetManager.cpp @@ -104,8 +104,9 @@ auto ConvexHullAssetManager::Acquire(AssetId id) const -> ConvexHullView return ConvexHullView{id}; } -auto ConvexHullAssetManager::GetAllLoaded() const -> std::vector +auto ConvexHullAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_map.keys()); } } // namespace nc::asset diff --git a/source/ncengine/asset/manager/ConvexHullAssetManager.h b/source/ncengine/asset/manager/ConvexHullAssetManager.h index b598468c0..41c3b1323 100644 --- a/source/ncengine/asset/manager/ConvexHullAssetManager.h +++ b/source/ncengine/asset/manager/ConvexHullAssetManager.h @@ -17,17 +17,17 @@ class ConvexHullAssetManager : public IAssetService public: explicit ConvexHullAssetManager(const std::string& assetDirectory); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> ConvexHullView override; - auto Acquire(AssetId id) const -> ConvexHullView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_map.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_map.at(m_map.index(id)); } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::ConvexHull; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> ConvexHullView override; + auto Acquire(AssetId id) const -> ConvexHullView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_map.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_map.at(m_map.index(id)); } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::ConvexHull; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: StringTable m_map; diff --git a/source/ncengine/asset/manager/CubeMapAssetManager.cpp b/source/ncengine/asset/manager/CubeMapAssetManager.cpp index dc5eedf3a..bf1abc6c0 100644 --- a/source/ncengine/asset/manager/CubeMapAssetManager.cpp +++ b/source/ncengine/asset/manager/CubeMapAssetManager.cpp @@ -117,8 +117,9 @@ auto CubeMapAssetManager::Acquire(AssetId id) const -> CubeMapView }; } -auto CubeMapAssetManager::GetAllLoaded() const -> std::vector +auto CubeMapAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_cubeMapIds.keys()); } } // namespace nc::asset diff --git a/source/ncengine/asset/manager/CubeMapAssetManager.h b/source/ncengine/asset/manager/CubeMapAssetManager.h index 5daab720c..ada4e277b 100644 --- a/source/ncengine/asset/manager/CubeMapAssetManager.h +++ b/source/ncengine/asset/manager/CubeMapAssetManager.h @@ -16,17 +16,17 @@ class CubeMapAssetManager : public IAssetService explicit CubeMapAssetManager(const std::string& cubeMapAssetDirectory, uint32_t maxCubeMapsCount); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> CubeMapView override; - auto Acquire(AssetId id) const -> CubeMapView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_cubeMapIds.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_cubeMapIds.at(m_cubeMapIds.index(id)); } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::CubeMap; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> CubeMapView override; + auto Acquire(AssetId id) const -> CubeMapView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_cubeMapIds.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_cubeMapIds.at(m_cubeMapIds.index(id)); } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::CubeMap; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: StringTable m_cubeMapIds; diff --git a/source/ncengine/asset/manager/FontAssetManager.cpp b/source/ncengine/asset/manager/FontAssetManager.cpp index b9bbb03a4..7bdfd553f 100644 --- a/source/ncengine/asset/manager/FontAssetManager.cpp +++ b/source/ncengine/asset/manager/FontAssetManager.cpp @@ -121,8 +121,9 @@ auto FontAssetManager::IsLoaded(const FontInfo& font) const -> bool return m_fonts.contains(font); } -auto FontAssetManager::GetAllLoaded() const -> std::vector +auto FontAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; auto out = std::vector{}; out.reserve(m_fonts.size()); std::ranges::transform(m_fonts, std::back_inserter(out), [](const auto& pair) diff --git a/source/ncengine/asset/manager/FontAssetManager.h b/source/ncengine/asset/manager/FontAssetManager.h index 7920d7eba..9d99a8812 100644 --- a/source/ncengine/asset/manager/FontAssetManager.h +++ b/source/ncengine/asset/manager/FontAssetManager.h @@ -35,17 +35,17 @@ class FontAssetManager : public IAssetService public: explicit FontAssetManager(const std::string& assetDirectory); - auto Load(const FontInfo& font) -> bool override; - auto Load(std::span fonts) -> bool override; - auto Unload(const FontInfo& font) -> bool override; - void UnloadAll() override; - auto Acquire(const FontInfo& font) const -> FontView override; - auto Acquire(AssetId) const -> FontView override { throw NcError{"Not Implemented"};} - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const FontInfo& font) const -> bool override; - auto GetPath(AssetId) const -> std::string_view override { throw NcError{"Not Implemented"}; } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Font; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + auto Load(const FontInfo& font) -> bool override; + auto Load(std::span fonts) -> bool override; + auto Unload(const FontInfo& font) -> bool override; + void UnloadAll() override; + auto Acquire(const FontInfo& font) const -> FontView override; + auto Acquire(AssetId) const -> FontView override { throw NcError{"Not Implemented"};} + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const FontInfo& font) const -> bool override; + auto GetPath(AssetId) const -> std::string_view override { throw NcError{"Not Implemented"}; } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Font; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: std::unordered_map m_fonts; diff --git a/source/ncengine/asset/manager/MeshAssetManager.cpp b/source/ncengine/asset/manager/MeshAssetManager.cpp index 17f3aefbd..4423d4d92 100644 --- a/source/ncengine/asset/manager/MeshAssetManager.cpp +++ b/source/ncengine/asset/manager/MeshAssetManager.cpp @@ -174,8 +174,9 @@ auto MeshAssetManager::Acquire(AssetId id) const -> MeshView return m_accessors.at(index); } -auto MeshAssetManager::GetAllLoaded() const -> std::vector +auto MeshAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_accessors.keys()); } } // namespace nc::asset diff --git a/source/ncengine/asset/manager/MeshAssetManager.h b/source/ncengine/asset/manager/MeshAssetManager.h index fe11b5904..e53eb33b7 100644 --- a/source/ncengine/asset/manager/MeshAssetManager.h +++ b/source/ncengine/asset/manager/MeshAssetManager.h @@ -16,18 +16,18 @@ class MeshAssetManager : public IAssetService public: explicit MeshAssetManager(const std::string& assetDirectory); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> MeshView override; - auto Acquire(AssetId id) const -> MeshView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_accessors.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_accessors.key_at(id); } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Mesh; } - auto OnBoneUpdate() -> decltype(auto) { return (m_onBoneUpdate); } - auto OnMeshUpdate() -> decltype(auto) { return (m_onMeshUpdate); } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> MeshView override; + auto Acquire(AssetId id) const -> MeshView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_accessors.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_accessors.key_at(id); } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Mesh; } + auto OnBoneUpdate() -> decltype(auto) { return (m_onBoneUpdate); } + auto OnMeshUpdate() -> decltype(auto) { return (m_onMeshUpdate); } private: std::vector m_vertexData; diff --git a/source/ncengine/asset/manager/MeshColliderAssetManager.cpp b/source/ncengine/asset/manager/MeshColliderAssetManager.cpp index e072875a1..0b0c8ffcd 100644 --- a/source/ncengine/asset/manager/MeshColliderAssetManager.cpp +++ b/source/ncengine/asset/manager/MeshColliderAssetManager.cpp @@ -104,8 +104,9 @@ auto MeshColliderAssetManager::Acquire(AssetId id) const -> MeshColliderView return MeshColliderView{id}; } -auto MeshColliderAssetManager::GetAllLoaded() const -> std::vector +auto MeshColliderAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_map.keys()); } } // namespace nc::asset diff --git a/source/ncengine/asset/manager/MeshColliderAssetManager.h b/source/ncengine/asset/manager/MeshColliderAssetManager.h index 25e9148c3..f098603f6 100644 --- a/source/ncengine/asset/manager/MeshColliderAssetManager.h +++ b/source/ncengine/asset/manager/MeshColliderAssetManager.h @@ -17,17 +17,17 @@ class MeshColliderAssetManager : public IAssetService bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> MeshColliderView override; - auto Acquire(AssetId id) const -> MeshColliderView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_map.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_map.at(m_map.index(id)); } - auto GetAssetType() const -> AssetType override { return AssetType::MeshCollider; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> MeshColliderView override; + auto Acquire(AssetId id) const -> MeshColliderView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_map.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_map.at(m_map.index(id)); } + auto GetAssetType() const -> AssetType override { return AssetType::MeshCollider; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: StringTable m_map; diff --git a/source/ncengine/asset/manager/ShaderAssetManager.cpp b/source/ncengine/asset/manager/ShaderAssetManager.cpp index 3860c0dc1..ac235031f 100644 --- a/source/ncengine/asset/manager/ShaderAssetManager.cpp +++ b/source/ncengine/asset/manager/ShaderAssetManager.cpp @@ -229,8 +229,9 @@ auto ShaderAssetManager::IsLoaded(const std::string& path) const -> bool return std::ranges::any_of(m_shaderFlyweights.begin(), m_shaderFlyweights.end(), [path](const auto& view) { return view.uid == path; }); } -auto ShaderAssetManager::GetAllLoaded() const -> std::vector +auto ShaderAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_shaderFlyweights, [](const auto& item) { return std::string_view{item.uid}; diff --git a/source/ncengine/asset/manager/ShaderAssetManager.h b/source/ncengine/asset/manager/ShaderAssetManager.h index 333e373a4..6a68fbafe 100644 --- a/source/ncengine/asset/manager/ShaderAssetManager.h +++ b/source/ncengine/asset/manager/ShaderAssetManager.h @@ -20,16 +20,16 @@ class ShaderAssetManager final : public IAssetService public: explicit ShaderAssetManager(const std::string& assetDirectory); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> ShaderView override; - auto Acquire(AssetId) const -> ShaderView override { throw NcError{"Not Implemented"}; } - auto IsLoaded(const std::string& path) const -> bool override; - auto GetAllLoaded() const -> std::vector override; - auto GetPath(AssetId) const -> std::string_view override { throw NcError{"Not Implemented"}; } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Shader; } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> ShaderView override; + auto Acquire(AssetId) const -> ShaderView override { throw NcError{"Not Implemented"}; } + auto IsLoaded(const std::string& path) const -> bool override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto GetPath(AssetId) const -> std::string_view override { throw NcError{"Not Implemented"}; } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::Shader; } private: std::vector m_shaderFlyweights; diff --git a/source/ncengine/asset/manager/SkeletalAnimationAssetManager.cpp b/source/ncengine/asset/manager/SkeletalAnimationAssetManager.cpp index cfc733a6e..44804f447 100644 --- a/source/ncengine/asset/manager/SkeletalAnimationAssetManager.cpp +++ b/source/ncengine/asset/manager/SkeletalAnimationAssetManager.cpp @@ -113,8 +113,9 @@ auto SkeletalAnimationAssetManager::Acquire(AssetId id) const -> SkeletalAnimati }; } -auto SkeletalAnimationAssetManager::GetAllLoaded() const -> std::vector +auto SkeletalAnimationAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + (void)serializableOnly; return GetPaths(m_table.keys()); } } // namespace nc::asset diff --git a/source/ncengine/asset/manager/SkeletalAnimationAssetManager.h b/source/ncengine/asset/manager/SkeletalAnimationAssetManager.h index 363a5614e..72e600e03 100644 --- a/source/ncengine/asset/manager/SkeletalAnimationAssetManager.h +++ b/source/ncengine/asset/manager/SkeletalAnimationAssetManager.h @@ -16,17 +16,17 @@ class SkeletalAnimationAssetManager : public IAssetService bool override; - auto Load(std::span paths) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> SkeletalAnimationView override; - auto Acquire(AssetId id) const -> SkeletalAnimationView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_table.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_table.at(m_table.index(id)); } - auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::SkeletalAnimation; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> SkeletalAnimationView override; + auto Acquire(AssetId id) const -> SkeletalAnimationView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_table.contains(path); } + auto GetPath(AssetId id) const -> std::string_view override { return m_table.at(m_table.index(id)); } + auto GetAssetType() const -> asset::AssetType override { return asset::AssetType::SkeletalAnimation; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: StringTable m_table; diff --git a/source/ncengine/asset/manager/TextureAssetManager.cpp b/source/ncengine/asset/manager/TextureAssetManager.cpp index 491ff0a21..1aeb91670 100644 --- a/source/ncengine/asset/manager/TextureAssetManager.cpp +++ b/source/ncengine/asset/manager/TextureAssetManager.cpp @@ -5,8 +5,25 @@ #include "ncasset/Import.h" +#include +#include #include +#include +namespace +{ +template +auto GetSerializableAssetPaths(const std::vector& vec, Proj&& proj) -> std::vector +{ + auto out = std::vector{}; + out.reserve(vec.size()); + + auto view = vec | std::ranges::views::filter([](const auto& item) { return !item.contains(nc::asset::RuntimeKey); }) + | std::ranges::views::transform(std::forward(proj)); + + return std::vector(view.begin(), view.end()); +} +} namespace nc::asset { TextureAssetManager::TextureAssetManager(const std::string& texturesAssetDirectory, uint32_t maxTextures) @@ -74,6 +91,7 @@ auto TextureAssetManager::Load(std::span paths) -> bool auto TextureAssetManager::LoadFromMemory(const std::string& key, Texture texture) -> bool { + auto runtimeTextureKey = RuntimeKey + key; if (m_table.size() + 1 >= m_maxTextureCount) { throw NcError("Cannot exceed max texture count."); @@ -84,8 +102,8 @@ auto TextureAssetManager::LoadFromMemory(const std::string& key, Texture texture return false; } - auto textureWithId = TextureWithId{std::move(texture), m_table.hash(key)}; - m_table.emplace(key); + auto textureWithId = TextureWithId{std::move(texture), m_table.hash(runtimeTextureKey)}; + m_table.emplace(runtimeTextureKey); m_onUpdate.Emit(TextureUpdateEventData{ UpdateAction::Load, std::span{&textureWithId, 1} @@ -111,7 +129,9 @@ auto TextureAssetManager::LoadFromRGBA(const std::string& key, auto TextureAssetManager::Unload(const std::string& path) -> bool { - if (!m_table.erase(path)) + auto runtimeTextureKey = RuntimeKey + path; + + if (!m_table.erase(path) && !m_table.erase(runtimeTextureKey)) return false; m_onUpdate.Emit(TextureUpdateEventData{ @@ -133,7 +153,15 @@ void TextureAssetManager::UnloadAll() auto TextureAssetManager::Acquire(const std::string& path) const -> TextureView { - NC_ASSERT(m_table.contains(path), fmt::format("Texture is not loaded: '{}'", path)); + auto runtimeTextureKey = RuntimeKey + path; + if (!m_table.contains(path)) + { + if (!m_table.contains(runtimeTextureKey)) + { + throw nc::NcError("Texture is not loaded: '{}'", path); + } + return Acquire(m_table.hash(runtimeTextureKey)); + } return Acquire(m_table.hash(path)); } @@ -147,11 +175,29 @@ auto TextureAssetManager::Acquire(AssetId id) const -> TextureView }; } -auto TextureAssetManager::GetAllLoaded() const -> std::vector +auto TextureAssetManager::GetAllLoaded(bool serializableOnly) const -> std::vector { + if (serializableOnly) + { + return GetSerializableAssetPaths(m_table.keys(), [](const auto& data) + { + return std::string_view{data}; + }); + } + return GetPaths(m_table.keys(), [](const auto& data) { return std::string_view{data}; }); } + +auto TextureAssetManager::GetPath(AssetId id) const -> std::string_view +{ + auto path = m_table.at(m_table.index(id)); + if (path.starts_with(RuntimeKey)) + { + path.remove_prefix(RuntimeKeyLength); + } + return path; +} } // namespace nc::asset diff --git a/source/ncengine/asset/manager/TextureAssetManager.h b/source/ncengine/asset/manager/TextureAssetManager.h index 142c08bd2..93865486b 100644 --- a/source/ncengine/asset/manager/TextureAssetManager.h +++ b/source/ncengine/asset/manager/TextureAssetManager.h @@ -16,6 +16,9 @@ struct Texture; struct TextureWithId; struct TextureUpdateEventData; +constexpr auto RuntimeKey = "RuntimeKey::"; +constexpr std::size_t RuntimeKeyLength = std::char_traits::length(RuntimeKey); + class TextureAssetManager : public IAssetService, public IRuntimeTextureLoader { @@ -23,23 +26,23 @@ class TextureAssetManager : public IAssetService, explicit TextureAssetManager(const std::string& texturesAssetDirectory, uint32_t maxTextures); - auto Load(const std::string& path) -> bool override; - auto Load(std::span paths) -> bool override; - auto LoadFromMemory(const std::string& key, Texture texture) -> bool override; + auto Load(const std::string& path) -> bool override; + auto Load(std::span paths) -> bool override; + auto LoadFromMemory(const std::string& key, Texture texture) -> bool override; auto LoadFromRGBA(const std::string& key, std::span rgbaData, uint32_t width, uint32_t height, - TextureFormat format = TextureFormat::RGBA8_UNORM) -> bool override; - auto Unload(const std::string& path) -> bool override; - void UnloadAll() override; - auto Acquire(const std::string& path) const -> TextureView override; - auto Acquire(AssetId id) const -> TextureView override; - auto GetAllLoaded() const -> std::vector override; - auto IsLoaded(const std::string& path) const -> bool override { return m_table.contains(path); } - auto GetPath(AssetId id) const -> std::string_view override { return m_table.at(m_table.index(id)); } - auto GetAssetType() const -> AssetType override { return AssetType::Texture; } - auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } + TextureFormat format = TextureFormat::RGBA8_UNORM) -> bool override; + auto Unload(const std::string& path) -> bool override; + void UnloadAll() override; + auto Acquire(const std::string& path) const -> TextureView override; + auto Acquire(AssetId id) const -> TextureView override; + auto GetAllLoaded(bool serializableOnly = false) const -> std::vector override; + auto IsLoaded(const std::string& path) const -> bool override { return m_table.contains(path) || m_table.contains(RuntimeKey + path); } + auto GetPath(AssetId id) const -> std::string_view override; + auto GetAssetType() const -> AssetType override { return AssetType::Texture; } + auto OnUpdate() -> decltype(auto) { return (m_onUpdate); } private: StringTable m_table; diff --git a/source/ncengine/ui/editor/impl/EditorUI.cpp b/source/ncengine/ui/editor/impl/EditorUI.cpp index d0ac231c9..17a5b010c 100644 --- a/source/ncengine/ui/editor/impl/EditorUI.cpp +++ b/source/ncengine/ui/editor/impl/EditorUI.cpp @@ -150,7 +150,7 @@ auto EditorUI::ProcessInput(const EditorHotkeys& hotkeys, asset::NcAsset& ncAsse if (KeyDown(hotkeys.openNewSceneDialog)) m_newSceneDialog.Open(); else if (KeyDown(hotkeys.openSaveSceneDialog)) - m_saveSceneDialog.Open(ncAsset.GetLoadedAssets()); + m_saveSceneDialog.Open(ncAsset.GetLoadedAssets(true)); else if (KeyDown(hotkeys.openLoadSceneDialog)) m_loadSceneDialog.Open(); @@ -188,7 +188,7 @@ void EditorUI::DrawMenu(EditorContext& ctx) if (ImGui::MenuItem("New")) m_newSceneDialog.Open(); if (ImGui::MenuItem("Save")) - m_saveSceneDialog.Open(ctx.modules.Get()->GetLoadedAssets()); + m_saveSceneDialog.Open(ctx.modules.Get()->GetLoadedAssets(true)); if (ImGui::MenuItem("Load")) m_loadSceneDialog.Open(); diff --git a/test/ncengine/AssetServiceStub.h b/test/ncengine/AssetServiceStub.h index 690ef92a1..f5a8970c0 100644 --- a/test/ncengine/AssetServiceStub.h +++ b/test/ncengine/AssetServiceStub.h @@ -11,21 +11,21 @@ * Example usage: * DEFINE_ASSET_SERVICE_STUB(stubService, nc::asset::AssetType::AudioClip, nc::AudioClipView, std::string); */ - -#define DEFINE_ASSET_SERVICE_STUB(className, assetType, viewType, inputType) \ -struct className : public nc::asset::IAssetService \ -{ \ - viewType view; \ - const char* path = "test_path"; \ - \ - bool Load(const inputType&) override { return true; } \ - bool Load(std::span) override { return true; } \ - bool Unload(const inputType&) override { return true; } \ - void UnloadAll() override {} \ - bool IsLoaded(const inputType&) const override { return true; } \ - auto GetPath(nc::asset::AssetId) const -> std::string_view override { return path; } \ - auto GetAllLoaded() const -> std::vector override { return {}; } \ - auto GetAssetType() const -> nc::asset::AssetType override { return assetType; } \ - auto Acquire(const inputType&) const -> viewType override { return view; } \ - auto Acquire(nc::asset::AssetId) const -> viewType override { return view; } \ + +#define DEFINE_ASSET_SERVICE_STUB(className, assetType, viewType, inputType) \ +struct className : public nc::asset::IAssetService \ +{ \ + viewType view; \ + const char* path = "test_path"; \ + \ + bool Load(const inputType&) override { return true; } \ + bool Load(std::span) override { return true; } \ + bool Unload(const inputType&) override { return true; } \ + void UnloadAll() override {} \ + bool IsLoaded(const inputType&) const override { return true; } \ + auto GetPath(nc::asset::AssetId) const -> std::string_view override { return path; } \ + auto GetAllLoaded(bool) const -> std::vector override { return {}; } \ + auto GetAssetType() const -> nc::asset::AssetType override { return assetType; } \ + auto Acquire(const inputType&) const -> viewType override { return view; } \ + auto Acquire(nc::asset::AssetId) const -> viewType override { return view; } \ }; className className##Instance; diff --git a/test/ncengine/asset/TextureAssetManager_tests.cpp b/test/ncengine/asset/TextureAssetManager_tests.cpp index cf0051393..33220a460 100644 --- a/test/ncengine/asset/TextureAssetManager_tests.cpp +++ b/test/ncengine/asset/TextureAssetManager_tests.cpp @@ -276,3 +276,107 @@ TEST_F(TextureAssetManager_tests, LoadFromMemory_MixedWithFileLoad_CorrectIndice EXPECT_EQ(view2.index, 1u); EXPECT_EQ(view3.index, 2u); } + +TEST_F(TextureAssetManager_tests, GetAllLoaded_Default_IncludesRuntimeTextures) +{ + assetManager->Load(Texture_base); + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2); + + auto all = assetManager->GetAllLoaded(); + EXPECT_EQ(all.size(), 2u); +} + +TEST_F(TextureAssetManager_tests, GetAllLoaded_SerializableOnly_ExcludesRuntimeTextures) +{ + assetManager->Load(Texture_base); + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2); + + auto serializable = assetManager->GetAllLoaded(true); + EXPECT_EQ(serializable.size(), 1u); + EXPECT_EQ(serializable[0], Texture_base); +} + +TEST_F(TextureAssetManager_tests, GetAllLoaded_OnlyRuntimeTextures_SerializableReturnsEmpty) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime1", rgbaData, 2, 2); + assetManager->LoadFromRGBA("runtime2", rgbaData, 2, 2); + + auto serializable = assetManager->GetAllLoaded(true); + auto all = assetManager->GetAllLoaded(false); + + EXPECT_EQ(serializable.size(), 0u); + EXPECT_EQ(all.size(), 2u); +} + +TEST_F(TextureAssetManager_tests, IsLoaded_RuntimeTexture_ReturnsTrueWithOriginalKey) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("my_runtime_tex", rgbaData, 2, 2); + + EXPECT_TRUE(assetManager->IsLoaded("my_runtime_tex")); +} + +TEST_F(TextureAssetManager_tests, GetPath_RuntimeTexture_ReturnsUnprefixedKey) +{ + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("test_runtime", rgbaData, 2, 2); + + auto view = assetManager->Acquire("test_runtime"); + auto path = assetManager->GetPath(view.id); + + EXPECT_FALSE(path.starts_with(RuntimeKey)); + EXPECT_NE(path.find("test_runtime"), std::string_view::npos); +} + +TEST_F(TextureAssetManager_tests, UnloadAll_ClearsRuntimeTextures) +{ + assetManager->Load(Texture_base); + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2); + + assetManager->UnloadAll(); + + EXPECT_FALSE(assetManager->IsLoaded(Texture_base)); + EXPECT_FALSE(assetManager->IsLoaded("runtime_tex")); +} + +TEST_F(TextureAssetManager_tests, Unload_RuntimeTexture_DoesNotAffectFileTextures) +{ + assetManager->Load(Texture_base); + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2); + + assetManager->Unload("runtime_tex"); + + EXPECT_TRUE(assetManager->IsLoaded(Texture_base)); + EXPECT_FALSE(assetManager->IsLoaded("runtime_tex")); +} + +TEST_F(TextureAssetManager_tests, Unload_FileTexture_DoesNotAffectRuntimeTextures) +{ + assetManager->Load(Texture_base); + std::vector rgbaData(16, 255); + assetManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2); + + assetManager->Unload(Texture_base); + + EXPECT_FALSE(assetManager->IsLoaded(Texture_base)); + EXPECT_TRUE(assetManager->IsLoaded("runtime_tex")); +} + +TEST_F(TextureAssetManager_tests, LoadFromMemory_ExceedsMaxTextures_Throws) +{ + auto smallManager = std::make_unique(NC_TEST_COLLATERAL_DIRECTORY, 2); + smallManager->Load(Texture_base); + + std::vector rgbaData(16, 255); + EXPECT_THROW(smallManager->LoadFromRGBA("runtime_tex", rgbaData, 2, 2), nc::NcError); +} + +TEST_F(TextureAssetManager_tests, Acquire_NotLoaded_Throws) +{ + EXPECT_THROW(assetManager->Acquire("nonexistent"), nc::NcError); +} diff --git a/test/ncengine/serialize/SceneSerialization_unit_tests.cpp b/test/ncengine/serialize/SceneSerialization_unit_tests.cpp index 3438c5e16..a3821e160 100644 --- a/test/ncengine/serialize/SceneSerialization_unit_tests.cpp +++ b/test/ncengine/serialize/SceneSerialization_unit_tests.cpp @@ -30,8 +30,9 @@ class NcAssetMock : public NcAsset m_assets.insert(std::cbegin(assets), std::cend(assets)); } - auto GetLoadedAssets() const noexcept -> AssetMap override + auto GetLoadedAssets(bool serializableOnly) const noexcept -> AssetMap override { + (void)serializableOnly; return m_assets; }