From b25bc3cc32c5a9057c66ad282fa1cdfe0ed6094a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 10 Apr 2023 18:45:13 +0100 Subject: First cut loading assets using assimp This is far from perfect, specifically the created texture atlas is not compatibile with wrapping texture UVs --- assetFactory/assetFactory.cpp | 5 +- assetFactory/assetFactory.h | 3 ++ assetFactory/assimp.cpp | 119 ++++++++++++++++++++++++++++++++++++++++++ assetFactory/assimp.h | 14 +++++ res/foliage.xml | 9 ++++ test/perf-assetFactory.cpp | 11 ++++ test/test-assetFactory.cpp | 17 ++++++ 7 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 assetFactory/assimp.cpp create mode 100644 assetFactory/assimp.h create mode 100644 res/foliage.xml diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index ec0e19f..917edfe 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -109,7 +109,7 @@ AssetFactory::createTexutre() const std::transform( textureFragments.begin(), textureFragments.end(), std::back_inserter(imageSizes), [](const auto & tf) { return TexturePacker::Image {tf.second->image->width, tf.second->image->height}; - }); + }); const auto [layout, outSize] = TexturePacker {imageSizes}.pack(); // * create texture texture = std::make_shared(outSize.x, outSize.y, TextureOptions {.wrap = GL_CLAMP_TO_EDGE}); @@ -134,7 +134,8 @@ AssetFactory::persist(Persistence::PersistenceStore & store) using MapObjects = Persistence::MapByMember>; using MapAssets = Persistence::MapByMember; using MapTextureFragments = Persistence::MapByMember; + using MapAssImp = Persistence::MapByMember, &AssImp::path>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) && STORE_NAME_HELPER("textureFragment", textureFragments, MapTextureFragments) - && STORE_NAME_HELPER("asset", assets, MapAssets); + && STORE_NAME_HELPER("assimp", assimps, MapAssImp) && STORE_NAME_HELPER("asset", assets, MapAssets); } diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 52692c4..7843d44 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -1,6 +1,7 @@ #pragma once #include "asset.h" +#include "assimp.h" #include "persistence.h" #include "shape.h" #include "textureFragment.h" @@ -12,6 +13,7 @@ class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; using Assets = std::map>; + using AssImps = std::map>; using TextureFragments = std::map>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; @@ -26,6 +28,7 @@ public: Shapes shapes; Assets assets; + AssImps assimps; Colours colours; TextureFragments textureFragments; diff --git a/assetFactory/assimp.cpp b/assetFactory/assimp.cpp new file mode 100644 index 0000000..55090f4 --- /dev/null +++ b/assetFactory/assimp.cpp @@ -0,0 +1,119 @@ +#include "assimp.h" +#include "assetFactory.h" +#include "collections.hpp" +#include "ptr.hpp" +#include "resource.h" +#include +#include +#include +#include + +template +glm::vec<3, T> +operator*(const aiVector3t & v) +{ + return {v.x, v.y, v.z}; +} + +template +glm::vec<2, T> +operator!(const aiVector3t & v) +{ + return {v.x, v.y}; +} + +#define AIRANGE(parent, member) \ + std::span \ + { \ + (parent)->m##member, (parent)->mNum##member \ + } + +using ScemeCPtr = std::shared_ptr; +class AssImpNode : public Shape { +public: + AssImpNode(ScemeCPtr scene, const aiNode * node) : scene(std::move(scene)), node(node) { } + + ScemeCPtr scene; + const aiNode * node; + + CreatedFaces + createMesh(ModelFactoryMesh & mesh, float) const + { + CreatedFaces faces; + addMesh(faces, mesh, node); + return faces; + } + + void + addMesh(CreatedFaces & faces, ModelFactoryMesh & mesh, const aiNode * node) const + { + for (const auto & m : AIRANGE(node, Meshes)) { + addMesh(faces, mesh, scene->mMeshes[m]); + } + for (const auto & n : AIRANGE(node, Children)) { + addMesh(faces, mesh, n); + } + } + + void + addMesh(CreatedFaces & faces, ModelFactoryMesh & mesh, const aiMesh * amesh) const + { + const auto vhs = AIRANGE(amesh, Vertices) * [&mesh](auto && v) { + return mesh.add_vertex(*v); + }; + const auto & m = *scene->mMaterials[amesh->mMaterialIndex]; + + AssetFactory::TextureFragmentCoords tfc; + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { + aiString path; + m.Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); + tfc = mf->getTextureCoords(path.C_Str()); + } + + for (const auto & f : AIRANGE(amesh, Faces)) { + const auto fvhs = AIRANGE(&f, Indices) * [&vhs](auto && i) { + return vhs[i]; + }; + const auto fh = faces.emplace(mesh.add_namedFace(amesh->mName.C_Str(), fvhs))->second; + if (amesh->HasTextureCoords(0)) { + for (auto idx = f.mIndices; const auto fheh : mesh.fh_range(fh)) { + const auto ouv = !amesh->mTextureCoords[0][*idx++]; + const auto uv = glm::mix(tfc[0], tfc[2], ouv); + mesh.set_texcoord2D(fheh, uv); + } + } + } + } +}; + +void +AssImp::postLoad() +{ + ScemeCPtr scene { + aiImportFile(Resource::mapPath(path).c_str(), aiProcess_RemoveRedundantMaterials), &aiReleaseImport}; + if (!scene) { + throw std::runtime_error("Failed to load asset library: " + path); + } + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { + const auto root = AIRANGE(scene->mRootNode, Children); + std::transform( + root.begin(), root.end(), std::inserter(mf->shapes, mf->shapes.end()), [&scene](const aiNode * m) { + return AssetFactory::Shapes::value_type {m->mName.C_Str(), std::make_shared(scene, m)}; + }); + const auto textures = AIRANGE(scene, Textures); + std::transform(textures.begin(), textures.end(), + std::inserter(mf->textureFragments, mf->textureFragments.end()), [](const aiTexture * t) { + auto texture = std::make_shared(); + texture->id = texture->path = t->mFilename.C_Str(); + texture->image = std::make_unique( + std::span {reinterpret_cast(t->pcData), t->mWidth}, STBI_rgb_alpha); + return AssetFactory::TextureFragments::value_type {texture->id, texture}; + }); + } +} + +bool +AssImp::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(path); +} diff --git a/assetFactory/assimp.h b/assetFactory/assimp.h new file mode 100644 index 0000000..55b551d --- /dev/null +++ b/assetFactory/assimp.h @@ -0,0 +1,14 @@ +#pragma once + +#include "persistence.h" + +class AssImp : public Persistence::Persistable { +public: + using Ptr = std::shared_ptr; + + void postLoad() override; + + bool persist(Persistence::PersistenceStore & store) override; + + std::string path; +}; diff --git a/res/foliage.xml b/res/foliage.xml new file mode 100644 index 0000000..1b0a1aa --- /dev/null +++ b/res/foliage.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/perf-assetFactory.cpp b/test/perf-assetFactory.cpp index c90ac52..147e4ba 100644 --- a/test/perf-assetFactory.cpp +++ b/test/perf-assetFactory.cpp @@ -14,6 +14,17 @@ brush47xml_load(benchmark::State & state) } } +static void +foliagexml_load(benchmark::State & state) +{ + TestMainWindow window; + + for (auto _ : state) { + benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/foliage.xml")); + } +} + BENCHMARK(brush47xml_load); +BENCHMARK(foliagexml_load); BENCHMARK_MAIN(); diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 54168aa..dd458ca 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -8,6 +8,8 @@ #include "assetFactory/assetFactory.h" #include "assetFactory/object.h" #include "assetFactory/texturePacker.h" +#include "game/scenary/foliage.h" +#include "game/scenary/plant.h" #include "game/vehicles/railVehicle.h" #include "game/vehicles/railVehicleClass.h" #include "gfx/gl/sceneRenderer.h" @@ -96,6 +98,21 @@ BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) render(); } + +BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) +{ + auto mf = AssetFactory::loadXML(RESDIR "/foliage.xml"); + BOOST_REQUIRE(mf); + auto tree_01_1 = mf->assets.at("Tree-01-1"); + BOOST_REQUIRE(tree_01_1); + auto tree_01_1_f = std::dynamic_pointer_cast(tree_01_1); + BOOST_REQUIRE(tree_01_1_f); + + auto plant = std::make_shared(tree_01_1_f, Location {}); + objects.objects.push_back(plant); + + render(); +} BOOST_AUTO_TEST_SUITE_END(); template using InOut = std::tuple; -- cgit v1.2.3