summaryrefslogtreecommitdiff
path: root/assetFactory
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-04-07 23:55:09 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2023-04-07 23:55:09 +0100
commit2bcbb86db4061e32005adea8806e4ac552691bcf (patch)
tree28a7142314632b695f8e278798823b1b7041e243 /assetFactory
parentAdd timeouts to asset factory tests (diff)
downloadilt-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
Diffstat (limited to 'assetFactory')
-rw-r--r--assetFactory/faceController.cpp83
-rw-r--r--assetFactory/faceController.h18
2 files changed, 93 insertions, 8 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 &current = 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;
};