diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-09 23:29:00 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-09 23:29:00 +0100 |
commit | b6b3944e9507f337f77a72ea44b5c0ccba7a2c01 (patch) | |
tree | a487df54853522f973ed987006e787117804b9eb /assetFactory | |
parent | Swap messy glmvec wrapper for OpenMesh Point/Normal with real glm::vec and a ... (diff) | |
parent | Move remaining split/plane functions to use library (diff) | |
download | ilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.tar.bz2 ilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.tar.xz ilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.zip |
Merge branch 'model-factory-textures'
Diffstat (limited to 'assetFactory')
-rw-r--r-- | assetFactory/asset.cpp | 10 | ||||
-rw-r--r-- | assetFactory/asset.h | 6 | ||||
-rw-r--r-- | assetFactory/assetFactory.cpp | 64 | ||||
-rw-r--r-- | assetFactory/assetFactory.h | 13 | ||||
-rw-r--r-- | assetFactory/cuboid.cpp | 4 | ||||
-rw-r--r-- | assetFactory/faceController.cpp | 186 | ||||
-rw-r--r-- | assetFactory/faceController.h | 25 | ||||
-rw-r--r-- | assetFactory/factoryMesh.cpp | 40 | ||||
-rw-r--r-- | assetFactory/modelFactoryMesh.cpp | 16 | ||||
-rw-r--r-- | assetFactory/modelFactoryMesh.h | 11 | ||||
-rw-r--r-- | assetFactory/style.cpp | 52 | ||||
-rw-r--r-- | assetFactory/style.h | 7 | ||||
-rw-r--r-- | assetFactory/textureFragment.cpp | 7 | ||||
-rw-r--r-- | assetFactory/textureFragment.h | 14 | ||||
-rw-r--r-- | assetFactory/texturePacker.cpp | 80 | ||||
-rw-r--r-- | assetFactory/texturePacker.h | 41 |
16 files changed, 473 insertions, 103 deletions
diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp index 3ab2f1c..e3f5feb 100644 --- a/assetFactory/asset.cpp +++ b/assetFactory/asset.cpp @@ -1,4 +1,5 @@ #include "asset.h" +#include "assetFactory.h" bool Asset::persist(Persistence::PersistenceStore & store) @@ -6,6 +7,15 @@ Asset::persist(Persistence::PersistenceStore & store) return STORE_MEMBER(id) && STORE_MEMBER(name); } +Asset::TexturePtr +Asset::getTexture() const +{ + if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) { + return mf->getTexture(); + } + return nullptr; +} + Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) : Persistence::SelectionPtrBase<FactoryMesh::Ptr> {fmesh}, out {m} { } diff --git a/assetFactory/asset.h b/assetFactory/asset.h index e3318e4..30f40cd 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -4,12 +4,18 @@ #include "persistence.h" #include <stdTypeDefs.hpp> +class Texture; + class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> { public: + using TexturePtr = std::shared_ptr<Texture>; + std::string id; std::string name; protected: + TexturePtr getTexture() const; + struct MeshConstruct : public Persistence::SelectionPtrBase<FactoryMesh::Ptr> { using Persistence::SelectionPtrBase<FactoryMesh::Ptr>::setValue; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index f5fc2b3..ed1af58 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -2,11 +2,16 @@ #include "collections.hpp" #include "cuboid.h" #include "cylinder.h" +#include "filesystem.h" +#include "gfx/image.h" +#include "gfx/models/texture.h" #include "modelFactoryMesh_fwd.h" #include "object.h" #include "plane.h" +#include "resource.h" #include "saxParse-persistence.h" -#include <filesystem.h> +#include "texturePacker.h" +#include <stb/stb_image.h> AssetFactory::AssetFactory() : shapes { @@ -75,11 +80,68 @@ 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 +{ + createTexutre(); + const auto & fragmentUV = textureFragmentPositions.at(id); + return { + fragmentUV.xy(), + fragmentUV.zy(), + fragmentUV.zw(), + fragmentUV.xw(), + }; +} + +Asset::TexturePtr +AssetFactory::getTexture() const +{ + createTexutre(); + return texture; +} + +void +AssetFactory::createTexutre() const +{ + if (!textureFragments.empty() && (!texture || textureFragmentPositions.empty())) { + // * load images + std::vector<std::unique_ptr<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); + }); + // * 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}; + }); + 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(), + 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++; + image++; + size++; + return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction}; + }); + } +} + bool AssetFactory::persist(Persistence::PersistenceStore & store) { using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>; using MapAssets = Persistence::MapByMember<Assets>; + using MapTextureFragments = Persistence::MapByMember<TextureFragments>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) + && STORE_NAME_HELPER("textureFragment", textureFragments, MapTextureFragments) && STORE_NAME_HELPER("asset", assets, MapAssets); } diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index b47d408..52692c4 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -3,23 +3,31 @@ #include "asset.h" #include "persistence.h" #include "shape.h" +#include "textureFragment.h" #include <filesystem> +class Texture; + 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 TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; using Colours = std::map<std::string, Colour, std::less<>>; + using TextureFragmentCoords = std::array<glm::vec2, 4>; AssetFactory(); [[nodiscard]] static std::shared_ptr<AssetFactory> loadXML(const std::filesystem::path &); [[nodiscard]] ColourAlpha parseColour(std::string_view) const; + [[nodiscard]] TextureFragmentCoords getTextureCoords(std::string_view) const; + [[nodiscard]] Asset::TexturePtr getTexture() const; Shapes shapes; Assets assets; Colours colours; + TextureFragments textureFragments; static Colours parseX11RGB(const char * rgbtxtpath); static void normalizeColourName(std::string &); @@ -27,4 +35,9 @@ public: private: friend Persistence::SelectionPtrBase<std::shared_ptr<AssetFactory>>; bool persist(Persistence::PersistenceStore & store) override; + + void createTexutre() const; + + mutable Asset::TexturePtr texture; + mutable std::map<std::string_view, glm::vec4, std::less<>> textureFragmentPositions; }; diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp index f200258..a8ddcd9 100644 --- a/assetFactory/cuboid.cpp +++ b/assetFactory/cuboid.cpp @@ -21,8 +21,8 @@ Cuboid::createMesh(ModelFactoryMesh & mesh, float) const return { mesh.add_namedFace("top", vhs[4], vhs[5], vhs[6], vhs[7]), mesh.add_namedFace("bottom", vhs[0], vhs[1], vhs[2], vhs[3]), - mesh.add_namedFace("left", vhs[0], vhs[7], vhs[6], vhs[1]), - mesh.add_namedFace("right", vhs[2], vhs[5], vhs[4], vhs[3]), + mesh.add_namedFace("right", vhs[0], vhs[7], vhs[6], vhs[1]), + mesh.add_namedFace("left", vhs[2], vhs[5], vhs[4], vhs[3]), mesh.add_namedFace("front", vhs[0], vhs[3], vhs[4], vhs[7]), mesh.add_namedFace("back", vhs[2], vhs[1], vhs[6], vhs[5]), }; diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 1ec1467..b305f1c 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -2,90 +2,140 @@ #include "collections.hpp" #include "maths.h" #include "modelFactoryMesh.h" +#include "ray.hpp" void -FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, +FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & names, Shape::CreatedFaces & faces) const { - const auto getAdjacentFaceName = [&mesh](const auto & ofrange, OpenMesh::FaceHandle nf) -> std::string { - const auto nfrange = mesh.ff_range(nf); - if (const auto target = std::find_first_of(ofrange.begin(), ofrange.end(), nfrange.begin(), nfrange.end()); - target != ofrange.end()) { - return mesh.property(mesh.nameFaceProperty, *target); - }; - return {}; - }; + std::stringstream nameStream {names}; + std::for_each(std::istream_iterator<std::string>(nameStream), std::istream_iterator<std::string> {}, + [&](const auto & name) { + applySingle(mesh, parents, name, faces); + }); +} - const auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (controlledFaces.empty()) { - throw std::runtime_error("Named face(s) do not exist: " + name); - } +void +FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, + Shape::CreatedFaces & faces) const +{ + auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (!type.empty()) { - const auto mutation = getMatrix(); + if (!type.empty() || !splits.empty()) { faces.erase(name); - for (const auto & cf : controlledFaces) { - // get points - const auto baseVertices {materializeRange(mesh.fv_range(cf.second))}; - auto points = std::accumulate(baseVertices.begin(), baseVertices.end(), std::vector<glm::vec3> {}, - [&mesh](auto && out, auto && v) { - out.push_back(mesh.point(v)); - return std::move(out); - }); - const auto vertexCount = points.size(); - const auto centre - = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast<float>(vertexCount); - if (type == "extrude") { - Shape::CreatedFaces newFaces; - // mutate points - std::for_each(points.begin(), points.end(), [&mutation, ¢re](auto && p) { - p = centre + ((p - centre) % mutation); - }); - // create new vertices - std::vector<OpenMesh::VertexHandle> vertices; - std::transform(points.begin(), points.end(), std::back_inserter(vertices), [&mesh](auto && p) { - return mesh.add_vertex(p); - }); - // create new faces - const auto ofrange = materializeRange(mesh.ff_range(cf.second)); - mesh.delete_face(cf.second); - for (size_t idx {}; idx < vertexCount; ++idx) { - const auto next = (idx + 1) % vertexCount; - const auto newFace - = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]}); - auto & name = mesh.property(mesh.nameFaceProperty, newFace); - name = getAdjacentFaceName(ofrange, newFace); - newFaces.emplace(name, newFace); - } - newFaces.emplace(name, mesh.add_face(vertices)); - if (smooth) { - for (const auto & [name, face] : newFaces) { - mesh.property(mesh.smoothFaceProperty, face) = true; - } - } - applyStyle(mesh, parents + this, newFaces); - for (const auto & [name, faceController] : faceControllers) { - faceController->apply(mesh, parents + this, name, newFaces); - } - faces.merge(std::move(newFaces)); - } - else { - mesh.property(mesh.smoothFaceProperty, cf.second) = smooth; - applyStyle(mesh, parents + this, cf.second); + } + for (auto & [faceName, faceHandle] : controlledFaces) { + Shape::CreatedFaces newFaces; + for (const auto & [newFaceSuffix, splitDef] : splits) { + newFaces.merge(split(mesh, name + newFaceSuffix, faceHandle, *splitDef)); + } + if (type == "extrude") { + newFaces.merge(extrude(mesh, name, faceHandle)); + } + if (!newFaces.empty()) { + applyStyle(mesh, parents + this, newFaces); + for (const auto & [subFaceName, faceController] : faceControllers) { + faceController->apply(mesh, parents + this, subFaceName, newFaces); } + faces.merge(std::move(newFaces)); + } + else { + applyStyle(mesh, parents + this, faceHandle); + } + } +} + +Shape::CreatedFaces +FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle faceHandle) const +{ + // get points + const auto baseVertices {materializeRange(mesh.fv_range(faceHandle))}; + // create new vertices + const auto vertices + = baseVertices * [&mesh, mutation = getMatrix(), centre = mesh.calc_face_centroid(faceHandle)](auto && v) { + return mesh.add_vertex(centre + ((mesh.point(v) - centre) % mutation)); + }; + // get new faces names + const auto vertexCount = baseVertices.size(); + std::vector<std::string> faceNames; + for (size_t idx {}; idx < vertexCount; ++idx) { + const auto next = (idx + 1) % vertexCount; + const auto existingEdge = mesh.find_halfedge(baseVertices[idx], baseVertices[next]); + faceNames.push_back(mesh.property(mesh.nameAdjFaceProperty, existingEdge)); + } + // create new faces + mesh.delete_face(faceHandle); + Shape::CreatedFaces newFaces; + for (size_t idx {}; idx < vertexCount; ++idx) { + const auto next = (idx + 1) % vertexCount; + newFaces.emplace(mesh.add_namedFace( + faceNames[idx], baseVertices[idx], baseVertices[next], vertices[next], vertices[idx])); + } + newFaces.emplace(mesh.add_namedFace(faceName, vertices)); + + return newFaces; +} + +Shape::CreatedFaces +FaceController::split( + ModelFactoryMesh & mesh, const std::string & name, OpenMesh::FaceHandle & fh, const Split & split) const +{ + // Map face vertex handles to their relationship to the split plane + const auto vertices = materializeRange(mesh.fv_range(fh)); + auto vertexRelations = vertices * [&split, &mesh](OpenMesh::VertexHandle vh) { + return std::make_pair(vh, split.getRelation(mesh.point(vh))); + }; + // Insert new vertices where half edges intersect the split plane + for (size_t curIdx = 0; curIdx < vertexRelations.size(); ++curIdx) { + const size_t nextIdx = (curIdx + 1) % vertexRelations.size(); + const auto ¤t = vertexRelations[curIdx], next = vertexRelations[nextIdx]; + if (GeometricPlane::isIntersect(current.second, next.second)) { + const auto ray = Ray::fromPoints(mesh.point(current.first), mesh.point(next.first)); + const auto intersect = split.getRayIntersectPosition(ray); + assert(intersect); + const auto newv = mesh.add_vertex(intersect->position); + auto where = vertexRelations.begin(); + ++curIdx; + std::advance(where, curIdx); + vertexRelations.emplace(where, newv, GeometricPlane::PlaneRelation::On); } } - else { - for (const auto & cf : controlledFaces) { - applyStyle(mesh, parents + this, cf.second); + // Create vertex vectors + std::array<std::vector<OpenMesh::VertexHandle>, 2> out; + auto filterVertices = [&vertexRelations](auto & out, auto notRelation) { + for (const auto & vhr : vertexRelations) { + if (vhr.second != notRelation) { + out.emplace_back(vhr.first); + } } + }; + filterVertices(out.front(), GeometricPlane::PlaneRelation::Above); + filterVertices(out.back(), GeometricPlane::PlaneRelation::Below); + + if (out.back().size() > 2) { + Shape::CreatedFaces newFaces; + const auto oldName = mesh.property(mesh.nameFaceProperty, fh); + mesh.delete_face(fh); + const auto newf1 = newFaces.insert(mesh.add_namedFace(oldName, out.front()))->second; + const auto newf2 = newFaces.insert(mesh.add_namedFace(name, out.back()))->second; + mesh.copy_property(mesh.smoothFaceProperty, fh, newf1); + mesh.copy_property(mesh.smoothFaceProperty, fh, newf2); + fh = newf1; + return newFaces; } + return {}; } bool FaceController::persist(Persistence::PersistenceStore & store) { - return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && STORE_MEMBER(smooth) - && Mutation::persist(store) + return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && Mutation::persist(store) + && STORE_NAME_HELPER("split", splits, Persistence::MapByMember<Splits>) && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember<FaceControllers>); } + +bool +FaceController::Split::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(origin) && STORE_MEMBER(normal); +} diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 10a226a..0376241 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -1,5 +1,6 @@ #pragma once +#include "geometricPlane.h" #include "modelFactoryMesh_fwd.h" #include "mutation.h" #include "persistence.h" @@ -10,15 +11,29 @@ class FaceController : public Mutation, public Style, public Persistence::Persistable { public: + class Split : public Persistable, public GeometricPlane { + public: + std::string id; + + private: + friend Persistence::SelectionPtrBase<std::unique_ptr<Split>>; + bool persist(Persistence::PersistenceStore & store) override; + std::string + getId() const override + { + return {}; + }; + }; using FaceControllers = std::map<std::string, std::unique_ptr<FaceController>>; + using Splits = std::map<std::string, std::unique_ptr<Split>>; - void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, + void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & names, Shape::CreatedFaces & faces) const; std::string id; std::string type; - bool smooth {false}; FaceControllers faceControllers; + Splits splits; private: friend Persistence::SelectionPtrBase<std::unique_ptr<FaceController>>; @@ -28,4 +43,10 @@ private: { return {}; }; + + void applySingle(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, + Shape::CreatedFaces & faces) const; + Shape::CreatedFaces extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle) const; + Shape::CreatedFaces split( + ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle &, const Split &) const; }; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 1665b90..8869efd 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -2,34 +2,48 @@ #include "collections.hpp" #include "gfx/models/vertex.hpp" #include "modelFactoryMesh.h" -#include <glm/ext/matrix_transform.hpp> Mesh::Ptr FactoryMesh::createMesh() const { - constexpr glm::vec2 NullUV {}; - ModelFactoryMesh mesh; for (const auto & use : uses) { use->createMesh(mesh, 1); } - mesh.garbage_collection(); - mesh.triangulate(); 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); - for (const auto & vertex : mesh.fv_range(face)) { - vertices.emplace_back(mesh.point(vertex), NullUV, - smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) - : mesh.property(mesh.face_normals_pph(), face), - colour); + const auto & smooth = mesh.property(mesh.smoothFaceProperty, face); + const auto & colour = mesh.color(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); + } + } + + 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, vectorOfN(vertices.size())); + return std::make_shared<Mesh>(vertices, indices); } bool diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp index 806ac3b..e640502 100644 --- a/assetFactory/modelFactoryMesh.cpp +++ b/assetFactory/modelFactoryMesh.cpp @@ -4,4 +4,20 @@ ModelFactoryMesh::ModelFactoryMesh() { add_property(smoothFaceProperty); add_property(nameFaceProperty); + add_property(nameAdjFaceProperty); +} + +void +ModelFactoryMesh::configNamedFace(const std::string & name, OpenMesh::FaceHandle handle) +{ + property(nameFaceProperty, handle) = name; + const auto halfEdges = fh_range(handle); + for (const auto & he : halfEdges) { + if (auto ofh = opposite_face_handle(he); ofh.is_valid()) { + property(nameAdjFaceProperty, he) = property(nameFaceProperty, ofh); + } + if (auto oheh = opposite_halfedge_handle(he); oheh.is_valid()) { + property(nameAdjFaceProperty, oheh) = name; + } + } } diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index 149ff1a..8ac2edd 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -4,6 +4,7 @@ #include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh> #include <OpenMesh/Core/Mesh/Traits.hh> #include <glm/geometric.hpp> +#include <glm/vec2.hpp> #include <glm/vec3.hpp> #include <glm/vec4.hpp> @@ -35,9 +36,11 @@ struct ModelFactoryTraits : public OpenMesh::DefaultTraits { FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status | OpenMesh::Attributes::Color); EdgeAttributes(OpenMesh::Attributes::Status); VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + HalfedgeAttributes(OpenMesh::Attributes::TexCoord2D); using Point = glm::vec3; using Normal = glm::vec3; using Color = glm::vec4; + using TexCoord2D = glm::vec2; }; struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTraits> { @@ -45,13 +48,17 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTra OpenMesh::FPropHandleT<bool> smoothFaceProperty; OpenMesh::FPropHandleT<std::string> nameFaceProperty; + OpenMesh::HPropHandleT<std::string> nameAdjFaceProperty; template<typename... Vs> std::pair<std::string, OpenMesh::FaceHandle> add_namedFace(std::string name, Vs &&... vs) { const auto handle = add_face(std::forward<Vs>(vs)...); - property(nameFaceProperty, handle) = name; - return std::make_pair(name, handle); + configNamedFace(name, handle); + return {std::move(name), handle}; } + +private: + void configNamedFace(const std::string & name, OpenMesh::FaceHandle); }; diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index fc5c34e..12346a6 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -4,27 +4,48 @@ void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour, - [](auto && style) { - return style->colour.a > 0; - }); - effectiveColour.has_value()) { - for (const auto & face : faces) { - mesh.set_color(face.second, effectiveColour->get()); - } + for (const auto & face : faces) { + applyStyle(mesh, face.second, getColour(parents)); } } void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour, - [](auto && style) { - return style->colour.a > 0; - }); - effectiveColour.has_value()) { - mesh.set_color(face, effectiveColour->get()); + applyStyle(mesh, face, getColour(parents)); +} + +void +Style::applyStyle( + ModelFactoryMesh & mesh, const ModelFactoryMesh::FaceHandle & face, EffectiveColour effectiveColour) const +{ + if (smooth.has_value()) { + mesh.property(mesh.smoothFaceProperty, face) = smooth.value(); + } + if (texture.empty()) { + if (effectiveColour.has_value()) { + mesh.set_color(face, effectiveColour->get()); + } } + else { + mesh.set_color(face, {}); + if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) { + auto coords = mf->getTextureCoords(texture); + 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)) { + mesh.set_texcoord2D(heh, *coord++); + } + } + } +} + +Style::EffectiveColour +Style::getColour(const StyleStack & parents) +{ + return getProperty(parents, &Style::colour, [](auto && style) { + return style->colour.a > 0; + }); } bool @@ -42,5 +63,6 @@ Style::persist(Persistence::PersistenceStore & store) } }; - return STORE_HELPER(colour, ColourParser); + return STORE_HELPER(colour, ColourParser) && STORE_MEMBER(smooth) && STORE_MEMBER(texture) + && STORE_MEMBER(textureRotation); } diff --git a/assetFactory/style.h b/assetFactory/style.h index e8fd012..d931f98 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -12,6 +12,7 @@ public: using StyleStack = std::vector<const Style *>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; + using EffectiveColour = std::optional<std::reference_wrapper<const ColourAlpha>>; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const; @@ -27,8 +28,14 @@ public: return {}; } + static EffectiveColour getColour(const StyleStack & parents); + ColourAlpha colour {}; + std::optional<bool> smooth; + std::string texture; + std::string textureRotation; // Multiples of 90deg, no int/enum support protected: bool persist(Persistence::PersistenceStore & store); + void applyStyle(ModelFactoryMesh &, const ModelFactoryMesh::FaceHandle &, EffectiveColour) const; }; diff --git a/assetFactory/textureFragment.cpp b/assetFactory/textureFragment.cpp new file mode 100644 index 0000000..72107a5 --- /dev/null +++ b/assetFactory/textureFragment.cpp @@ -0,0 +1,7 @@ +#include "textureFragment.h" + +bool +TextureFragment::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(path); +} diff --git a/assetFactory/textureFragment.h b/assetFactory/textureFragment.h new file mode 100644 index 0000000..52f2591 --- /dev/null +++ b/assetFactory/textureFragment.h @@ -0,0 +1,14 @@ +#pragma once + +#include "persistence.h" +#include "stdTypeDefs.hpp" + +class TextureFragment : public Persistence::Persistable, public StdTypeDefs<TextureFragment> { +public: + std::string id; + std::string path; + +private: + friend Persistence::SelectionPtrBase<Ptr>; + bool persist(Persistence::PersistenceStore & store) override; +}; diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp new file mode 100644 index 0000000..68a6010 --- /dev/null +++ b/assetFactory/texturePacker.cpp @@ -0,0 +1,80 @@ +#include "texturePacker.h" +#include "collections.hpp" +#include <algorithm> +#include <cstdio> +#include <numeric> +#include <ostream> +#include <set> + +TexturePacker::TexturePacker(std::span<const Image> in) : + inputImages {std::move(in)}, sortedIndexes {vectorOfN(inputImages.size(), size_t {})} +{ + std::sort(sortedIndexes.rbegin(), sortedIndexes.rend(), [this](const auto a, const auto b) { + return area(inputImages[a]) < area(inputImages[b]); + }); +} + +TexturePacker::Result +TexturePacker::pack() const +{ + return pack(minSize()); +} + +TexturePacker::Result +TexturePacker::pack(Size size) const +{ + using Spaces = std::set<Space>; + Spaces spaces {{{}, size}}; + + Positions result(inputImages.size()); + for (const auto & idx : sortedIndexes) { + const auto & image = inputImages[idx]; + if (const auto spaceItr = std::find_if(spaces.begin(), spaces.end(), + [image](const Space & s) { + return image.x <= s.size.x && image.y <= s.size.y; + }); + spaceItr != spaces.end()) { + auto space = *spaceItr; + result[idx] = space.position; + spaces.erase(spaceItr); + if (space.size.x > image.x) { + spaces.emplace(Position {space.position.x + image.x, space.position.y}, + Size {space.size.x - image.x, image.y}); + } + if (space.size.y > image.y) { + spaces.emplace(Position {space.position.x, space.position.y + image.y}, + Size {space.size.x, space.size.y - image.y}); + } + } + else { + if (size.x < size.y) { + return pack({size.x * 2, size.y}); + } + else { + return pack({size.x, size.y * 2}); + } + } + } + + return {result, size}; +} + +TexturePacker::Size +TexturePacker::minSize() const +{ + return std::accumulate(inputImages.begin(), inputImages.end(), Size {1}, [](Size size, const Image & i) { + while (size.x < i.x) { + size.x *= 2; + } + while (size.y < i.y) { + size.y *= 2; + } + return size; + }); +} + +decltype(TexturePacker::Size::x) +TexturePacker::area(const Size & size) +{ + return size.x * size.y; +} diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h new file mode 100644 index 0000000..ca0d67a --- /dev/null +++ b/assetFactory/texturePacker.h @@ -0,0 +1,41 @@ +#pragma once + +#include <glm/vec2.hpp> +#include <span> +#include <vector> + +class TexturePacker { +public: + using Position = glm::uvec2; + using Size = glm::uvec2; + + struct Area { +#ifndef __cpp_aggregate_paren_init + constexpr Area(Position p, Size s) : position {std::move(p)}, size {std::move(s)} { } +#endif + + Position position; + Size size; + bool + operator<(const Area & other) const + { + return area(size) < area(other.size); + } + }; + using Image = Size; + using Space = Area; + using Positions = std::vector<Position>; + using Result = std::pair<Positions, Size>; + + TexturePacker(std::span<const Image>); + + Result pack(Size) const; + Result pack() const; + + Size minSize() const; + static decltype(Size::x) area(const Size & size); + +private: + std::span<const Image> inputImages; + std::vector<size_t> sortedIndexes; +}; |