diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-14 18:03:34 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-14 18:03:34 +0100 |
commit | 5776a36b454fac04617313da011d7aa2b0e834d3 (patch) | |
tree | 1eb96d07e9a17a51e5763f397fc003f762cd2e75 /assetFactory | |
parent | Merge branch 'model-factory-textures' (diff) | |
parent | Add an asset template and use it to define all the foliage assets in the plan... (diff) | |
download | ilt-5776a36b454fac04617313da011d7aa2b0e834d3.tar.bz2 ilt-5776a36b454fac04617313da011d7aa2b0e834d3.tar.xz ilt-5776a36b454fac04617313da011d7aa2b0e834d3.zip |
Merge branch 'assimp'
Diffstat (limited to 'assetFactory')
-rw-r--r-- | assetFactory/asset.h | 4 | ||||
-rw-r--r-- | assetFactory/assetFactory.cpp | 63 | ||||
-rw-r--r-- | assetFactory/assetFactory.h | 8 | ||||
-rw-r--r-- | assetFactory/assimp.cpp | 130 | ||||
-rw-r--r-- | assetFactory/assimp.h | 14 | ||||
-rw-r--r-- | assetFactory/factoryMesh.cpp | 61 | ||||
-rw-r--r-- | assetFactory/modelFactoryMesh.cpp | 1 | ||||
-rw-r--r-- | assetFactory/modelFactoryMesh.h | 2 | ||||
-rw-r--r-- | assetFactory/style.cpp | 9 | ||||
-rw-r--r-- | assetFactory/textureFragment.cpp | 10 | ||||
-rw-r--r-- | assetFactory/textureFragment.h | 6 | ||||
-rw-r--r-- | assetFactory/texturePacker.cpp | 29 | ||||
-rw-r--r-- | assetFactory/texturePacker.h | 1 |
13 files changed, 271 insertions, 67 deletions
diff --git a/assetFactory/asset.h b/assetFactory/asset.h index 30f40cd..dba4974 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -4,11 +4,11 @@ #include "persistence.h" #include <stdTypeDefs.hpp> -class Texture; +class TextureAtlas; class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> { public: - using TexturePtr = std::shared_ptr<Texture>; + using TexturePtr = std::shared_ptr<TextureAtlas>; std::string id; std::string name; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index ed1af58..af0cd54 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -11,7 +11,7 @@ #include "resource.h" #include "saxParse-persistence.h" #include "texturePacker.h" -#include <stb/stb_image.h> +#include <numeric> AssetFactory::AssetFactory() : shapes { @@ -30,6 +30,18 @@ AssetFactory::loadXML(const std::filesystem::path & filename) return Persistence::SAXParsePersistence {}.loadState<std::shared_ptr<AssetFactory>>(file); } +AssetFactory::Assets +AssetFactory::loadAll(const std::filesystem::path & root) +{ + return std::accumulate(std::filesystem::recursive_directory_iterator {root}, + std::filesystem::recursive_directory_iterator {}, Assets {}, [](auto && out, auto && path) { + if (path.path().extension() == ".xml") { + out.merge(loadXML(path)->assets); + } + return std::move(out); + }); +} + AssetFactory::Colours AssetFactory::parseX11RGB(const char * path) { @@ -81,17 +93,11 @@ AssetFactory::parseColour(std::string_view in) const throw std::runtime_error("No such asset factory colour"); } -AssetFactory::TextureFragmentCoords -AssetFactory::getTextureCoords(std::string_view id) const +GLuint +AssetFactory::getMaterialIndex(std::string_view id) const { createTexutre(); - const auto & fragmentUV = textureFragmentPositions.at(id); - return { - fragmentUV.xy(), - fragmentUV.zy(), - fragmentUV.zw(), - fragmentUV.xw(), - }; + return textureFragmentPositions.at(id); } Asset::TexturePtr @@ -104,33 +110,31 @@ AssetFactory::getTexture() const void AssetFactory::createTexutre() const { - if (!textureFragments.empty() && (!texture || textureFragmentPositions.empty())) { - // * load images - std::vector<std::unique_ptr<Image>> images; + if (!textureFragments.empty() && !texture) { + // * layout images + std::map<const TextureFragment *, std::unique_ptr<const Image>> images; std::transform( - textureFragments.begin(), textureFragments.end(), std::back_inserter(images), [](const auto & tf) { - return std::make_unique<Image>(Resource::mapPath(tf.second->path), STBI_rgb_alpha); + textureFragments.begin(), textureFragments.end(), std::inserter(images, images.end()), [](auto && tf) { + return decltype(images)::value_type {tf.second.get(), tf.second->image->get()}; }); - // * layout images std::vector<TexturePacker::Image> imageSizes; - std::transform(images.begin(), images.end(), std::back_inserter(imageSizes), [](const auto & image) { - return TexturePacker::Image {image->width, image->height}; + std::transform(images.begin(), images.end(), std::back_inserter(imageSizes), [](const auto & i) { + return TexturePacker::Image {i.second->width, i.second->height}; }); const auto [layout, outSize] = TexturePacker {imageSizes}.pack(); // * create texture - texture = std::make_shared<Texture>(outSize.x, outSize.y, TextureOptions {.wrap = GL_CLAMP_TO_EDGE}); - std::transform(textureFragments.begin(), textureFragments.end(), + texture = std::make_shared<TextureAtlas>(outSize.x, outSize.y, layout.size()); + std::transform(images.begin(), images.end(), std::inserter(textureFragmentPositions, textureFragmentPositions.end()), - [position = layout.begin(), image = images.begin(), size = imageSizes.begin(), - outSize = glm::vec2 {outSize}](const auto & tf) mutable { - const auto positionFraction = glm::vec4 {*position, *position + *size} / outSize.xyxy(); - glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(position->x), static_cast<GLint>(position->y), - static_cast<GLint>(size->x), static_cast<GLint>(size->y), GL_RGBA, GL_UNSIGNED_BYTE, - image->get()->data.data()); + [position = layout.begin(), size = imageSizes.begin(), this](const auto & i) mutable { + const auto m = texture->add(*position, *size, i.second->data.data(), + { + .wrapU = i.first->mapmodeU, + .wrapV = i.first->mapmodeV, + }); position++; - image++; size++; - return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction}; + return decltype(textureFragmentPositions)::value_type {i.first->id, m}; }); } } @@ -141,7 +145,8 @@ AssetFactory::persist(Persistence::PersistenceStore & store) using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>; using MapAssets = Persistence::MapByMember<Assets>; using MapTextureFragments = Persistence::MapByMember<TextureFragments>; + using MapAssImp = Persistence::MapByMember<AssImps, std::shared_ptr<AssImp>, &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..e449ce2 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<std::string, Shape::Ptr, std::less<>>; using Assets = std::map<std::string, Asset::Ptr, std::less<>>; + using AssImps = std::map<std::string, AssImp::Ptr, std::less<>>; using TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; @@ -20,12 +22,14 @@ public: AssetFactory(); [[nodiscard]] static std::shared_ptr<AssetFactory> loadXML(const std::filesystem::path &); + [[nodiscard]] static Assets loadAll(const std::filesystem::path &); [[nodiscard]] ColourAlpha parseColour(std::string_view) const; - [[nodiscard]] TextureFragmentCoords getTextureCoords(std::string_view) const; + [[nodiscard]] GLuint getMaterialIndex(std::string_view) const; [[nodiscard]] Asset::TexturePtr getTexture() const; Shapes shapes; Assets assets; + AssImps assimps; Colours colours; TextureFragments textureFragments; @@ -39,5 +43,5 @@ private: void createTexutre() const; mutable Asset::TexturePtr texture; - mutable std::map<std::string_view, glm::vec4, std::less<>> textureFragmentPositions; + mutable std::map<std::string_view, GLuint, std::less<>> textureFragmentPositions; }; diff --git a/assetFactory/assimp.cpp b/assetFactory/assimp.cpp new file mode 100644 index 0000000..840c5a9 --- /dev/null +++ b/assetFactory/assimp.cpp @@ -0,0 +1,130 @@ +#include "assimp.h" +#include "assetFactory.h" +#include "collections.hpp" +#include "ptr.hpp" +#include "resource.h" +#include <assimp/cimport.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <future> +#include <stb/stb_image.h> + +template<typename T> +glm::vec<3, T> +operator*(const aiVector3t<T> & v) +{ + return {v.x, v.y, v.z}; +} + +template<typename T> +glm::vec<2, T> +operator!(const aiVector3t<T> & v) +{ + return {v.x, v.y}; +} + +#define AIRANGE(parent, member) \ + std::span \ + { \ + (parent)->m##member, (parent)->mNum##member \ + } + +using SceneCPtr = std::shared_ptr<const aiScene>; +class AssImpNode : public Shape { +public: + AssImpNode(SceneCPtr scene, const aiNode * node) : scene(std::move(scene)), node(node) { } + + SceneCPtr 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]->GetName(); + + GLuint material {}; + if (auto mf = Persistence::ParseBase::getShared<AssetFactory>("assetFactory")) { + material = mf->getMaterialIndex(m.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++]; + mesh.set_texcoord2D(fheh, ouv); + mesh.property(mesh.materialFaceProperty, fh) = material; + } + } + } + } +}; + +static_assert(TextureOptions::MapMode::Repeat == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Wrap)); +static_assert(TextureOptions::MapMode::Clamp == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Clamp)); +static_assert(TextureOptions::MapMode::Decal == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Decal)); +static_assert(TextureOptions::MapMode::Mirror == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Mirror)); + +void +AssImp::postLoad() +{ + SceneCPtr 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>("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<AssImpNode>(scene, m)}; + }); + const auto materials = AIRANGE(scene, Materials); + std::transform(materials.begin(), materials.end(), + std::inserter(mf->textureFragments, mf->textureFragments.end()), [&scene](const aiMaterial * m) { + aiString path; + m->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); + auto texture = std::make_shared<TextureFragment>(); + texture->id = m->GetName().C_Str(); + texture->path = path.C_Str(); + m->Get(AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0), texture->mapmodeU); + m->Get(AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0), texture->mapmodeV); + texture->image = Worker::addWork([t = scene->GetEmbeddedTexture(path.C_Str())]() { + return std::make_unique<Image>( + std::span {reinterpret_cast<unsigned char *>(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<AssImp>; + + void postLoad() override; + + bool persist(Persistence::PersistenceStore & store) override; + + std::string path; +}; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 8869efd..f7bc7c8 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -6,43 +6,46 @@ Mesh::Ptr FactoryMesh::createMesh() const { - ModelFactoryMesh mesh; + std::vector<Vertex> vertices; + std::vector<unsigned int> indices; + for (const auto & use : uses) { + ModelFactoryMesh mesh; use->createMesh(mesh, 1); - } - mesh.update_face_normals(); - mesh.update_vertex_normals(); - std::vector<Vertex> vertices; - std::vector<unsigned int> indices; - for (const auto & face : mesh.faces()) { - const auto & smooth = mesh.property(mesh.smoothFaceProperty, face); - const auto & colour = mesh.color(face); + mesh.update_face_normals(); + mesh.update_vertex_normals(); + for (const auto & face : mesh.faces()) { + const auto & smooth = mesh.property(mesh.smoothFaceProperty, face); + const auto & colour = mesh.color(face); + const auto & material = mesh.property(mesh.materialFaceProperty, face); - std::vector<unsigned int> faceIndices; - for (const auto & heh : mesh.fh_range(face)) { - const auto & vertex = mesh.to_vertex_handle(heh); - const auto & textureUV = mesh.texcoord2D(heh); - const auto & point = mesh.point(vertex); - const auto & normal = smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) - : mesh.property(mesh.face_normals_pph(), face); - Vertex outVertex {point, textureUV, normal, colour}; - if (const auto existingItr = std::find(vertices.rbegin(), vertices.rend(), outVertex); - existingItr != vertices.rend()) { - faceIndices.push_back(static_cast<unsigned int>(std::distance(existingItr, vertices.rend()) - 1)); - } - else { - faceIndices.push_back(static_cast<unsigned int>(vertices.size())); - vertices.emplace_back(outVertex); + std::vector<unsigned int> faceIndices; + for (const auto & heh : mesh.fh_range(face)) { + const auto & vertex = mesh.to_vertex_handle(heh); + const auto & textureUV = mesh.texcoord2D(heh); + const auto & point = mesh.point(vertex); + const auto & normal = smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) + : mesh.property(mesh.face_normals_pph(), face); + Vertex outVertex {point, textureUV, normal, colour, material}; + if (const auto existingItr = std::find(vertices.rbegin(), vertices.rend(), outVertex); + existingItr != vertices.rend()) { + faceIndices.push_back(static_cast<unsigned int>(std::distance(existingItr, vertices.rend()) - 1)); + } + else { + faceIndices.push_back(static_cast<unsigned int>(vertices.size())); + vertices.emplace_back(outVertex); + } } - } - for (unsigned int i = 2; i < faceIndices.size(); i++) { - indices.push_back(faceIndices[0]); - indices.push_back(faceIndices[i - 1]); - indices.push_back(faceIndices[i]); + for (unsigned int i = 2; i < faceIndices.size(); i++) { + indices.push_back(faceIndices[0]); + indices.push_back(faceIndices[i - 1]); + indices.push_back(faceIndices[i]); + } } } + return std::make_shared<Mesh>(vertices, indices); } diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp index e640502..3d4b5f3 100644 --- a/assetFactory/modelFactoryMesh.cpp +++ b/assetFactory/modelFactoryMesh.cpp @@ -3,6 +3,7 @@ ModelFactoryMesh::ModelFactoryMesh() { add_property(smoothFaceProperty); + add_property(materialFaceProperty); add_property(nameFaceProperty); add_property(nameAdjFaceProperty); } diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index 8ac2edd..4eac7ee 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -1,6 +1,7 @@ #pragma once #include "modelFactoryMesh_fwd.h" +#include <GL/glew.h> #include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh> #include <OpenMesh/Core/Mesh/Traits.hh> #include <glm/geometric.hpp> @@ -47,6 +48,7 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTra ModelFactoryMesh(); OpenMesh::FPropHandleT<bool> smoothFaceProperty; + OpenMesh::FPropHandleT<GLuint> materialFaceProperty; OpenMesh::FPropHandleT<std::string> nameFaceProperty; OpenMesh::HPropHandleT<std::string> nameAdjFaceProperty; diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 12346a6..ea67fc2 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -30,7 +30,14 @@ Style::applyStyle( else { mesh.set_color(face, {}); if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) { - auto coords = mf->getTextureCoords(texture); + const auto material = mf->getMaterialIndex(texture); + mesh.property(mesh.materialFaceProperty, face) = material; + static constexpr std::array<ModelFactoryTraits::TexCoord2D, 4> coords {{ + {0.f, 0.f}, + {1.f, 0.f}, + {1.f, 1.f}, + {0.f, 1.f}, + }}; auto coord = coords.begin(); // Wild assumption that face is a quad and the texture should apply linearly for (const auto & heh : mesh.fh_range(face)) { diff --git a/assetFactory/textureFragment.cpp b/assetFactory/textureFragment.cpp index 72107a5..0a4ec1d 100644 --- a/assetFactory/textureFragment.cpp +++ b/assetFactory/textureFragment.cpp @@ -1,7 +1,17 @@ #include "textureFragment.h" +#include "resource.h" +#include <stb/stb_image.h> bool TextureFragment::persist(Persistence::PersistenceStore & store) { return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(path); } + +void +TextureFragment::postLoad() +{ + image = Worker::addWork([this]() { + return std::make_unique<Image>(Resource::mapPath(path), STBI_rgb_alpha); + }); +} diff --git a/assetFactory/textureFragment.h b/assetFactory/textureFragment.h index 52f2591..e6f07f3 100644 --- a/assetFactory/textureFragment.h +++ b/assetFactory/textureFragment.h @@ -1,14 +1,20 @@ #pragma once +#include "gfx/image.h" +#include "gfx/models/texture.h" #include "persistence.h" #include "stdTypeDefs.hpp" +#include "worker.h" class TextureFragment : public Persistence::Persistable, public StdTypeDefs<TextureFragment> { public: std::string id; std::string path; + TextureOptions::MapMode mapmodeU, mapmodeV; + Worker::WorkPtrT<std::unique_ptr<Image>> image; private: friend Persistence::SelectionPtrBase<Ptr>; bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; }; diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp index 68a6010..fcc935f 100644 --- a/assetFactory/texturePacker.cpp +++ b/assetFactory/texturePacker.cpp @@ -1,7 +1,9 @@ #include "texturePacker.h" #include "collections.hpp" +#include <GL/glew.h> #include <algorithm> #include <cstdio> +#include <glm/common.hpp> #include <numeric> #include <ostream> #include <set> @@ -12,6 +14,9 @@ TexturePacker::TexturePacker(std::span<const Image> in) : std::sort(sortedIndexes.rbegin(), sortedIndexes.rend(), [this](const auto a, const auto b) { return area(inputImages[a]) < area(inputImages[b]); }); + int mts; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mts); + maxTextureSize = static_cast<unsigned int>(mts); } TexturePacker::Result @@ -23,6 +28,9 @@ TexturePacker::pack() const TexturePacker::Result TexturePacker::pack(Size size) const { + if (size.x > maxTextureSize || size.y > maxTextureSize) { + return {}; + } using Spaces = std::set<Space>; Spaces spaces {{{}, size}}; @@ -47,14 +55,27 @@ TexturePacker::pack(Size size) const } } else { - if (size.x < size.y) { - return pack({size.x * 2, size.y}); + const auto x = pack({size.x * 2, size.y}), y = pack({size.x, size.y * 2}); + if (!x.first.empty() && (y.first.empty() || area(x.second) < area(y.second))) { + return x; } - else { - return pack({size.x, size.y * 2}); + else if (!y.first.empty()) { + return y; } + return {}; } } + if (GLEW_ARB_texture_non_power_of_two) { + // Crop the size back to minimum size + size = std::transform_reduce( + result.begin(), result.end(), inputImages.begin(), Size {}, + [](auto && max, auto && limit) { + return glm::max(max, limit); + }, + [](auto && pos, auto && size) { + return pos + size; + }); + } return {result, size}; } diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h index ca0d67a..a1b270b 100644 --- a/assetFactory/texturePacker.h +++ b/assetFactory/texturePacker.h @@ -38,4 +38,5 @@ public: private: std::span<const Image> inputImages; std::vector<size_t> sortedIndexes; + unsigned int maxTextureSize; }; |