diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h index 78cea9a3e9..b4831ea650 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h +++ b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.h @@ -11,6 +11,7 @@ namespace VideoCommon { struct MaterialData; +struct MeshData; struct PixelShaderData; struct TextureData; @@ -48,5 +49,8 @@ public: // Loads a material virtual LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) = 0; + + // Loads a mesh + virtual LoadInfo LoadMesh(const AssetID& asset_id, MeshData* data) = 0; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp index 134b18b496..dfe640ffe8 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp +++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp @@ -97,4 +97,10 @@ CustomAssetLoader::LoadMaterial(const CustomAssetLibrary::AssetID& asset_id, { return LoadOrCreateAsset(asset_id, m_materials, std::move(library)); } + +std::shared_ptr CustomAssetLoader::LoadMesh(const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library) +{ + return LoadOrCreateAsset(asset_id, m_meshes, std::move(library)); +} } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.h b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h index 920f62e830..7ddb746ca6 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLoader.h +++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h @@ -13,6 +13,7 @@ #include "Common/WorkQueueThread.h" #include "VideoCommon/Assets/CustomAsset.h" #include "VideoCommon/Assets/MaterialAsset.h" +#include "VideoCommon/Assets/MeshAsset.h" #include "VideoCommon/Assets/ShaderAsset.h" #include "VideoCommon/Assets/TextureAsset.h" @@ -47,6 +48,9 @@ public: std::shared_ptr LoadMaterial(const CustomAssetLibrary::AssetID& asset_id, std::shared_ptr library); + std::shared_ptr LoadMesh(const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library); + private: // TODO C++20: use a 'derived_from' concept against 'CustomAsset' when available template @@ -80,6 +84,7 @@ private: std::map> m_game_textures; std::map> m_pixel_shaders; std::map> m_materials; + std::map> m_meshes; std::thread m_asset_monitor_thread; Common::Flag m_asset_monitor_thread_shutdown; diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp index ff20d117c2..be5159b448 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp @@ -4,13 +4,16 @@ #include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h" #include +#include #include #include "Common/FileUtil.h" +#include "Common/IOFile.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" #include "VideoCommon/Assets/MaterialAsset.h" +#include "VideoCommon/Assets/MeshAsset.h" #include "VideoCommon/Assets/ShaderAsset.h" #include "VideoCommon/Assets/TextureAsset.h" #include "VideoCommon/RenderState.h" @@ -220,6 +223,110 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const As return LoadInfo{json_data.size(), GetLastAssetWriteTime(asset_id)}; } +CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMesh(const AssetID& asset_id, + MeshData* data) +{ + const auto asset_map = GetAssetMapForID(asset_id); + + // Asset map for a mesh is the mesh and some metadata + if (asset_map.size() != 2) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have two files mapped!", asset_id); + return {}; + } + + const auto metadata = asset_map.find("metadata"); + const auto mesh = asset_map.find("mesh"); + if (metadata == asset_map.end()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a metadata entry mapped!", asset_id); + return {}; + } + + if (mesh == asset_map.end()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a mesh entry mapped!", asset_id); + return {}; + } + + std::size_t metadata_size; + { + std::error_code ec; + metadata_size = std::filesystem::file_size(metadata->second, ec); + if (ec) + { + ERROR_LOG_FMT(VIDEO, + "Asset '{}' error - failed to get mesh metadata file size with error '{}'!", + asset_id, ec); + return {}; + } + } + std::size_t mesh_size; + { + std::error_code ec; + mesh_size = std::filesystem::file_size(mesh->second, ec); + if (ec) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to get mesh file size with error '{}'!", + asset_id, ec); + return {}; + } + } + const auto approx_mem_size = metadata_size + mesh_size; + + File::IOFile file(PathToString(mesh->second), "rb"); + if (!file.IsOpen()) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to open mesh file '{}'!", asset_id, + PathToString(mesh->second)); + return {}; + } + + std::vector bytes; + bytes.reserve(file.GetSize()); + file.ReadBytes(bytes.data(), file.GetSize()); + if (!MeshData::FromDolphinMesh(bytes, data)) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the mesh file '{}'!", asset_id, + PathToString(mesh->second)); + return {}; + } + + std::string json_data; + if (!File::ReadFileToString(PathToString(metadata->second), json_data)) + { + ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the json file '{}'!", asset_id, + PathToString(metadata->second)); + return {}; + } + + picojson::value root; + const auto error = picojson::parse(root, json_data); + + if (!error.empty()) + { + ERROR_LOG_FMT(VIDEO, + "Asset '{}' error - failed to load the json file '{}', due to parse error: {}", + asset_id, PathToString(metadata->second), error); + return {}; + } + if (!root.is()) + { + ERROR_LOG_FMT( + VIDEO, + "Asset '{}' error - failed to load the json file '{}', due to root not being an object!", + asset_id, PathToString(metadata->second)); + return {}; + } + + const auto& root_obj = root.get(); + + if (!MeshData::FromJson(asset_id, root_obj, data)) + return {}; + + return LoadInfo{approx_mem_size, GetLastAssetWriteTime(asset_id)}; +} + CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const AssetID& asset_id, TextureData* data) { diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h index 244b49c88b..c4d99baf82 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h @@ -23,6 +23,7 @@ public: LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) override; LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override; LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override; + LoadInfo LoadMesh(const AssetID& asset_id, MeshData* data) override; // Gets the latest time from amongst all the files in the asset map TimeType GetLastAssetWriteTime(const AssetID& asset_id) const override; diff --git a/Source/Core/VideoCommon/Assets/MeshAsset.cpp b/Source/Core/VideoCommon/Assets/MeshAsset.cpp index b8dd029375..05a4ba0961 100644 --- a/Source/Core/VideoCommon/Assets/MeshAsset.cpp +++ b/Source/Core/VideoCommon/Assets/MeshAsset.cpp @@ -12,6 +12,7 @@ #include "Common/IOFile.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" +#include "VideoCommon/Assets/CustomAssetLibrary.h" namespace VideoCommon { @@ -645,4 +646,18 @@ bool MeshData::FromGLTF(std::string_view gltf_file, MeshData* data) ERROR_LOG_FMT(VIDEO, "GLTF '{}' has invalid extension", gltf_file); return false; } + +CustomAssetLibrary::LoadInfo MeshAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id) +{ + auto potential_data = std::make_shared(); + const auto loaded_info = m_owning_library->LoadMesh(asset_id, potential_data.get()); + if (loaded_info.m_bytes_loaded == 0) + return {}; + { + std::lock_guard lk(m_data_lock); + m_loaded = true; + m_data = std::move(potential_data); + } + return loaded_info; +} } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/MeshAsset.h b/Source/Core/VideoCommon/Assets/MeshAsset.h index eb23f39783..a678b14cee 100644 --- a/Source/Core/VideoCommon/Assets/MeshAsset.h +++ b/Source/Core/VideoCommon/Assets/MeshAsset.h @@ -57,4 +57,13 @@ struct MeshData std::map> m_mesh_material_to_material_asset_id; }; + +class MeshAsset final : public CustomLoadableAsset +{ +public: + using CustomLoadableAsset::CustomLoadableAsset; + +private: + CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override; +}; } // namespace VideoCommon