#include #include #include #include #include #include #include "AssetFSIOSystem.h" namespace { std::string ToReference(std::string_view reference) { if (reference.empty()) { throw std::invalid_argument("Asset reference cannot be empty"); } return std::string{reference}; } std::optional FindCookedModel(const std::string& reference) { if (reference.find("://") == std::string::npos) { return std::nullopt; } try { const std::filesystem::path cookedPath = AssetFS::GetInstance().GetCookedPathForFile(reference); if (cookedPath.extension() == ModelFile::Extension) { return cookedPath; } } catch (const std::exception& exception) { spdlog::warn("Importing {} instead of using its cooked model: {}", reference, exception.what()); } return std::nullopt; } // Assimp opens files itself, so with packed assets it has to go through AssetFS std::unique_ptr MakeIOSystem() { if (!AssetFS::GetInstance().IsPacked()) { return nullptr; } return std::make_unique(); } // Cooked texture paths are relative to the model file, imported ones are absolute void ResolveTexturePaths(ModelDoc::ModelAsset& model, const std::filesystem::path& modelDir) { for (ModelDoc::MaterialInfo& material : model.materials) { for (ModelDoc::TextureInfo& texture : material.textures) { if (texture.embedded || texture.path.empty() || std::filesystem::path{texture.path}.is_absolute()) { continue; } texture.path = (modelDir / texture.path).lexically_normal().generic_string(); } } } } std::filesystem::path AssetManager::Resolve(std::string_view reference) const { const std::string value = ToReference(reference); if (value.find("://") != std::string::npos) { return AssetFS::GetInstance().GetFullPath(value); } const std::filesystem::path path{value}; if (path.is_absolute()) { return path; } throw std::invalid_argument( "Non-virtual asset references must be absolute: " + value); } ModelDoc::ModelAsset AssetManager::LoadModel( std::string_view reference, const ModelDoc::LoadOptions& options) const { const std::string sourceReference = ToReference(reference); ModelDoc::ModelAsset model = LoadCookedOrImport(sourceReference, options); model.sourcePath = sourceReference; model.skeleton.assetReference.path = sourceReference; for (std::size_t index = 0; index < model.primitives.size(); ++index) { auto& primitive = model.primitives[index]; primitive.mesh.assetReference.path = sourceReference; primitive.mesh.assetReference.subresource = primitive.sourceMeshIndex != ModelDoc::InvalidIndex ? "mesh:" + std::to_string(primitive.sourceMeshIndex) : "primitive:" + std::to_string(index); if (primitive.mesh.skeleton) { primitive.mesh.skeleton->assetReference.path = sourceReference; } } for (std::size_t index = 0; index < model.materials.size(); ++index) { auto& material = model.materials[index]; material.assetReference.path = sourceReference; material.assetReference.subresource = "material:" + std::to_string(index); } for (std::size_t index = 0; index < model.animations.size(); ++index) { auto& animation = model.animations[index]; animation.assetReference.path = sourceReference; animation.assetReference.subresource = "animation:" + std::to_string(index); } return model; } ModelDoc::ModelAsset AssetManager::LoadCookedOrImport( const std::string& reference, const ModelDoc::LoadOptions& options) const { if (const auto cookedPath = FindCookedModel(reference)) { ModelDoc::ModelAsset model = ModelFile::Read(AssetFS::GetInstance().ReadFile(*cookedPath)); if (ModelFile::SupportsLoadOptions(model, options)) { ModelFile::ApplyLoadOptions(model, options); ResolveTexturePaths(model, cookedPath->parent_path()); spdlog::debug("Loaded cooked model {}", reference); return model; } spdlog::debug("Importing {}, its cooked model doesn't match the requested load options", reference); } return ModelDoc::LoadModel(Resolve(reference).generic_string(), options, MakeIOSystem()); } std::vector AssetManager::LoadAnimationClips( std::string_view reference, const Skeleton& targetSkeleton) const { const std::string sourceReference = ToReference(reference); auto clips = ModelDoc::LoadAnimationClips( Resolve(sourceReference).generic_string(), targetSkeleton, MakeIOSystem()); for (std::size_t index = 0; index < clips.size(); ++index) { auto& clip = clips[index]; clip.assetReference.path = sourceReference; clip.assetReference.subresource = "animation:" + std::to_string(index); } return clips; }