diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-07 23:55:09 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-07 23:55:09 +0100 |
commit | 2bcbb86db4061e32005adea8806e4ac552691bcf (patch) | |
tree | 28a7142314632b695f8e278798823b1b7041e243 | |
parent | Add timeouts to asset factory tests (diff) | |
download | ilt-2bcbb86db4061e32005adea8806e4ac552691bcf.tar.bz2 ilt-2bcbb86db4061e32005adea8806e4ac552691bcf.tar.xz ilt-2bcbb86db4061e32005adea8806e4ac552691bcf.zip |
Extend face controller to support splitting a face along a plane
Individual parts of the splits faces can then be styled separately
-rw-r--r-- | assetFactory/faceController.cpp | 83 | ||||
-rw-r--r-- | assetFactory/faceController.h | 18 | ||||
-rw-r--r-- | res/brush47.xml | 6 |
3 files changed, 98 insertions, 9 deletions
diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 25bf833..28812d8 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -2,6 +2,7 @@ #include "collections.hpp" #include "maths.h" #include "modelFactoryMesh.h" +#include <glm/gtx/intersect.hpp> void FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & names, @@ -30,24 +31,29 @@ void FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const { - const auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (controlledFaces.empty()) { - throw std::runtime_error("Named face(s) do not exist: " + name); - } + auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (!type.empty()) { + if (!type.empty() || !splits.empty()) { faces.erase(name); } - for (const auto & [faceName, faceHandle] : controlledFaces) { + 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") { - auto newFaces = extrude(mesh, faceName, faceHandle); + 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)); } - applyStyle(mesh, parents + this, faceHandle); + else { + applyStyle(mesh, parents + this, faceHandle); + } } } @@ -88,9 +94,70 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O return newFaces; } +enum class PlaneRelation { Above, Below, On }; +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) { + const auto d = glm::dot(split.normal, mesh.point(vh) - split.origin); + return std::make_pair(vh, d < 0.f ? PlaneRelation::Below : d > 0.f ? PlaneRelation::Above : PlaneRelation::On); + }; + // 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 ((current.second == PlaneRelation::Above && next.second == PlaneRelation::Below) + || (current.second == PlaneRelation::Below && next.second == PlaneRelation::Above)) { + const auto origin = mesh.point(current.first), dir = glm::normalize(mesh.point(next.first) - origin); + + float dist {}; + glm::intersectRayPlane(origin, dir, split.origin, split.normal, dist); + const auto newv = mesh.add_vertex(origin + (dir * dist)); + auto where = vertexRelations.begin(); + ++curIdx; + std::advance(where, curIdx); + vertexRelations.emplace(where, newv, PlaneRelation::On); + } + } + // 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(), PlaneRelation::Above); + filterVertices(out.back(), 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) && 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 851292a..91860f4 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -10,7 +10,22 @@ class FaceController : public Mutation, public Style, public Persistence::Persistable { public: + class Split : public Persistable { + public: + std::string id; + glm::vec3 origin, normal; + + 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 & names, Shape::CreatedFaces & faces) const; @@ -18,6 +33,7 @@ public: std::string id; std::string type; FaceControllers faceControllers; + Splits splits; private: friend Persistence::SelectionPtrBase<std::unique_ptr<FaceController>>; @@ -33,4 +49,6 @@ private: static std::string getAdjacentFaceName(const ModelFactoryMesh & mesh, const std::span<const OpenMesh::FaceHandle> ofrange, OpenMesh::FaceHandle nf); 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/res/brush47.xml b/res/brush47.xml index f74cb29..20c43c8 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -70,7 +70,11 @@ <face id="front back" colour="#e1eff3"/> <face id="top" type="extrude" scale="0.5,0.85,0" smooth="true" position="0,0,0.575" colour="#aeb0b0"> <face id="top" texture="roofTopWithVents"/> - <face id="right left" texture="roofSideWithVents"/> + <face id="right left"> + <split id="frontcorner" origin="0,7.90704,0" normal="0,1,0"/> + <split id="backcorner" origin="0,-7.90704,0" normal="0,-1,0"/> + <face id="right left" texture="roofSideWithVents"/> + </face> </face> </face> </face> |