From c9553fb46053050bf2f78d2f8b6e0304eddfbbe3 Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sun, 20 Nov 2022 12:58:09 +0000 Subject: [PATCH] adapt the old loader interface to make use of packed database --- demo/demo/main.cpp | 2 +- demo/demo/models/viewer.cpp | 6 +- demo/demo/rollball.cpp | 4 +- demo/demo/topdown.cpp | 2 +- fggl/assets/CMakeLists.txt | 1 + fggl/assets/types.cpp | 50 +++++++++ fggl/audio/openal/audio.cpp | 11 +- fggl/audio/openal/module.cpp | 2 +- fggl/data/assimp/module.cpp | 144 +++++++++++++++++-------- fggl/entity/loader/loader.cpp | 12 +-- fggl/entity/module.cpp | 4 +- fggl/gfx/ogl4/meshes.cpp | 18 ++-- fggl/gfx/ogl4/models.cpp | 4 +- fggl/gfx/ogl4/module.cpp | 4 +- include/fggl/assets/loader.hpp | 118 +++++++++++++++----- include/fggl/assets/manager.hpp | 14 +-- include/fggl/assets/packed/adapter.hpp | 65 +++++++++-- include/fggl/assets/packed/direct.hpp | 17 --- include/fggl/assets/types.hpp | 51 ++++++++- include/fggl/audio/openal/audio.hpp | 4 +- include/fggl/entity/loader/loader.hpp | 8 +- include/fggl/gfx/ogl4/fallback.hpp | 8 +- include/fggl/gfx/ogl4/models.hpp | 4 +- include/fggl/mesh/mesh.hpp | 24 +++-- include/fggl/util/guid.hpp | 21 ++++ 25 files changed, 435 insertions(+), 163 deletions(-) create mode 100644 fggl/assets/types.cpp diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp index 109fd0e..fbb3a1a 100644 --- a/demo/demo/main.cpp +++ b/demo/demo/main.cpp @@ -108,7 +108,7 @@ int main(int argc, const char* argv[]) { { auto* assets = app.service<fggl::assets::AssetManager>(); auto* loader = app.service<fggl::assets::Loader>(); - loader->load("ui/click.ogg", fggl::audio::OGG_VORBIS, assets); + loader->load("ui/click.ogg", fggl::audio::ASSET_CLIP_SHORT, assets); } auto* windowing = app.service<fggl::display::WindowService>(); diff --git a/demo/demo/models/viewer.cpp b/demo/demo/models/viewer.cpp index e073c29..277cbae 100644 --- a/demo/demo/models/viewer.cpp +++ b/demo/demo/models/viewer.cpp @@ -38,7 +38,8 @@ namespace demo { manager.add<fggl::math::Transform>(model); auto& mesh = manager.add<fggl::mesh::StaticMultiMesh3D>(model); - auto* meshData = assets->get<fggl::mesh::MultiMesh3D>("backpack/backpack.obj"); + auto assetRef = fggl::assets::make_asset_id_rt("viewer", "backpack/backpack.obj" ); + auto* meshData = assets->get<fggl::mesh::MultiMesh3D>( assetRef ); if ( meshData == nullptr ) { fggl::debug::warning("loading model did not work!"); } else { @@ -113,7 +114,8 @@ namespace demo { auto *loader = owner().service<fggl::assets::Loader>(); auto *manager = owner().service<fggl::assets::AssetManager>(); - loader->load("backpack/backpack.obj", fggl::data::models::ASSIMP_MODEL, manager, "viewer"); + auto backPackRef = fggl::assets::make_asset_id_rt("viewer", "backpack/backpack.obj"); + loader->loadChain(backPackRef, manager); // create camera setup_camera(world()); diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp index c594b32..24aa3da 100644 --- a/demo/demo/rollball.cpp +++ b/demo/demo/rollball.cpp @@ -87,8 +87,8 @@ namespace demo { // asset loader auto* assetLoader = m_owner.service<fggl::assets::Loader>(); - assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET, entFactory); - assetLoader->load("rollball.yml", fggl::entity::SCENE, this); + assetLoader->load("rollball.yml", fggl::entity::ENTITY_PROTOTYPE, entFactory); + assetLoader->load("rollball.yml", fggl::entity::ENTITY_SCENE, this); // collectable callbacks /*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable); diff --git a/demo/demo/topdown.cpp b/demo/demo/topdown.cpp index 0ad73c8..8069a3c 100644 --- a/demo/demo/topdown.cpp +++ b/demo/demo/topdown.cpp @@ -132,7 +132,7 @@ void TopDown::activate() { fggl::debug::log(fggl::debug::Level::info, "TopDown::activate()"); auto* assetLoader = m_owner.service<fggl::assets::Loader>(); - assetLoader->load("topdown.yml", fggl::entity::PROTOTYPE_ASSET); + assetLoader->load("topdown.yml", fggl::entity::ENTITY_PROTOTYPE); auto* factory = m_owner.service<fggl::entity::EntityFactory>(); //fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml"); diff --git a/fggl/assets/CMakeLists.txt b/fggl/assets/CMakeLists.txt index d39a513..1ce3b6d 100644 --- a/fggl/assets/CMakeLists.txt +++ b/fggl/assets/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(fggl PRIVATE module.cpp + types.cpp packed/module.cpp ) \ No newline at end of file diff --git a/fggl/assets/types.cpp b/fggl/assets/types.cpp new file mode 100644 index 0000000..ef1a812 --- /dev/null +++ b/fggl/assets/types.cpp @@ -0,0 +1,50 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 20/11/22. +// + +#include "fggl/assets/types.hpp" + +namespace fggl::assets { + AssetID make_asset_id_rt(const std::string &pack, const std::string &path, const std::string &view) { + auto fullPath = pack + ":" + path; + if (!view.empty()) { + fullPath += "[" + view + "]"; + } + + #ifndef NDEBUG + util::internString(fullPath.c_str()); + #endif + + auto hash = util::hash_fnv1a_64(fullPath.c_str()); + return AssetID::make(hash); + } + + AssetID asset_from_user(const std::string &input, const std::string &pack) { + if (input.find(':') != 0) { + // probably fully qualified + #ifndef NDEBUG + util::internString(input.c_str()); + #endif + auto hash = util::hash_fnv1a_64(input.c_str()); + return AssetID::make(hash); + } + + // probably local + return make_asset_id_rt(pack, input); + } + +} \ No newline at end of file diff --git a/fggl/audio/openal/audio.cpp b/fggl/audio/openal/audio.cpp index d763a6f..3eb308b 100644 --- a/fggl/audio/openal/audio.cpp +++ b/fggl/audio/openal/audio.cpp @@ -35,13 +35,13 @@ namespace fggl::audio::openal { - assets::AssetRefRaw load_vorbis(assets::Loader* /*loader*/, const assets::AssetGUID &guid, const assets::AssetData &data, void* userPtr) { - auto *filePath = std::get<assets::AssetPath *>(data); + assets::AssetRefRaw load_vorbis(assets::Loader* /*loader*/, const assets::AssetID &guid, const assets::LoaderContext &data, void* userPtr) { auto *manager = static_cast<assets::AssetManager*>(userPtr); + auto filePath = data.assetPath; // vorbis auto* clip = new AudioClipShort(); - clip->sampleCount = stb_vorbis_decode_filename( filePath->c_str(), &clip->channels, &clip->sampleRate, &clip->data); + clip->sampleCount = stb_vorbis_decode_filename( filePath.c_str(), &clip->channels, &clip->sampleRate, &clip->data); debug::info("clip loaded: channels={}, sampleRate={}, sampleCount={}", clip->channels, clip->sampleRate, clip->sampleCount); if ( clip->sampleCount == -1 ) { @@ -65,7 +65,7 @@ namespace fggl::audio::openal { return false; } - assets::AssetTypeID check_vorbis(std::filesystem::path path) { + assets::AssetTypeID check_vorbis(std::filesystem::path path ) { if ( path.extension() != ".ogg" ) { return assets::INVALID_ASSET_TYPE; } @@ -96,7 +96,8 @@ namespace fggl::audio::openal { debug::info("beginning audio: {}", filename); // load audio clip into temp storage - auto* clip = m_assets->get<AudioClipShort>(filename); + auto assetRef = assets::make_asset_id_rt("core", filename); + auto* clip = m_assets->get<AudioClipShort>(assetRef); if ( clip == nullptr ) { debug::warning("audio asset requested, but not loaded: {}", filename); return; diff --git a/fggl/audio/openal/module.cpp b/fggl/audio/openal/module.cpp index 89aeb85..964d5c1 100644 --- a/fggl/audio/openal/module.cpp +++ b/fggl/audio/openal/module.cpp @@ -26,7 +26,7 @@ namespace fggl::audio { { auto *assetLoader = services.get<assets::Loader>(); - assetLoader->setFactory(OGG_VORBIS, openal::load_vorbis, assets::LoadType::PATH); + assetLoader->setFactory( ASSET_CLIP_SHORT, openal::load_vorbis, assets::LoadType::PATH); } { diff --git a/fggl/data/assimp/module.cpp b/fggl/data/assimp/module.cpp index 4ae1bb6..afa6b5f 100644 --- a/fggl/data/assimp/module.cpp +++ b/fggl/data/assimp/module.cpp @@ -41,7 +41,7 @@ namespace fggl::data::models { return {vec.x, vec.y}; } - static void process_mesh(mesh::Mesh3D& mesh, aiMesh* assimpMesh, const aiScene* scene) { + static void process_mesh(mesh::Mesh3D& mesh, aiMesh* assimpMesh, const aiScene* scene, const std::vector<assets::AssetID>& assets) { assert( assimpMesh != nullptr ); assert( scene != nullptr ); @@ -71,30 +71,28 @@ namespace fggl::data::models { // process material if ( assimpMesh->mMaterialIndex >= 0) { - mesh.material = "_MAT_" + std::to_string(assimpMesh->mMaterialIndex); + mesh.material = assets[assimpMesh->mMaterialIndex]; } } - static void process_node(mesh::MultiMesh3D& mesh, aiNode* node, const aiScene* scene) { + static void process_node(mesh::MultiMesh3D& mesh, aiNode* node, const aiScene* scene, const std::vector<assets::AssetID>& assets) { for ( auto idx = 0U; idx < node->mNumMeshes; ++idx ) { auto *assimpMesh = scene->mMeshes[ node->mMeshes[idx] ]; // process assimp submesh mesh::Mesh3D meshData; - process_mesh(meshData, assimpMesh, scene); + process_mesh(meshData, assimpMesh, scene, assets); mesh.meshes.push_back(meshData); } for ( auto idx = 0U; idx < node->mNumChildren; ++idx) { - process_node(mesh, node->mChildren[idx], scene); + process_node(mesh, node->mChildren[idx], scene, assets); } } - static void process_material(const assets::AssetGUID& guid, aiMaterial* assimpMat, assets::Loader* loader, assets::AssetManager* manager) { - auto lastSlash = guid.rfind('/'); - auto prefix = guid.substr(0, lastSlash); + static void process_material(const assets::AssetID& guid, aiMaterial* assimpMat, assets::Loader* loader, assets::AssetManager* manager, const std::filesystem::path& prefix, const std::string& pack) { mesh::Material* material = new mesh::Material(); // for each material, calculate what it's name would be then request it @@ -102,45 +100,60 @@ namespace fggl::data::models { aiString texName; assimpMat->GetTexture( aiTextureType_DIFFUSE, i, &texName ); - auto textureGuid = prefix + "/" + texName.C_Str(); - loader->load(textureGuid, DATA_TEXTURE2D, manager); + auto texID = assets::make_asset_id_rt(pack, prefix / texName.C_Str() ); + if ( !manager->has(texID) ) { + debug::info("triggered JIT upload for {}, use the chain loader", texID); + loader->load(texID, manager); + } - material->diffuseTextures.push_back(textureGuid); + material->diffuseTextures.push_back(texID); } for ( unsigned int i = 0U ; i < assimpMat->GetTextureCount(aiTextureType_NORMALS); ++i ) { aiString texName; assimpMat->GetTexture( aiTextureType_NORMALS, i, &texName ); - auto textureGuid = prefix + "/" + texName.C_Str(); - loader->load(textureGuid, DATA_TEXTURE2D, manager); + auto texID = assets::make_asset_id_rt(pack, prefix / texName.C_Str() ); + if ( !manager->has(texID) ) { + debug::info("triggered JIT upload for {}, use the chain loader", texID); + loader->load(texID, manager); + } - material->normalTextures.push_back(textureGuid); + material->normalTextures.push_back(texID); } for ( unsigned int i = 0U ; i < assimpMat->GetTextureCount(aiTextureType_SPECULAR); ++i ) { aiString texName; assimpMat->GetTexture( aiTextureType_SPECULAR, i, &texName ); - auto textureGuid = prefix + "/" + texName.C_Str(); - loader->load(textureGuid, DATA_TEXTURE2D, manager); + auto texID = assets::make_asset_id_rt(pack, prefix / texName.C_Str() ); + if ( !manager->has(texID) ) { + debug::info("triggered JIT upload for {}, use the chain loader", texID); + loader->load(texID, manager); + } - material->specularTextures.push_back(textureGuid); + material->specularTextures.push_back(texID); } manager->set( guid, material ); } - static assets::AssetRefRaw load_assimp_model(assets::Loader* loader, const assets::AssetGUID& guid, const assets::AssetData& data, void* userPtr) { - auto *filePath = std::get<assets::AssetPath *>(data); + static assets::AssetRefRaw load_assimp_model(assets::Loader* loader, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr) { +// auto *filePath = std::get<assets::AssetPath *>(data); assets::AssetManager* manager = (assets::AssetManager*)userPtr; + auto filePath = data.assetPath; + + if ( manager->has(guid) ) { + // asset in DB, what do you want me to do? + return nullptr; + } // assimp stuff Assimp::Importer importer; // import the scene from disk - const aiScene *scene = importer.ReadFile(filePath->c_str(), aiProcess_Triangulate | aiProcess_FlipUVs); + const aiScene *scene = importer.ReadFile(filePath.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs); if ( !scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode ) { debug::warning("unable to load required model asset"); return nullptr; @@ -148,25 +161,29 @@ namespace fggl::data::models { debug::debug("Processing assimp mesh, {} meshes, {} materials, {} textures", scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures); + // calculate the mapping from material => asset mappings + auto parentRel = std::filesystem::relative( filePath.parent_path(), filePath.parent_path().parent_path() ); + std::vector<assets::AssetID> matAssets; + for ( uint64_t idx = 0; idx < scene->mNumMaterials; ++idx ) { + + // FIXME to calculate this properly we need to know the pack name, root and relative path + aiString matName = scene->mMaterials[idx]->GetName(); + auto matRel = parentRel / matName.C_Str(); + auto matIDMinusPack = assets::make_asset_id_rt( data.pack, matRel); + + matAssets.push_back(matIDMinusPack); + } + // now we can try importing the mesh data mesh::MultiMesh3D* packedMesh = new mesh::MultiMesh3D(); - process_node( *packedMesh, scene->mRootNode, scene); - - std::map<std::string, std::string> materials; - { - // prefix the materials if required - for (auto& mesh : packedMesh->meshes) { - if (!mesh.material.empty()) { - mesh.material = guid + mesh.material; - } - } + process_node( *packedMesh, scene->mRootNode, scene, matAssets); - // try and decode the material data - for (auto idx = 0; idx < scene->mNumMaterials; ++idx) { - auto matName = guid +"_MAT_" + std::to_string(idx); - process_material(matName, scene->mMaterials[idx], loader, manager); - packedMesh->materials.push_back(matName); - } + // now we try importing the materials + for (auto idx = 0u; idx < scene->mNumMaterials; ++idx ) { + auto *mat = scene->mMaterials[idx]; + auto matID = matAssets[idx]; + + process_material(matID, mat, loader, manager, parentRel, data.pack); } // FIXME asset loading system needs rework, this is bonkers. @@ -174,15 +191,20 @@ namespace fggl::data::models { return nullptr; } - static assets::AssetRefRaw load_assimp_texture(assets::Loader* loader, const assets::AssetGUID& guid, const assets::AssetData& data, void* userPtr) { - auto *filePath = std::get<assets::AssetPath *>(data); + static assets::AssetRefRaw load_assimp_texture(assets::Loader* loader, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr) { assets::AssetManager* manager = (assets::AssetManager*)userPtr; + if ( manager->has(guid) ) { + // already loaded. + return nullptr; + } + + auto filePath = data.assetPath; stbi_set_flip_vertically_on_load(true); //load the texture data into memory data::Texture2D* image = new data::Texture2D(); - image->data = stbi_load(filePath->c_str(), &image->size.x, &image->size.y, &image->channels, 3); + image->data = stbi_load(filePath.c_str(), &image->size.x, &image->size.y, &image->channels, 3); if ( image->data == nullptr ) { debug::warning("error reading texture: {}", stbi_failure_reason()); @@ -230,10 +252,6 @@ namespace fggl::data::models { return assets::INVALID_ASSET_TYPE; } - static bool load_model_assimp(std::filesystem::path filePath, assets::MemoryBlock& block) { - return false; //TODO - } - static assets::AssetTypeID is_model_assimp(std::filesystem::path filePath) { if ( filePath.extension() == ".obj" ){ return MODEL_MULTI3D; @@ -242,16 +260,50 @@ namespace fggl::data::models { return assets::INVALID_ASSET_TYPE; } + static bool extract_requirements(const std::string& packName, std::filesystem::path packRoot, assets::ResourceRecord& rr) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( rr.m_path, aiProcess_Triangulate | aiProcess_FlipUVs); + if ( !scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode ) { + debug::warning("unable to load required model asset"); + return false; + } + + // we want to find out about dependencies + auto baseDir = std::filesystem::relative( rr.m_path.parent_path(), packRoot ); + for ( auto idx = 0U; idx < scene->mNumMaterials; ++idx) { + const auto *mat = scene->mMaterials[idx]; + + std::array matTypes { aiTextureType_DIFFUSE, aiTextureType_NORMALS, aiTextureType_SPECULAR }; + for ( auto& matType : matTypes ) { + const auto diffCount = mat->GetTextureCount(matType); + for (auto idx2 = 0U; idx2 < diffCount; ++idx2) { + aiString texName; + mat->GetTexture(matType, idx2, &texName); + + auto assetPath = baseDir / texName.C_Str(); + auto assetRequired = assets::make_asset_id_rt(packName, assetPath); + rr.m_requires.push_back(assetRequired); + } + } + + } + + return true; + } + bool AssimpModule::factory(modules::ModuleService service, modules::Services &serviceManager) { if ( service == MODEL_PROVIDER ) { auto* assetLoader = serviceManager.get<assets::Loader>(); - assetLoader->setFactory( ASSIMP_MODEL, load_assimp_model, assets::LoadType::PATH ); - assetLoader->setFactory( DATA_TEXTURE2D, load_assimp_texture, assets::LoadType::PATH ); + assetLoader->setFactory( MODEL_MULTI3D, load_assimp_model, assets::LoadType::PATH ); + assetLoader->setFactory( TEXTURE_RGBA, load_assimp_texture, assets::LoadType::PATH ); + // new loading system auto* checkin = serviceManager.get<assets::CheckinAdapted>(); checkin->setLoader( MIME_JPG, load_tex_stb, is_tex_stb ); checkin->setLoader( MIME_PNG, load_tex_stb, is_tex_stb ); - checkin->setLoader( MIME_OBJ, load_model_assimp, is_model_assimp ); + + checkin->setLoader( MIME_OBJ, assets::NEEDS_CHECKIN, is_model_assimp ); + checkin->setProcessor( MIME_OBJ, extract_requirements); } } diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp index ba19af3..076aa57 100644 --- a/fggl/entity/loader/loader.cpp +++ b/fggl/entity/loader/loader.cpp @@ -22,14 +22,14 @@ namespace fggl::entity { - assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetGUID& asset, assets::AssetData data, void* ptr) { - auto *filePath = std::get<assets::AssetPath *>(data); + assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetID& asset, const assets::LoaderContext& data, void* ptr) { scenes::Game* gamePtr = (scenes::Game*)ptr; + auto filePath = data.assetPath; // load assets auto* entityFactory = gamePtr->owner().service<EntityFactory>(); - auto nodes = YAML::LoadAllFromFile(filePath->c_str()); + auto nodes = YAML::LoadAllFromFile(filePath.c_str()); for (const auto& node : nodes) { auto scene = node["scene"]; if ( !scene ) { @@ -77,11 +77,11 @@ namespace fggl::entity { return nullptr; } - assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetGUID &guid, assets::AssetData data, EntityFactory* factory) { - auto *filePath = std::get<assets::AssetPath *>(data); + assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetID &guid, const assets::LoaderContext& data, EntityFactory* factory) { + auto filePath = data.assetPath; // We need to process the prototypes, and load them into the asset system. - auto nodes = YAML::LoadAllFromFile(filePath->c_str()); + auto nodes = YAML::LoadAllFromFile(filePath.c_str()); for (const auto &node : nodes) { auto prefabs = node["prefabs"]; diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp index 22cf0a0..5ebda82 100644 --- a/fggl/entity/module.cpp +++ b/fggl/entity/module.cpp @@ -44,14 +44,14 @@ namespace fggl::entity { // we are responsible for prefabs... auto *assetLoader = services.get<assets::Loader>(); - assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b, void* ptr) { + assetLoader->setFactory(ENTITY_PROTOTYPE, [factory](assets::Loader* loader, const assets::AssetID& a, assets::LoaderContext b, void* ptr) { EntityFactory* facPtr = factory; if ( ptr != nullptr ) { facPtr = (EntityFactory*)ptr; } return load_prototype(loader, a, b, facPtr); }, assets::LoadType::PATH); - assetLoader->setFactory(SCENE, load_scene, assets::LoadType::PATH); + assetLoader->setFactory(ENTITY_SCENE, load_scene, assets::LoadType::PATH); return true; } diff --git a/fggl/gfx/ogl4/meshes.cpp b/fggl/gfx/ogl4/meshes.cpp index 961c4f6..34f757b 100644 --- a/fggl/gfx/ogl4/meshes.cpp +++ b/fggl/gfx/ogl4/meshes.cpp @@ -158,10 +158,11 @@ namespace fggl::gfx::ogl4 { shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f}); } - static ogl::Texture* upload_texture( std::string name, assets::AssetManager* manager ) { - debug::info("loading texture: {}", name); + static ogl::Texture* upload_texture( assets::AssetID name, assets::AssetManager* manager ) { + debug::info("loading texture: {}", name.get()); - auto* texture = manager->get<ogl::Texture>("ogl_"+name); + auto uploadedTex = assets::make_asset_id_rt("ogl", std::to_string(name.get()) ); + auto* texture = manager->get<ogl::Texture>( uploadedTex ); if ( texture != nullptr ) { return texture; } @@ -177,7 +178,7 @@ namespace fggl::gfx::ogl4 { texture = new ogl::Texture(ogl::TextureType::Tex2D); texture->setData( ogl::InternalImageFormat::RedGreenBlue, image); - return manager->set("ogl_"+name, texture); + return manager->set(uploadedTex, texture); } static Material* get_fallback_material(assets::AssetManager* manager) { @@ -193,14 +194,15 @@ namespace fggl::gfx::ogl4 { return manager->set(FALLBACK_MAT, mat); } - static Material* upload_material( std::string name, assets::AssetManager* manager ) { + static Material* upload_material( assets::AssetID name, assets::AssetManager* manager ) { auto* meshMaterial = manager->get<mesh::Material>(name); if ( meshMaterial == nullptr ){ - debug::error("attempted to load material {}, but did not exist!", name); + debug::error("attempted to load material {}, but did not exist!", name.get()); return get_fallback_material(manager); } - auto* material = manager->get<Material>("ogl_"+name); + auto uploadedMat = assets::make_asset_id_rt("ogl", std::to_string(name.get()) ); + auto* material = manager->get<Material>( uploadedMat ); if ( material != nullptr ) { return material; } @@ -216,7 +218,7 @@ namespace fggl::gfx::ogl4 { material->m_specular = upload_texture(meshMaterial->getPrimarySpecular(), manager); } - return manager->set("ogl_"+name, material); + return manager->set( uploadedMat, material); } MeshData upload_mesh(const mesh::Mesh3D& rawMesh, assets::AssetManager* manager) { diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp index 5eee6ce..ff778b8 100644 --- a/fggl/gfx/ogl4/models.cpp +++ b/fggl/gfx/ogl4/models.cpp @@ -67,7 +67,7 @@ namespace fggl::gfx::ogl4 { modelComp.drawType = ogl::Primitive::TRIANGLE; } - StaticModel* StaticModelRenderer::uploadMesh(assets::AssetGUID guid, const data::Mesh &mesh, bool allowCache) { + StaticModel* StaticModelRenderer::uploadMesh(assets::AssetID guid, const data::Mesh &mesh, bool allowCache) { assert( m_assets != nullptr ); // if the asset has already been uploaded, we don't need to do anything @@ -117,7 +117,7 @@ namespace fggl::gfx::ogl4 { return modelAsset; }*/ - StaticModelGPU* StaticModelRenderer::uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh &mesh) { + StaticModelGPU* StaticModelRenderer::uploadMesh2(const assets::AssetID& meshName, const data::Mesh &mesh) { assert( m_assets != nullptr ); if ( m_assets->has(meshName) ) { diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp index a3c0d4e..534b016 100644 --- a/fggl/gfx/ogl4/module.cpp +++ b/fggl/gfx/ogl4/module.cpp @@ -125,7 +125,9 @@ namespace fggl::gfx { if ( spec.has("model") ) { // figure out what model we want auto assetStr = spec.get<std::string>("model", ""); - auto* asset = assetService->get<ogl4::StaticModel>(assetStr); + auto assetId = assets::asset_from_user( assetStr ); + + auto* asset = assetService->get<ogl4::StaticModel>(assetId); if ( asset == nullptr ) { // the asset is not loaded/does not exist diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp index 0bc18d9..b07a9a6 100644 --- a/include/fggl/assets/loader.hpp +++ b/include/fggl/assets/loader.hpp @@ -40,9 +40,10 @@ namespace fggl::assets { struct ResourceRequest { AssetGUID m_guid; - AssetType m_type; + AssetTypeID m_type; }; + class Loader { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::Loader"); @@ -57,50 +58,111 @@ namespace fggl::assets { Loader(Loader &&) = delete; Loader &operator=(Loader &&) = delete; - inline void setFactory(AssetType type, Checkin fn, LoadType loading = LoadType::DIRECT) { + inline void setFactory(AssetTypeID type, Checkin fn, LoadType loading = LoadType::DIRECT) { m_factories[type] = std::make_pair(fn, loading); } - inline void unsetFactory(AssetType type) { + inline void unsetFactory(AssetTypeID type) { m_factories.erase(type); } - inline void request(const AssetGUID &guid, const AssetType &type) { + inline void request(const AssetGUID &guid, const AssetTypeID &type) { m_requests.push(ResourceRequest{guid, type}); } - void load(const AssetGUID &guid, const AssetType &type, void* userPtr = nullptr, const std::string& pack = "core") { + void loadChain(const AssetGUID &guid, const std::string& pack = "core") { + loadChain( assets::make_asset_id_rt(pack, guid) ); + } - auto assetID = assets::make_asset_id(pack, guid); - std::filesystem::path path{}; + void loadChain(AssetID asset, void* userPtr = nullptr) { + if (!m_checkin->exists(asset)) { + debug::warning("attempted to chain load unknown assetID {}", asset); + return; + } - // integrate the asset search routine, falling back to path scanning if needed - if ( m_checkin->exists(assetID) ) { - path = m_checkin->getPath(assetID); - } else { - path = m_storage->resolvePath(data::StorageType::Data, guid); + // FIXME use the fancy dependency resolution algorithm from the module system + std::vector<AssetID> loadOrder; + std::stack<AssetID> openSet; + openSet.push(asset); + while ( !openSet.empty() ) { + auto current = openSet.top(); + openSet.pop(); + + // this WILL (probably) break if an asset ends up in the chain twice + loadOrder.push_back( current ); + const auto& record = m_checkin->find(current); + for ( const auto& dep : record.m_requires ) { + openSet.push( dep ); + } } - auto factoryItr = m_factories.find(type); - if ( factoryItr == m_factories.end() ) { - debug::error("attempted to load asset with unknown type: {}", type.get()); + processChain(loadOrder, userPtr); + } + + void processChain( std::vector<AssetID>& loadOrder, void* userPtr = nullptr ) { + if ( loadOrder.empty() ) { return; } + debug::info("Starting chain load"); + for ( auto it = loadOrder.rbegin(); it < loadOrder.rend(); ++it ) { + debug::info(" CHAIN -> loading {}", *it ); + load( *it, userPtr ); + } + debug::info("Ended chain loader"); + } + + inline void load(const AssetGUID& guid, const AssetTypeID& type, void* userPtr = nullptr, const std::string& pack = "core" ) { + auto assetID = assets::make_asset_id_rt(pack, guid); + + // try checkin load first, falling back if it fails + bool checkinLoad = load( assetID, userPtr ); + if ( !checkinLoad ) { + debug::info("could not perform checkin load for {} - missing loaders?", guid); + + auto path = m_storage->resolvePath(data::StorageType::Data, guid); + loadDirect(path, assetID, type, pack, userPtr); + } + } + + bool loadDirect( const std::filesystem::path& path, AssetID asset, AssetTypeID assetType, const std::string& pack, void* userPtr = nullptr ){ + // check if we know how to load this asset (using old loaders) + auto factoryItr = m_factories.find( assetType ); + if ( factoryItr == m_factories.end() ) { + debug::error("attempted to load asset with unknown type: {}", assetType.get() ); + return false; + } + + // perform loading + LoaderContext ctx { + .pack = pack, + .packRoot = "", + .assetPath = path + }; + + // perform loading const auto& [callback, callbackType] = factoryItr->second; - switch (callbackType) { - case LoadType::DIRECT: - // TODO we load the data into main memory and give a pointer to it. - debug::log(debug::Level::error, "Tried to load direct asset - no one wrote that yet!"); - break; - case LoadType::STAGED: - // TODO we load the data into temp memory and give a pointer to it. - debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!"); - break; - case LoadType::PATH: - callback(this, guid, AssetData(&path), userPtr); - break; + if ( callbackType == LoadType::PATH ) { + callback(this, asset, ctx, userPtr); + return true; + } + + debug::log(debug::Level::error, "Tried to use old/unsupported loading method"); + return false; + } + + bool load(const AssetID &assetId, void* userPtr = nullptr) { + if ( !m_checkin->exists(assetId) ) { + #ifndef NDEBUG + debug::warning("asked to load unknown asset: {}", util::guidToString( util::GUID::make(assetId.get()) ) ); + #else + debug::warning("asked to load unknown asset: {}", assetId.get()); + #endif + return false; } + + const auto& record = m_checkin->find( assetId ); + return loadDirect( record.m_path, assetId, record.m_assetType, record.m_pack, userPtr ); } void progress() { @@ -132,7 +194,7 @@ namespace fggl::assets { data::Storage *m_storage; CheckinAdapted *m_checkin; std::queue<ResourceRequest> m_requests; - std::map<AssetType, Config> m_factories; + std::map<AssetTypeID, Config> m_factories; }; } // namespace fggl::assets diff --git a/include/fggl/assets/manager.hpp b/include/fggl/assets/manager.hpp index 99a06ca..125c153 100644 --- a/include/fggl/assets/manager.hpp +++ b/include/fggl/assets/manager.hpp @@ -60,7 +60,7 @@ namespace fggl::assets { AssetManager &operator=(AssetManager &&) = delete; template<typename T> - T* get(const AssetGUID &guid) const { + T* get(const AssetID &guid) const { try { const auto &assetRecord = m_registry.at(guid); std::shared_ptr<AssetBoxT<T>> casted = std::dynamic_pointer_cast<AssetBoxT<T>>(assetRecord); @@ -75,7 +75,7 @@ namespace fggl::assets { } template<typename T> - T* set(const AssetGUID &guid, T* assetRef) { + T* set(const AssetID &guid, T* assetRef) { auto ptr = std::make_shared<AssetBoxT<T>>(); ptr->asset = assetRef; @@ -83,11 +83,11 @@ namespace fggl::assets { return (*ptr).asset; } - inline void require(const AssetGUID &guid) { + inline void require(const AssetID &guid) { //m_registry.at(guid).refCount++; } - inline bool has(const AssetGUID &guid) { + inline bool has(const AssetID &guid) { return m_registry.contains(guid); } @@ -95,8 +95,8 @@ namespace fggl::assets { //m_registry.at(guid).refCount--; } - inline std::vector<AssetGUID> known() { - std::vector<AssetGUID> assetList; + inline std::vector<AssetID> known() { + std::vector<AssetID> assetList; for ( auto& itr : m_registry ) { assetList.push_back(itr.first); } @@ -104,7 +104,7 @@ namespace fggl::assets { } private: - std::map<AssetGUID, std::shared_ptr<AssetBox>> m_registry; + std::map<AssetID, std::shared_ptr<AssetBox>> m_registry; }; } // namespace fggl::assets diff --git a/include/fggl/assets/packed/adapter.hpp b/include/fggl/assets/packed/adapter.hpp index fd787ba..17fb107 100644 --- a/include/fggl/assets/packed/adapter.hpp +++ b/include/fggl/assets/packed/adapter.hpp @@ -38,6 +38,8 @@ namespace fggl::assets { AssetID assetID; ResourceType m_fileType; AssetTypeID m_assetType; + std::string m_pack; + std::vector<AssetID> m_requires; }; struct ManifestHeader { @@ -63,8 +65,10 @@ namespace fggl::assets { class CheckinAdapted { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::checkin::debug"); + using FilePredicate = std::function<AssetTypeID(std::filesystem::path)>; using FileLoader = std::function<bool(std::filesystem::path, MemoryBlock& block)>; + using AssetMetadata = std::function<bool(const std::string& pack, std::filesystem::path packRoot, ResourceRecord&)>; CheckinAdapted(data::Storage* storage, RawCheckin* checkSvc) : m_storage(storage), m_checkin(checkSvc) {}; @@ -99,9 +103,13 @@ namespace fggl::assets { // asset discovery void discover( std::filesystem::path& packDir, bool useCache=true, bool updateCache=false ) { + // note we're loading this pack + auto packName = packDir.filename(); + m_packs[packName].rootDir = packDir; + if ( useCache && has_manifest(packDir)) { // check if we've cached the search - load_manifest(packDir.filename(), packDir); + load_manifest(packName, packDir); return; } @@ -126,7 +134,7 @@ namespace fggl::assets { if ( updateCache ) { save_manifest(packDir.filename(), packDir, packFiles); } - m_packs[ packDir.filename() ] = packFiles; + m_packs[ packDir.filename() ].assets = packFiles; } inline void setLoader(ResourceType type, FileLoader loader, FilePredicate predicate = nullptr) { @@ -138,14 +146,28 @@ namespace fggl::assets { } } + inline void setProcessor(ResourceType type, AssetMetadata metaFunc) { + m_metadata[type] = metaFunc; + } + + inline const ResourceRecord& find(AssetID asset) const { + return m_files.at(asset); + } + private: data::Storage* m_storage; RawCheckin* m_checkin; std::map<AssetID, ResourceRecord> m_files; + struct PackInfo { + std::filesystem::path rootDir; + std::vector<AssetID> assets; + }; + std::map<ResourceType, FilePredicate> m_predicates; std::map<ResourceType, FileLoader> m_loaders; - std::map<std::string, std::vector<AssetID>> m_packs; + std::map<ResourceType, AssetMetadata> m_metadata; + std::map<std::string, PackInfo> m_packs; void process_file(std::filesystem::path path, std::filesystem::path packDir, std::vector<AssetID> packFiles) { for( auto& [rType, pred] : m_predicates ) { @@ -157,13 +179,23 @@ namespace fggl::assets { auto relPath = std::filesystem::relative(path, packDir); ResourceRecord rr{ .m_path = path, - .assetID = make_asset_id(packName, relPath.generic_string()), + .assetID = make_asset_id_rt(packName, relPath.generic_string()), .m_fileType = rType, .m_assetType = aType, + .m_pack = packName, + .m_requires = {} }; + + // processors (for stuff like dependencies) + auto metaProc = m_metadata.find(rType); + if ( metaProc != m_metadata.end() ) { + metaProc->second( packName, packDir, rr ); + } + + // store the resulting data m_files[rr.assetID] = rr; packFiles.push_back( rr.assetID ); - debug::trace("discovered {} ({}) from pack '{}'", rr.assetID.get(), relPath.c_str(), packName.c_str() ); + debug::trace("discovered {} ({}) from pack '{}'", rr.assetID, relPath.c_str(), packName.c_str() ); break; } } @@ -174,7 +206,7 @@ namespace fggl::assets { return std::filesystem::exists(packManifest); } - void load_manifest_entry(FILE* file, const std::filesystem::path& packRoot) { + void load_manifest_entry(FILE* file, const std::string& packName, const std::filesystem::path& packRoot) { // read our entry ( id, ftype, atype, pathLen ) ManifestHeader header{}; std::fread(&header, sizeof(ManifestHeader), 1, file); @@ -184,12 +216,23 @@ namespace fggl::assets { std::fread( relPath, sizeof(char), header.stringSize, file ); relPath[ header.stringSize + 1 ] = '\0'; + // read the dependency list + uint64_t nDeps = 0; + std::fread( &nDeps, sizeof(uint64_t), 1, file); + uint64_t *deps = new uint64_t[nDeps]; + std::fread( deps, sizeof(uint64_t), nDeps, file ); + + std::vector<AssetID> depList; + for ( uint64_t i = 0; i < nDeps; ++i ) { + depList.push_back( AssetID::make(deps[i]) ); + } + // calculate and verify path std::filesystem::path fullPath = packRoot / relPath; delete[] relPath; if ( !std::filesystem::exists(fullPath) ) { - debug::warning("pack manifest for {} contained invalid path {}", packRoot.filename().c_str(), fullPath.c_str()); + debug::warning("pack manifest for {} contained invalid path {}", packName, fullPath.c_str()); return; } @@ -198,10 +241,12 @@ namespace fggl::assets { .m_path = fullPath, .assetID = AssetID::make(header.assetID), .m_fileType = ResourceType::make(header.fileType), - .m_assetType = AssetTypeID::make(header.assetType) + .m_assetType = AssetTypeID::make(header.assetType), + .m_pack = packName, + .m_requires = depList }; m_files[ rr.assetID ] = rr; - m_packs[ packRoot.filename() ].push_back( rr.assetID ); + m_packs[ packRoot.filename() ].assets.push_back( rr.assetID ); debug::trace("discovered {} ({}) from pack {}", rr.assetID.get(), fullPath.c_str(), packRoot.filename().c_str() ); } @@ -222,7 +267,7 @@ namespace fggl::assets { uint64_t entries{0}; std::fread(&entries, sizeof(uint64_t), 1, file); for ( uint64_t i = 0; i < entries; ++i) { - load_manifest_entry(file, packRoot); + load_manifest_entry(file, packName, packRoot); } std::fclose( file ); diff --git a/include/fggl/assets/packed/direct.hpp b/include/fggl/assets/packed/direct.hpp index faa01ae..f10796d 100644 --- a/include/fggl/assets/packed/direct.hpp +++ b/include/fggl/assets/packed/direct.hpp @@ -35,23 +35,6 @@ */ namespace fggl::assets { - using AssetID = util::OpaqueName<uint64_t, struct AssetIDTag>; - - constexpr AssetID make_asset_id(const std::string& pack, const std::string& path, const std::string& view = "") { - auto ref = pack + ":" + path; - if ( !view.empty() ) { - ref += "[" + view + "]"; - } - return AssetID::make( util::hash_fnv1a_64(ref.c_str()) ); - } - - using AssetTypeID = util::OpaqueName<uint64_t, struct AssetTypeTag>; - constexpr auto INVALID_ASSET_TYPE = AssetTypeID::make(0); - - constexpr AssetTypeID make_asset_type(const char* type) { - return AssetTypeID::make( util::hash_fnv1a_64(type) ); - } - class RawCheckin { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::checkin"); diff --git a/include/fggl/assets/types.hpp b/include/fggl/assets/types.hpp index 4abcdc7..8f507b4 100644 --- a/include/fggl/assets/types.hpp +++ b/include/fggl/assets/types.hpp @@ -24,12 +24,38 @@ #include <functional> #include "fggl/util/safety.hpp" +#include "fggl/util/guid.hpp" namespace fggl::assets { using AssetType = util::OpaqueName<std::string_view, struct AssetTag>; using AssetGUID = std::string; using AssetPath = std::filesystem::path; + using AssetID = util::OpaqueName<uint64_t, struct AssetIDTag>; + + template<unsigned L1, unsigned L2> + constexpr AssetID make_asset_id(const char (&pack)[L1], const char (&path)[L2]) { + auto hash = util::hash_fnv1a_64( util::cat( pack, ":", path ).c ); + return AssetID::make( hash ); + } + + template<unsigned L1, unsigned L2, unsigned L3> + constexpr AssetID make_asset_id(const char (&pack)[L1], const char (&path)[L2], const char (&view)[L3]) { + auto hash = util::hash_fnv1a_64( util::cat( pack, ":", path, "[", view, "]").c ); + return AssetID::make( hash ); + } + + AssetID make_asset_id_rt(const std::string &pack, const std::string &path, const std::string &view = ""); + AssetID asset_from_user(const std::string &input, const std::string &pack = "core"); + + using AssetTypeID = util::OpaqueName<uint64_t, struct AssetTypeTag>; + constexpr auto INVALID_ASSET_TYPE = AssetTypeID::make(0); + + constexpr AssetTypeID make_asset_type(const char* type) { + return AssetTypeID::make( util::hash_fnv1a_64(type) ); + } + + struct MemoryBlock { std::byte *data; std::size_t size; @@ -43,9 +69,30 @@ namespace fggl::assets { using AssetRefRaw = std::shared_ptr<void>; + struct LoaderContext { + std::string pack; + std::filesystem::path packRoot; + std::filesystem::path assetPath; + }; + class Loader; - using AssetData = std::variant<MemoryBlock, AssetPath *, FILE *>; - using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetGUID &, const AssetData &, void* userPtr)>; + using Checkin = std::function<AssetRefRaw(Loader* loader, const AssetID &, const LoaderContext &, void* userPtr)>; } +// formatter +template<> struct fmt::formatter<fggl::assets::AssetID> { + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template <typename FormatContext> + auto format(const fggl::assets::AssetID & guid, FormatContext& ctx) const -> decltype(ctx.out()) { + #ifndef NDEBUG + return fmt::format_to(ctx.out(), "ASSET[{}]", guidToString( fggl::util::GUID::make( guid.get() ) )); + #else + return fmt::format_to(ctx.out(), "ASSET[{}]", guid.get()); + #endif + } +}; + #endif //FGGL_ASSETS_TYPES_HPP diff --git a/include/fggl/audio/openal/audio.hpp b/include/fggl/audio/openal/audio.hpp index cf59cab..ee74dcc 100644 --- a/include/fggl/audio/openal/audio.hpp +++ b/include/fggl/audio/openal/audio.hpp @@ -36,10 +36,10 @@ namespace fggl::audio::openal { constexpr uint32_t NULL_BUFFER_ID = 0; - assets::AssetRefRaw load_vorbis(assets::Loader* loader, const assets::AssetGUID &, const assets::AssetData &, void* userPtr); + assets::AssetRefRaw load_vorbis(assets::Loader* loader, const assets::AssetID &, const assets::LoaderContext &, void* userPtr); bool load_vorbis_short(std::filesystem::path path, assets::MemoryBlock& block); - assets::AssetTypeID check_vorbis(std::filesystem::path path); + assets::AssetTypeID check_vorbis( std::filesystem::path path ); enum class AudioType { MONO_8 = AL_FORMAT_MONO8, diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index 322b76c..86e6d4a 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -31,8 +31,8 @@ namespace fggl::entity { - constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype"); - constexpr auto SCENE = assets::AssetType::make("entity_scene"); + constexpr auto ENTITY_PROTOTYPE = assets::make_asset_type("entity/prototype"); + constexpr auto ENTITY_SCENE = assets::make_asset_type("entity/scene"); using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>; using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>; @@ -173,8 +173,8 @@ namespace fggl::entity { } }; - assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetGUID &guid, assets::AssetData data, EntityFactory* factory); - assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetGUID& asset, assets::AssetData data, void* ptr); + assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetID &guid, const assets::LoaderContext& data, EntityFactory* factory); + assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetID& asset, const assets::LoaderContext& data, void* ptr); } // namespace fggl::entity diff --git a/include/fggl/gfx/ogl4/fallback.hpp b/include/fggl/gfx/ogl4/fallback.hpp index 4efd49b..62d2260 100644 --- a/include/fggl/gfx/ogl4/fallback.hpp +++ b/include/fggl/gfx/ogl4/fallback.hpp @@ -19,6 +19,8 @@ #ifndef FGGL_GFX_OGL4_FALLBACK_HPP #define FGGL_GFX_OGL4_FALLBACK_HPP +#include "fggl/assets/types.hpp" + /** * Fallback shaders. * @@ -61,9 +63,9 @@ namespace fggl::gfx::ogl4 { constexpr const GLuint TEX_WHITE = 0xFFFFFFFF; constexpr const GLuint TEX_CHECKER = 0x00FF00FF; //FIXME pixel order is reversed?! - constexpr const char* FALLBACK_TEX = "FALLBACK_TEX"; - constexpr const char* FALLBACK_MAT = "FALLBACK_MAT"; - constexpr const char* SOLID_TEX = "SOLID_TEX"; + constexpr const assets::AssetID FALLBACK_TEX = assets::make_asset_id("fallback", "FALLBACK_TEX"); + constexpr const assets::AssetID FALLBACK_MAT = assets::make_asset_id("fallback", "FALLBACK_MAT"); + constexpr const assets::AssetID SOLID_TEX = assets::make_asset_id("fallback", "SOLID_TEX"); } // namespace fggl::gfx::ogl4 diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp index 5b8fba6..fd121d3 100644 --- a/include/fggl/gfx/ogl4/models.hpp +++ b/include/fggl/gfx/ogl4/models.hpp @@ -83,8 +83,8 @@ namespace fggl::gfx::ogl4 { StaticModelRenderer &operator=(const StaticModelRenderer &other) = delete; StaticModelRenderer &operator=(StaticModelRenderer &&other) = delete; - StaticModel* uploadMesh(assets::AssetGUID guid, const data::Mesh& mesh, bool allowCache=true); - StaticModelGPU* uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh& mesh); + StaticModel* uploadMesh(assets::AssetID guid, const data::Mesh& mesh, bool allowCache=true); + StaticModelGPU* uploadMesh2(const assets::AssetID& meshName, const data::Mesh& mesh); void render(entity::EntityManager &world, bool debugMode = false) { #ifdef FGGL_ALLOW_DEFERRED_UPLOAD diff --git a/include/fggl/mesh/mesh.hpp b/include/fggl/mesh/mesh.hpp index 1da8896..e05e5aa 100644 --- a/include/fggl/mesh/mesh.hpp +++ b/include/fggl/mesh/mesh.hpp @@ -20,6 +20,7 @@ #define FGGL_MESH_MESH_HPP #include "fggl/math/types.hpp" +#include "fggl/assets/types.hpp" #include <vector> #include <span> @@ -52,28 +53,29 @@ namespace fggl::mesh { DIFFUSE, NORMAL }; + constexpr auto MISSING_TEXTURE = assets::AssetID::make(0); struct Material { std::string name; math::vec3 ambient; math::vec3 diffuse; math::vec3 specular; - std::vector<std::string> diffuseTextures{}; - std::vector<std::string> normalTextures{}; - std::vector<std::string> specularTextures{}; + std::vector<assets::AssetID> diffuseTextures{}; + std::vector<assets::AssetID> normalTextures{}; + std::vector<assets::AssetID> specularTextures{}; - inline std::string getPrimaryDiffuse() { + inline assets::AssetID getPrimaryDiffuse() { assert( !diffuseTextures.empty() ); - return diffuseTextures.empty() ? "" : diffuseTextures[0]; + return diffuseTextures.empty() ? MISSING_TEXTURE : diffuseTextures[0]; } - inline std::string getPrimaryNormals() { + inline assets::AssetID getPrimaryNormals() { assert( !normalTextures.empty() ); - return normalTextures.empty() ? "" : normalTextures[0]; + return normalTextures.empty() ? MISSING_TEXTURE : normalTextures[0]; } - inline std::string getPrimarySpecular() { + inline assets::AssetID getPrimarySpecular() { assert( !specularTextures.empty() ); - return specularTextures.empty() ? "" : specularTextures[0]; + return specularTextures.empty() ? MISSING_TEXTURE : specularTextures[0]; } }; @@ -81,7 +83,7 @@ namespace fggl::mesh { struct Mesh { std::vector<VertexFormat> data; std::vector<uint32_t> indices; - std::string material; + assets::AssetID material; inline uint32_t append(const VertexFormat& vert) { auto nextIdx = data.size(); @@ -93,7 +95,7 @@ namespace fggl::mesh { template<typename MeshFormat> struct MultiMesh { std::vector<MeshFormat> meshes; - std::vector<std::string> materials; + std::vector<assets::AssetID> materials; MeshFormat& generate() { return meshes.template emplace_back(); diff --git a/include/fggl/util/guid.hpp b/include/fggl/util/guid.hpp index 7b188c7..6e9eb27 100644 --- a/include/fggl/util/guid.hpp +++ b/include/fggl/util/guid.hpp @@ -67,6 +67,27 @@ namespace fggl::util { return hash; } + template<unsigned N> + struct FString { + char c[N]; + }; + + // https://stackoverflow.com/a/65440575 + template<unsigned ...Len> + constexpr auto cat(const char (&...strings)[Len]) { + constexpr unsigned N = (... + Len) - sizeof...(Len); + FString<N + 1> result = {}; + result.c[N] = '\0'; + + char* dst = result.c; + for (const char* src : {strings...}) { + for (; *src != '\0'; src++, dst++) { + *dst = *src; + } + } + return result; + } + // debug-only functions #ifndef NDEBUG GUID internString(const char *str); -- GitLab