diff options
Diffstat (limited to 'assetFactory')
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, ¢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.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 {}; + }; +}; |