summaryrefslogtreecommitdiff
path: root/assetFactory
diff options
context:
space:
mode:
Diffstat (limited to 'assetFactory')
-rw-r--r--assetFactory/asset.cpp34
-rw-r--r--assetFactory/asset.h37
-rw-r--r--assetFactory/assetFactory.cpp85
-rw-r--r--assetFactory/assetFactory.h30
-rw-r--r--assetFactory/cuboid.cpp29
-rw-r--r--assetFactory/cuboid.h8
-rw-r--r--assetFactory/cylinder.cpp46
-rw-r--r--assetFactory/cylinder.h8
-rw-r--r--assetFactory/faceController.cpp91
-rw-r--r--assetFactory/faceController.h31
-rw-r--r--assetFactory/factoryMesh.cpp40
-rw-r--r--assetFactory/factoryMesh.h18
-rw-r--r--assetFactory/modelFactoryMesh.cpp15
-rw-r--r--assetFactory/modelFactoryMesh.h39
-rw-r--r--assetFactory/modelFactoryMesh_fwd.h3
-rw-r--r--assetFactory/mutation.cpp10
-rw-r--r--assetFactory/mutation.h14
-rw-r--r--assetFactory/object.cpp23
-rw-r--r--assetFactory/object.h26
-rw-r--r--assetFactory/plane.cpp15
-rw-r--r--assetFactory/plane.h8
-rw-r--r--assetFactory/shape.cpp17
-rw-r--r--assetFactory/shape.h25
-rw-r--r--assetFactory/style.cpp46
-rw-r--r--assetFactory/style.h34
-rw-r--r--assetFactory/use.cpp33
-rw-r--r--assetFactory/use.h27
27 files changed, 792 insertions, 0 deletions
diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp
new file mode 100644
index 0000000..3ab2f1c
--- /dev/null
+++ b/assetFactory/asset.cpp
@@ -0,0 +1,34 @@
+#include "asset.h"
+
+bool
+Asset::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_MEMBER(id) && STORE_MEMBER(name);
+}
+
+Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) :
+ Persistence::SelectionPtrBase<FactoryMesh::Ptr> {fmesh}, out {m} { }
+
+void
+Asset::MeshConstruct::endObject(Persistence::Stack & stk)
+{
+ out = fmesh->createMesh();
+ Persistence::SelectionPtrBase<FactoryMesh::Ptr>::endObject(stk);
+}
+
+Asset::MeshArrayConstruct::MeshArrayConstruct(std::span<Mesh::Ptr> m) :
+ Persistence::SelectionPtrBase<FactoryMesh::Ptr> {fmesh}, out {m}
+{
+}
+
+void
+Asset::MeshArrayConstruct::endObject(Persistence::Stack & stk)
+{
+ for (auto & outMesh : out) {
+ if (!outMesh) {
+ outMesh = fmesh->createMesh();
+ break;
+ }
+ }
+ Persistence::SelectionPtrBase<FactoryMesh::Ptr>::endObject(stk);
+}
diff --git a/assetFactory/asset.h b/assetFactory/asset.h
new file mode 100644
index 0000000..e3318e4
--- /dev/null
+++ b/assetFactory/asset.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "factoryMesh.h"
+#include "persistence.h"
+#include <stdTypeDefs.hpp>
+
+class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> {
+public:
+ std::string id;
+ std::string name;
+
+protected:
+ struct MeshConstruct : public Persistence::SelectionPtrBase<FactoryMesh::Ptr> {
+ using Persistence::SelectionPtrBase<FactoryMesh::Ptr>::setValue;
+
+ MeshConstruct(Mesh::Ptr & m);
+
+ void endObject(Persistence::Stack & stk) override;
+
+ FactoryMesh::Ptr fmesh;
+ Mesh::Ptr & out;
+ };
+
+ struct MeshArrayConstruct : public Persistence::SelectionPtrBase<FactoryMesh::Ptr> {
+ using Persistence::SelectionPtrBase<FactoryMesh::Ptr>::setValue;
+
+ MeshArrayConstruct(std::span<Mesh::Ptr> m);
+
+ void endObject(Persistence::Stack & stk) override;
+
+ FactoryMesh::Ptr fmesh;
+ std::span<Mesh::Ptr> out;
+ };
+
+ friend Persistence::SelectionPtrBase<std::shared_ptr<Asset>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+};
diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp
new file mode 100644
index 0000000..f5fc2b3
--- /dev/null
+++ b/assetFactory/assetFactory.cpp
@@ -0,0 +1,85 @@
+#include "assetFactory.h"
+#include "collections.hpp"
+#include "cuboid.h"
+#include "cylinder.h"
+#include "modelFactoryMesh_fwd.h"
+#include "object.h"
+#include "plane.h"
+#include "saxParse-persistence.h"
+#include <filesystem.h>
+
+AssetFactory::AssetFactory() :
+ shapes {
+ {"plane", std::make_shared<Plane>()},
+ {"cuboid", std::make_shared<Cuboid>()},
+ {"cylinder", std::make_shared<Cylinder>()},
+ },
+ colours {parseX11RGB("/usr/share/X11/rgb.txt")}
+{
+}
+
+std::shared_ptr<AssetFactory>
+AssetFactory::loadXML(const std::filesystem::path & filename)
+{
+ filesystem::FileStar file {filename.c_str(), "r"};
+ return Persistence::SAXParsePersistence {}.loadState<std::shared_ptr<AssetFactory>>(file);
+}
+
+AssetFactory::Colours
+AssetFactory::parseX11RGB(const char * path)
+{
+ filesystem::FileStar rgb {path, "r"};
+ Colours out;
+ Colour colour;
+ char inname[BUFSIZ];
+ while (fscanf(rgb, "%f %f %f %[^\n\r]s", &colour.r, &colour.g, &colour.b, inname) == 4) {
+ std::string name {inname};
+ normalizeColourName(name);
+ out.emplace(std::move(name), colour / 255.f);
+ }
+ return out;
+}
+
+void
+AssetFactory::normalizeColourName(std::string & name)
+{
+ std::erase_if(name, ::isblank);
+ name *= [l = std::locale {}](auto & ch) {
+ ch = std::tolower(ch, l);
+ };
+}
+
+AssetFactory::ColourAlpha
+AssetFactory::parseColour(std::string_view in) const
+{
+ if (in.empty()) {
+ throw std::runtime_error("Empty colour specification");
+ }
+ if (in[0] == '#') {
+ if (in.length() > 9 || in.length() % 2 == 0) {
+ throw std::runtime_error("Invalid hex colour specification");
+ }
+ ColourAlpha out {0, 0, 0, 1};
+ std::generate_n(&out.r, (in.length() - 1) / 2, [in = in.data() + 1]() mutable {
+ uint8_t channel;
+ std::from_chars(in, in + 2, channel, 16);
+ in += 2;
+ return static_cast<float>(channel) / 255.f;
+ });
+ return out;
+ }
+ if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) {
+ if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) {
+ return {colour->second, 1};
+ }
+ }
+ throw std::runtime_error("No such asset factory colour");
+}
+bool
+AssetFactory::persist(Persistence::PersistenceStore & store)
+{
+ using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>;
+ using MapAssets = Persistence::MapByMember<Assets>;
+ return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects)
+ && STORE_NAME_HELPER("asset", assets, MapAssets);
+}
diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h
new file mode 100644
index 0000000..b47d408
--- /dev/null
+++ b/assetFactory/assetFactory.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "asset.h"
+#include "persistence.h"
+#include "shape.h"
+#include <filesystem>
+
+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 Colour = glm::vec3;
+ using ColourAlpha = glm::vec4;
+ using Colours = std::map<std::string, Colour, std::less<>>;
+
+ AssetFactory();
+ [[nodiscard]] static std::shared_ptr<AssetFactory> loadXML(const std::filesystem::path &);
+ [[nodiscard]] ColourAlpha parseColour(std::string_view) const;
+
+ Shapes shapes;
+ Assets assets;
+ Colours colours;
+
+ static Colours parseX11RGB(const char * rgbtxtpath);
+ static void normalizeColourName(std::string &);
+
+private:
+ friend Persistence::SelectionPtrBase<std::shared_ptr<AssetFactory>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+};
diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp
new file mode 100644
index 0000000..24fe4a4
--- /dev/null
+++ b/assetFactory/cuboid.cpp
@@ -0,0 +1,29 @@
+#include "cuboid.h"
+#include "modelFactoryMesh.h"
+
+Cuboid::CreatedFaces
+Cuboid::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const
+{
+ static constexpr std::array<glm::vec3, 8> VERTICES {{
+ // bottom
+ {n, n, z},
+ {n, y, z},
+ {y, y, z},
+ {y, n, z},
+ // top
+ {y, n, o},
+ {y, y, o},
+ {n, y, o},
+ {n, n, o},
+ }};
+
+ const auto vhs = addMutatedToMesh(mesh, VERTICES, mutation);
+ 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("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/cuboid.h b/assetFactory/cuboid.h
new file mode 100644
index 0000000..5a4072a
--- /dev/null
+++ b/assetFactory/cuboid.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "shape.h"
+
+class Cuboid : public Shape {
+public:
+ CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const override;
+};
diff --git a/assetFactory/cylinder.cpp b/assetFactory/cylinder.cpp
new file mode 100644
index 0000000..cf0dbfb
--- /dev/null
+++ b/assetFactory/cylinder.cpp
@@ -0,0 +1,46 @@
+#include "cylinder.h"
+#include "maths.h"
+#include "modelFactoryMesh.h"
+
+Cylinder::CreatedFaces
+Cylinder::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const
+{
+ const glm::vec2 scale {std::accumulate(&mutation[0][0], &mutation[0][3], 0.f),
+ std::accumulate(&mutation[1][0], &mutation[1][3], 0.f)};
+ const unsigned int P = static_cast<unsigned int>(std::round(15.F * std::sqrt(glm::length(scale))));
+ std::vector<OpenMesh::VertexHandle> bottom(P), top(P);
+ std::generate_n(bottom.begin(), P, [a = 0.f, step = two_pi / static_cast<float>(P), &mesh, &mutation]() mutable {
+ const auto xy = sincosf(a += step) * .5F;
+ const auto xyz = (xy ^ 0) % mutation;
+ return mesh.add_vertex({xyz.x, xyz.y, xyz.z});
+ });
+ std::generate_n(top.begin(), P, [a = 0.f, step = two_pi / static_cast<float>(P), &mesh, &mutation]() mutable {
+ const auto xy = sincosf(a -= step) * .5F;
+ const auto xyz = (xy ^ 1) % mutation;
+ return mesh.add_vertex({xyz.x, xyz.y, xyz.z});
+ });
+ CreatedFaces surface;
+ std::generate_n(std::inserter(surface, surface.end()), P,
+ [a = 0.f, step = two_pi / static_cast<float>(P), &mesh, &mutation]() mutable {
+ const auto xy1 = sincosf(a) * .5F;
+ const auto xy2 = sincosf(a -= step) * .5F;
+ const auto xyz1b = (xy1 ^ 0) % mutation;
+ const auto xyz2b = (xy2 ^ 0) % mutation;
+ const auto xyz1t = (xy1 ^ 1) % mutation;
+ const auto xyz2t = (xy2 ^ 1) % mutation;
+ return mesh.add_namedFace("edge",
+ {
+ mesh.add_vertex({xyz1b.x, xyz1b.y, xyz1b.z}),
+ mesh.add_vertex({xyz2b.x, xyz2b.y, xyz2b.z}),
+ mesh.add_vertex({xyz2t.x, xyz2t.y, xyz2t.z}),
+ mesh.add_vertex({xyz1t.x, xyz1t.y, xyz1t.z}),
+ });
+ });
+ for (const auto & [name, face] : surface) {
+ mesh.property(mesh.smoothFaceProperty, face) = true;
+ }
+ surface.insert(mesh.add_namedFace("bottom", bottom));
+ surface.insert(mesh.add_namedFace("top", top));
+
+ return surface;
+}
diff --git a/assetFactory/cylinder.h b/assetFactory/cylinder.h
new file mode 100644
index 0000000..65ca5e5
--- /dev/null
+++ b/assetFactory/cylinder.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "shape.h"
+
+class Cylinder : public Shape {
+public:
+ CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const override;
+};
diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp
new file mode 100644
index 0000000..b485c80
--- /dev/null
+++ b/assetFactory/faceController.cpp
@@ -0,0 +1,91 @@
+#include "faceController.h"
+#include "collections.hpp"
+#include "maths.h"
+#include "modelFactoryMesh.h"
+
+void
+FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name,
+ 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 {};
+ };
+
+ const auto controlledFaces {materializeRange(faces.equal_range(name))};
+ if (controlledFaces.empty()) {
+ throw std::runtime_error("Named face(s) do not exist: " + name);
+ }
+
+ if (!type.empty()) {
+ const auto mutation = getMatrix();
+ 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, &centre](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.x, p.y, p.z});
+ });
+ // 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);
+ }
+ }
+ }
+ else {
+ for (const auto & cf : controlledFaces) {
+ applyStyle(mesh, parents + this, cf.second);
+ }
+ }
+}
+
+bool
+FaceController::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && STORE_MEMBER(smooth)
+ && STORE_MEMBER(scale) && STORE_MEMBER(position) && STORE_MEMBER(rotation)
+ && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember<FaceControllers>);
+}
diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h
new file mode 100644
index 0000000..10a226a
--- /dev/null
+++ b/assetFactory/faceController.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "modelFactoryMesh_fwd.h"
+#include "mutation.h"
+#include "persistence.h"
+#include "shape.h"
+#include "style.h"
+#include <map>
+#include <string>
+
+class FaceController : public Mutation, public Style, public Persistence::Persistable {
+public:
+ using FaceControllers = std::map<std::string, std::unique_ptr<FaceController>>;
+
+ void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name,
+ Shape::CreatedFaces & faces) const;
+
+ std::string id;
+ std::string type;
+ bool smooth {false};
+ FaceControllers faceControllers;
+
+private:
+ friend Persistence::SelectionPtrBase<std::unique_ptr<FaceController>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+ std::string
+ getId() const override
+ {
+ return {};
+ };
+};
diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp
new file mode 100644
index 0000000..0cfed85
--- /dev/null
+++ b/assetFactory/factoryMesh.cpp
@@ -0,0 +1,40 @@
+#include "factoryMesh.h"
+#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, glm::identity<Mutation::Matrix>());
+ }
+ mesh.garbage_collection();
+
+ mesh.triangulate();
+ mesh.update_face_normals();
+ mesh.update_vertex_normals();
+ std::vector<Vertex> vertices;
+ 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);
+ }
+ }
+ return std::make_shared<Mesh>(vertices, vectorOfN(vertices.size()));
+}
+
+bool
+FactoryMesh::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(size)
+ && STORE_NAME_HELPER("use", uses, Persistence::Appender<Use::Collection>);
+}
diff --git a/assetFactory/factoryMesh.h b/assetFactory/factoryMesh.h
new file mode 100644
index 0000000..bbeb870
--- /dev/null
+++ b/assetFactory/factoryMesh.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "gfx/models/mesh.h"
+#include "stdTypeDefs.hpp"
+#include "use.h"
+
+class FactoryMesh : public Persistence::Persistable, public StdTypeDefs<FactoryMesh> {
+public:
+ Mesh::Ptr createMesh() const;
+
+ std::string id;
+ glm::vec3 size;
+ Use::Collection uses;
+
+private:
+ friend Persistence::SelectionPtrBase<std::shared_ptr<FactoryMesh>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+};
diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp
new file mode 100644
index 0000000..f9ee6a0
--- /dev/null
+++ b/assetFactory/modelFactoryMesh.cpp
@@ -0,0 +1,15 @@
+#include "modelFactoryMesh.h"
+
+ModelFactoryMesh::ModelFactoryMesh()
+{
+ add_property(smoothFaceProperty);
+ add_property(nameFaceProperty);
+}
+
+std::pair<std::string, OpenMesh::FaceHandle>
+ModelFactoryMesh::add_namedFace(std::string name, std::vector<OpenMesh::VertexHandle> p)
+{
+ const auto handle = add_face(std::move(p));
+ property(nameFaceProperty, handle) = name;
+ return std::make_pair(name, handle);
+}
diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h
new file mode 100644
index 0000000..258913b
--- /dev/null
+++ b/assetFactory/modelFactoryMesh.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "modelFactoryMesh_fwd.h"
+#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
+#include <OpenMesh/Core/Mesh/Traits.hh>
+#include <glm/geometric.hpp>
+#include <glm/vec3.hpp>
+#include <glm/vec4.hpp>
+
+namespace OpenMesh {
+ template<typename Scalar, int DIM> struct glmvec : public VectorT<Scalar, DIM> {
+ using VectorT<Scalar, DIM>::VectorT;
+ glmvec(const VectorT<Scalar, DIM> & v) : VectorT<Scalar, DIM> {v} { }
+ operator glm::vec<DIM, Scalar>() const
+ {
+ glm::vec<DIM, Scalar> out;
+ std::copy(this->begin(), this->end(), &out[0]);
+ return out;
+ }
+ };
+}
+
+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);
+ using Point = OpenMesh::glmvec<float, 3>;
+ using Normal = OpenMesh::glmvec<float, 3>;
+ using Color = glm::vec4;
+};
+
+struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTraits> {
+ ModelFactoryMesh();
+
+ OpenMesh::FPropHandleT<bool> smoothFaceProperty;
+ OpenMesh::FPropHandleT<std::string> nameFaceProperty;
+
+ std::pair<std::string, OpenMesh::FaceHandle> add_namedFace(std::string name, std::vector<OpenMesh::VertexHandle> p);
+};
diff --git a/assetFactory/modelFactoryMesh_fwd.h b/assetFactory/modelFactoryMesh_fwd.h
new file mode 100644
index 0000000..ac10f2d
--- /dev/null
+++ b/assetFactory/modelFactoryMesh_fwd.h
@@ -0,0 +1,3 @@
+#pragma once
+
+struct ModelFactoryMesh;
diff --git a/assetFactory/mutation.cpp b/assetFactory/mutation.cpp
new file mode 100644
index 0000000..21d2a24
--- /dev/null
+++ b/assetFactory/mutation.cpp
@@ -0,0 +1,10 @@
+#include "mutation.h"
+#include <glm/gtx/transform.hpp>
+#include <maths.h>
+
+Mutation::Matrix
+Mutation::getMatrix() const
+{
+ return glm::translate(glm::identity<Matrix>(), position) * rotate_ypr(rotation)
+ * glm::scale(glm::identity<Matrix>(), scale);
+}
diff --git a/assetFactory/mutation.h b/assetFactory/mutation.h
new file mode 100644
index 0000000..440fab0
--- /dev/null
+++ b/assetFactory/mutation.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <glm/mat4x4.hpp>
+#include <glm/vec3.hpp>
+
+struct Mutation {
+ using Matrix = glm::mat4;
+
+ Matrix getMatrix() const;
+
+ glm::vec3 position {};
+ glm::vec3 rotation {};
+ glm::vec3 scale {1};
+};
diff --git a/assetFactory/object.cpp b/assetFactory/object.cpp
new file mode 100644
index 0000000..ae5a301
--- /dev/null
+++ b/assetFactory/object.cpp
@@ -0,0 +1,23 @@
+#include "object.h"
+#include <algorithm>
+
+Object::Object(std::string i) : id {std::move(i)} { }
+
+Object::CreatedFaces
+Object::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const
+{
+ CreatedFaces faces;
+ for (const auto & use : uses) {
+ auto useFaces = use->createMesh(mesh, mutation);
+ std::transform(useFaces.begin(), useFaces.end(), std::inserter(faces, faces.end()), [this](auto && face) {
+ return std::make_pair(id + ":" + face.first, std::move(face.second));
+ });
+ }
+ return faces;
+}
+
+bool
+Object::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_TYPE && STORE_MEMBER(id) && STORE_NAME_HELPER("use", uses, Persistence::Appender<Use::Collection>);
+}
diff --git a/assetFactory/object.h b/assetFactory/object.h
new file mode 100644
index 0000000..f3726c7
--- /dev/null
+++ b/assetFactory/object.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "persistence.h"
+#include "shape.h"
+#include "stdTypeDefs.hpp"
+#include "use.h"
+
+class Object : public StdTypeDefs<Object>, public Shape, public Persistence::Persistable {
+public:
+ Object() = default;
+ Object(std::string i);
+
+ CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const override;
+
+ Use::Collection uses;
+ std::string id;
+
+private:
+ friend Persistence::SelectionPtrBase<std::shared_ptr<Object>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+ std::string
+ getId() const override
+ {
+ return id;
+ };
+};
diff --git a/assetFactory/plane.cpp b/assetFactory/plane.cpp
new file mode 100644
index 0000000..563c4e9
--- /dev/null
+++ b/assetFactory/plane.cpp
@@ -0,0 +1,15 @@
+#include "plane.h"
+#include "modelFactoryMesh.h"
+
+Plane::CreatedFaces
+Plane::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const
+{
+ static constexpr std::array<glm::vec3, 4> VERTICES {{
+ {n, n, z},
+ {y, n, z},
+ {y, y, z},
+ {n, y, z},
+ }};
+
+ return {mesh.add_namedFace("plane", addMutatedToMesh(mesh, VERTICES, mutation))};
+}
diff --git a/assetFactory/plane.h b/assetFactory/plane.h
new file mode 100644
index 0000000..5e93ee4
--- /dev/null
+++ b/assetFactory/plane.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "shape.h"
+
+class Plane : public Shape {
+public:
+ CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const override;
+};
diff --git a/assetFactory/shape.cpp b/assetFactory/shape.cpp
new file mode 100644
index 0000000..f6e55e8
--- /dev/null
+++ b/assetFactory/shape.cpp
@@ -0,0 +1,17 @@
+#include "shape.h"
+#include "gfx/models/vertex.hpp"
+#include "maths.h"
+#include "modelFactoryMesh.h"
+#include "shape.h"
+
+std::vector<OpenMesh::VertexHandle>
+Shape::addMutatedToMesh(
+ ModelFactoryMesh & mesh, const std::span<const glm::vec3> vertices, const Mutation::Matrix & mutation)
+{
+ std::vector<OpenMesh::VertexHandle> vhs;
+ std::transform(vertices.begin(), vertices.end(), std::back_inserter(vhs), [&mesh, &mutation](const auto & v) {
+ const auto p = v % mutation;
+ return mesh.add_vertex({p.x, p.y, p.z});
+ });
+ return vhs;
+}
diff --git a/assetFactory/shape.h b/assetFactory/shape.h
new file mode 100644
index 0000000..5a2b59c
--- /dev/null
+++ b/assetFactory/shape.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "modelFactoryMesh_fwd.h"
+#include "mutation.h"
+#include "stdTypeDefs.hpp"
+#include <OpenMesh/Core/Mesh/Handles.hh>
+#include <map>
+#include <span>
+#include <string>
+
+class Vertex;
+
+class Shape : public StdTypeDefs<Shape> {
+public:
+ using CreatedFaces = std::multimap<std::string, OpenMesh::FaceHandle>;
+
+ static constexpr float z {}, y {.5}, n {-y}, o {1};
+
+ virtual ~Shape() = default;
+
+ virtual CreatedFaces createMesh(ModelFactoryMesh &, const Mutation::Matrix & mutation) const = 0;
+
+ static std::vector<OpenMesh::VertexHandle> addMutatedToMesh(
+ ModelFactoryMesh & mesh, const std::span<const glm::vec3> vertices, const Mutation::Matrix & mutation);
+};
diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp
new file mode 100644
index 0000000..fc5c34e
--- /dev/null
+++ b/assetFactory/style.cpp
@@ -0,0 +1,46 @@
+#include "style.h"
+#include "assetFactory.h"
+
+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());
+ }
+ }
+}
+
+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());
+ }
+}
+
+bool
+Style::persist(Persistence::PersistenceStore & store)
+{
+ struct ColourParser : public Persistence::SelectionV<ColourAlpha> {
+ using Persistence::SelectionV<ColourAlpha>::SelectionV;
+ using Persistence::SelectionV<ColourAlpha>::setValue;
+ void
+ setValue(std::string && str) override
+ {
+ if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) {
+ v = mf->parseColour(str);
+ }
+ }
+ };
+
+ return STORE_HELPER(colour, ColourParser);
+}
diff --git a/assetFactory/style.h b/assetFactory/style.h
new file mode 100644
index 0000000..e8fd012
--- /dev/null
+++ b/assetFactory/style.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "modelFactoryMesh.h"
+#include "persistence.h"
+#include "shape.h"
+#include <optional>
+#include <string>
+#include <utility>
+
+class Style {
+public:
+ using StyleStack = std::vector<const Style *>;
+ using Colour = glm::vec3;
+ using ColourAlpha = glm::vec4;
+
+ void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const;
+ void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const;
+
+ template<typename T>
+ static std::optional<std::reference_wrapper<const T>>
+ getProperty(const StyleStack & parents, T Style::*member, auto && test)
+ {
+ if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), std::forward<decltype(test)>(test));
+ itr != parents.rend()) {
+ return (*itr)->*member;
+ }
+ return {};
+ }
+
+ ColourAlpha colour {};
+
+protected:
+ bool persist(Persistence::PersistenceStore & store);
+};
diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp
new file mode 100644
index 0000000..708e310
--- /dev/null
+++ b/assetFactory/use.cpp
@@ -0,0 +1,33 @@
+#include "use.h"
+#include "assetFactory.h"
+
+Shape::CreatedFaces
+Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const
+{
+ auto faces = type->createMesh(mesh, mutation * getMatrix());
+ applyStyle(mesh, {this}, faces);
+ for (const auto & [name, faceController] : faceControllers) {
+ faceController->apply(mesh, {this}, name, faces);
+ }
+ return faces;
+}
+
+struct Lookup : public Persistence::SelectionV<Shape::CPtr> {
+ using Persistence::SelectionV<Shape::CPtr>::SelectionV;
+ using Persistence::SelectionV<Shape::CPtr>::setValue;
+ void
+ setValue(std::string && str) override
+ {
+ if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) {
+ v = mf->shapes.at(str);
+ }
+ }
+};
+
+bool
+Use::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_TYPE && STORE_HELPER(type, Lookup) && STORE_MEMBER(position) && STORE_MEMBER(scale)
+ && STORE_MEMBER(rotation) && Style::persist(store)
+ && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember<FaceControllers>);
+}
diff --git a/assetFactory/use.h b/assetFactory/use.h
new file mode 100644
index 0000000..5e4c35f
--- /dev/null
+++ b/assetFactory/use.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "faceController.h"
+#include "modelFactoryMesh_fwd.h"
+#include "persistence.h"
+#include "shape.h"
+#include "stdTypeDefs.hpp"
+#include "style.h"
+
+class Use : public StdTypeDefs<Use>, public Mutation, public Style, public Persistence::Persistable {
+public:
+ using FaceControllers = std::map<std::string, std::unique_ptr<FaceController>>;
+
+ Shape::CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const;
+
+ Shape::CPtr type;
+ FaceControllers faceControllers;
+
+private:
+ friend Persistence::SelectionPtrBase<std::shared_ptr<Use>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+ std::string
+ getId() const override
+ {
+ return {};
+ };
+};