From 6c3fab53711a8f65734a9beade1b4b3e9469d529 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 15 Feb 2023 02:23:16 +0000 Subject: Add OpenMeshCore library --- Jamroot.jam | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jamroot.jam b/Jamroot.jam index 14703eb..16c22ac 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -13,6 +13,7 @@ pkg-config.import glew ; pkg-config.import freetype2 ; pkg-config.import glib-2.0 ; lib pthread ; +lib OpenMeshCore ; variant coverage : debug ; project : requirements @@ -112,6 +113,7 @@ lib ilt : freetype2 glib-2.0 pthread + OpenMeshCore : : . lib -- cgit v1.2.3 From 715d4879fdd096ac82367984fdb22117d48737a4 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 15 Feb 2023 02:26:06 +0000 Subject: First cut of the model factory and the hardcoded Brush 47 model Requires temporary change to the fragment shader to hardcode some visible colour to the model --- assetFactory/assetFactoryConfig.h | 28 +++++++ assetFactory/assetFactoryConfig_fwd.h | 8 ++ assetFactory/cuboid.cpp | 29 +++++++ assetFactory/cuboid.h | 8 ++ assetFactory/cylinder.cpp | 43 ++++++++++ assetFactory/cylinder.h | 8 ++ assetFactory/faceController.cpp | 53 ++++++++++++ assetFactory/faceController.h | 20 +++++ assetFactory/factoryMesh.cpp | 27 ++++++ assetFactory/factoryMesh.h | 14 ++++ assetFactory/modelFactory.cpp | 14 ++++ assetFactory/modelFactory.h | 12 +++ assetFactory/mutation.cpp | 10 +++ assetFactory/mutation.h | 14 ++++ assetFactory/object.cpp | 17 ++++ assetFactory/object.h | 15 ++++ assetFactory/plane.cpp | 15 ++++ assetFactory/plane.h | 8 ++ assetFactory/shape.cpp | 17 ++++ assetFactory/shape.h | 25 ++++++ assetFactory/use.cpp | 11 +++ assetFactory/use.h | 17 ++++ test/Jamfile.jam | 1 + test/test-modelFactory.cpp | 150 ++++++++++++++++++++++++++++++++++ 24 files changed, 564 insertions(+) create mode 100644 assetFactory/assetFactoryConfig.h create mode 100644 assetFactory/assetFactoryConfig_fwd.h create mode 100644 assetFactory/cuboid.cpp create mode 100644 assetFactory/cuboid.h create mode 100644 assetFactory/cylinder.cpp create mode 100644 assetFactory/cylinder.h create mode 100644 assetFactory/faceController.cpp create mode 100644 assetFactory/faceController.h create mode 100644 assetFactory/factoryMesh.cpp create mode 100644 assetFactory/factoryMesh.h create mode 100644 assetFactory/modelFactory.cpp create mode 100644 assetFactory/modelFactory.h create mode 100644 assetFactory/mutation.cpp create mode 100644 assetFactory/mutation.h create mode 100644 assetFactory/object.cpp create mode 100644 assetFactory/object.h create mode 100644 assetFactory/plane.cpp create mode 100644 assetFactory/plane.h create mode 100644 assetFactory/shape.cpp create mode 100644 assetFactory/shape.h create mode 100644 assetFactory/use.cpp create mode 100644 assetFactory/use.h create mode 100644 test/test-modelFactory.cpp diff --git a/assetFactory/assetFactoryConfig.h b/assetFactory/assetFactoryConfig.h new file mode 100644 index 0000000..171b81a --- /dev/null +++ b/assetFactory/assetFactoryConfig.h @@ -0,0 +1,28 @@ +#pragma once + +#include "assetFactoryConfig_fwd.h" +#include +#include +#include +#include + +namespace OpenMesh { + template struct glmvec : public VectorT { + using VectorT::VectorT; + glmvec(const VectorT & v) : VectorT {v} { } + operator glm::vec() const + { + glm::vec out; + std::copy(this->begin(), this->end(), &out[0]); + return out; + } + }; +} + +struct ModelFactoryTraits : public OpenMesh::DefaultTraits { + FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + EdgeAttributes(OpenMesh::Attributes::Status); + VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + using Point = OpenMesh::glmvec; + using Normal = OpenMesh::glmvec; +}; diff --git a/assetFactory/assetFactoryConfig_fwd.h b/assetFactory/assetFactoryConfig_fwd.h new file mode 100644 index 0000000..28489ff --- /dev/null +++ b/assetFactory/assetFactoryConfig_fwd.h @@ -0,0 +1,8 @@ +#pragma once + +namespace OpenMesh { + template class PolyMesh_ArrayKernelT; +} +struct ModelFactoryTraits; + +using ModelFactoryMesh = OpenMesh::PolyMesh_ArrayKernelT; diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp new file mode 100644 index 0000000..90b2108 --- /dev/null +++ b/assetFactory/cuboid.cpp @@ -0,0 +1,29 @@ +#include "cuboid.h" +#include "assetFactoryConfig.h" + +Cuboid::CreatedFaces +Cuboid::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const +{ + static constexpr std::array 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 { + {"top", mesh.add_face({vhs[4], vhs[5], vhs[6], vhs[7]})}, + {"bottom", mesh.add_face({vhs[0], vhs[1], vhs[2], vhs[3]})}, + {"left", mesh.add_face({vhs[0], vhs[7], vhs[6], vhs[1]})}, + {"right", mesh.add_face({vhs[2], vhs[5], vhs[4], vhs[3]})}, + {"front", mesh.add_face({vhs[0], vhs[3], vhs[4], vhs[7]})}, + {"back", mesh.add_face({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..66d456d --- /dev/null +++ b/assetFactory/cylinder.cpp @@ -0,0 +1,43 @@ +#include "cylinder.h" +#include "assetFactoryConfig.h" +#include "maths.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(std::round(15.F * std::sqrt(glm::length(scale)))); + std::vector bottom(P), top(P); + std::generate_n(bottom.begin(), P, [a = 0.f, step = two_pi / static_cast(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(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(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 std::make_pair(std::string {"edge"}, + mesh.add_face({ + 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}), + })); + }); + surface.emplace("bottom", mesh.add_face(bottom)); + surface.emplace("top", mesh.add_face(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..bdef74b --- /dev/null +++ b/assetFactory/faceController.cpp @@ -0,0 +1,53 @@ +#include "faceController.h" +#include "assetFactoryConfig.h" +#include "maths.h" + +void +FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const +{ + if (!type.empty()) { + const auto mutation = getMatrix(); + const auto controlledFacesRange = faces.equal_range(name); + const std::vector controlledFaces(controlledFacesRange.first, controlledFacesRange.second); + faces.erase(name); + for (const auto & cf : controlledFaces) { + // get face vertices + const auto faceVertexRange = mesh.fv_range(cf.second); + // get points + const std::vector baseVertices(faceVertexRange.begin(), faceVertexRange.end()); + std::vector points; + std::transform( + faceVertexRange.begin(), faceVertexRange.end(), std::back_inserter(points), [&mesh](auto && v) { + return mesh.point(v); + }); + const auto vertexCount = points.size(); + const auto centre + = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast(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 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 + + mesh.delete_face(cf.second); + for (size_t idx {}; idx < vertexCount; ++idx) { + const auto next = (idx + 1) % vertexCount; + newFaces.emplace("extrusion", + mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]})); + } + newFaces.emplace(name, mesh.add_face(vertices)); + for (const auto & [name, faceController] : faceControllers) { + faceController.apply(mesh, name, newFaces); + } + faces.merge(std::move(newFaces)); + } + } + } +} diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h new file mode 100644 index 0000000..64c33f3 --- /dev/null +++ b/assetFactory/faceController.h @@ -0,0 +1,20 @@ +#pragma once + +#include "assetFactoryConfig_fwd.h" +#include "mutation.h" +#include "shape.h" +#include +#include + +class FaceController : public Mutation { +public: + using FaceControllers = std::map; + + void apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const; + + std::string id; + std::string colour; + std::string type; + bool smooth {false}; + FaceControllers faceControllers; +}; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp new file mode 100644 index 0000000..1b89d14 --- /dev/null +++ b/assetFactory/factoryMesh.cpp @@ -0,0 +1,27 @@ +#include "factoryMesh.h" +#include "assetFactoryConfig.h" +#include "collections.hpp" +#include "gfx/models/vertex.hpp" +#include + +Mesh::Ptr +FactoryMesh::createMesh() const +{ + constexpr glm::vec2 NullUV {}; + + ModelFactoryMesh mesh; + for (const auto & use : uses) { + use->createMesh(mesh, glm::identity()); + } + mesh.garbage_collection(); + + mesh.triangulate(); + mesh.update_face_normals(); + std::vector vertices; + for (const auto & face : mesh.faces()) { + for (const auto & vertex : mesh.fv_range(face)) { + vertices.emplace_back(mesh.point(vertex), NullUV, mesh.property(mesh.face_normals_pph(), face)); + } + } + return std::make_shared(vertices, vectorOfN(vertices.size())); +} diff --git a/assetFactory/factoryMesh.h b/assetFactory/factoryMesh.h new file mode 100644 index 0000000..f850893 --- /dev/null +++ b/assetFactory/factoryMesh.h @@ -0,0 +1,14 @@ +#pragma once + +#include "gfx/models/mesh.h" +#include "stdTypeDefs.hpp" +#include "use.h" + +class FactoryMesh : public StdTypeDefs { +public: + Mesh::Ptr createMesh() const; + + std::string id; + glm::vec3 size; + Use::Collection uses; +}; diff --git a/assetFactory/modelFactory.cpp b/assetFactory/modelFactory.cpp new file mode 100644 index 0000000..3a89157 --- /dev/null +++ b/assetFactory/modelFactory.cpp @@ -0,0 +1,14 @@ +#include "modelFactory.h" +#include "assetFactoryConfig.h" +#include "cuboid.h" +#include "cylinder.h" +#include "plane.h" + +ModelFactory::ModelFactory() : + shapes { + {"plane", std::make_shared()}, + {"cuboid", std::make_shared()}, + {"cylinder", std::make_shared()}, + } +{ +} diff --git a/assetFactory/modelFactory.h b/assetFactory/modelFactory.h new file mode 100644 index 0000000..f2b1b48 --- /dev/null +++ b/assetFactory/modelFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include "shape.h" + +class ModelFactory { +public: + using Shapes = std::map>; + + ModelFactory(); + + Shapes shapes; +}; 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 +#include + +Mutation::Matrix +Mutation::getMatrix() const +{ + return glm::translate(glm::identity(), position) * rotate_ypr(rotation) + * glm::scale(glm::identity(), 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 +#include + +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..8b70676 --- /dev/null +++ b/assetFactory/object.cpp @@ -0,0 +1,17 @@ +#include "object.h" +#include + +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; +} diff --git a/assetFactory/object.h b/assetFactory/object.h new file mode 100644 index 0000000..f48b78a --- /dev/null +++ b/assetFactory/object.h @@ -0,0 +1,15 @@ +#pragma once + +#include "shape.h" +#include "stdTypeDefs.hpp" +#include "use.h" + +class Object : public StdTypeDefs, public Shape { +public: + Object(std::string i); + + CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const; + + Use::Collection uses; + std::string id; +}; diff --git a/assetFactory/plane.cpp b/assetFactory/plane.cpp new file mode 100644 index 0000000..1e522ad --- /dev/null +++ b/assetFactory/plane.cpp @@ -0,0 +1,15 @@ +#include "plane.h" +#include "assetFactoryConfig.h" + +Plane::CreatedFaces +Plane::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const +{ + static constexpr std::array VERTICES {{ + {n, n, z}, + {y, n, z}, + {y, y, z}, + {n, y, z}, + }}; + + return {{"plane", mesh.add_face(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..0994898 --- /dev/null +++ b/assetFactory/shape.cpp @@ -0,0 +1,17 @@ +#include "shape.h" +#include "assetFactoryConfig.h" +#include "gfx/models/vertex.hpp" +#include "maths.h" +#include "shape.h" + +std::vector +Shape::addMutatedToMesh( + ModelFactoryMesh & mesh, const std::span vertices, const Mutation::Matrix & mutation) +{ + std::vector 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..9510538 --- /dev/null +++ b/assetFactory/shape.h @@ -0,0 +1,25 @@ +#pragma once + +#include "assetFactoryConfig_fwd.h" +#include "mutation.h" +#include "stdTypeDefs.hpp" +#include +#include +#include +#include + +class Vertex; + +class Shape : public StdTypeDefs { +public: + using CreatedFaces = std::multimap; + + 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 addMutatedToMesh( + ModelFactoryMesh & mesh, const std::span vertices, const Mutation::Matrix & mutation); +}; diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp new file mode 100644 index 0000000..d191329 --- /dev/null +++ b/assetFactory/use.cpp @@ -0,0 +1,11 @@ +#include "use.h" + +Shape::CreatedFaces +Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const +{ + auto faces = type->createMesh(mesh, mutation * getMatrix()); + for (const auto & [name, faceController] : faceControllers) { + faceController.apply(mesh, name, faces); + } + return faces; +} diff --git a/assetFactory/use.h b/assetFactory/use.h new file mode 100644 index 0000000..4bfce97 --- /dev/null +++ b/assetFactory/use.h @@ -0,0 +1,17 @@ +#pragma once + +#include "assetFactoryConfig_fwd.h" +#include "faceController.h" +#include "shape.h" +#include "stdTypeDefs.hpp" + +class Use : public StdTypeDefs, public Mutation { +public: + using FaceControllers = std::map; + + Shape::CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const; + + Shape::CPtr type; + std::string colour; + FaceControllers faceControllers; +}; diff --git a/test/Jamfile.jam b/test/Jamfile.jam index cefad7b..d802975 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -52,5 +52,6 @@ run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; +run test-modelFactory.cpp : : : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; diff --git a/test/test-modelFactory.cpp b/test/test-modelFactory.cpp new file mode 100644 index 0000000..39362f5 --- /dev/null +++ b/test/test-modelFactory.cpp @@ -0,0 +1,150 @@ +#define BOOST_TEST_MODULE test_model_factory + +#include "testHelpers.h" +#include "testRenderOutput.h" +#include +#include + +#include "assetFactory/factoryMesh.h" +#include "assetFactory/modelFactory.h" +#include "assetFactory/object.h" +#include "gfx/gl/sceneRenderer.h" +#include "lib/collection.hpp" +#include "lib/location.hpp" +#include "testMainWindow.h" +#include "ui/applicationBase.h" + +BOOST_GLOBAL_FIXTURE(ApplicationBase); +BOOST_GLOBAL_FIXTURE(TestMainWindow); + +const std::filesystem::path TMP {"/tmp"}; +class FactoryFixture : public TestRenderOutputSize, public SceneProvider { +public: + FactoryFixture() : sceneRenderer {size, output} { } + ~FactoryFixture() + { + glDisable(GL_DEBUG_OUTPUT); + auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); + std::filesystem::create_directories(outpath.parent_path()); + Texture::save(outImage, size, outpath.c_str()); + } + void + content(const SceneShader & shader) const override + { + shader.basic.use(Location {{0, 0, 0}, {0, 0, 0}}); + meshes.apply(&Mesh::Draw); + } + void + lights(const SceneShader & shader) const override + { + shader.pointLight.add({-3, 1, 5}, {1, 1, 1}, .1F); + } + void + environment(const SceneShader &, const SceneRenderer & sceneRenderer) const override + { + sceneRenderer.setAmbientLight({.2, .2, .2}); + sceneRenderer.setDirectionalLight({.3, .3, .3}, east + south + south + down, *this); + } + void + shadows(const ShadowMapper & mapper) const override + { + mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); + meshes.apply(&Mesh::Draw); + } + void + render(float dist = 10.f) + { + sceneRenderer.camera.setView({dist, dist, dist}, south + west + down); + sceneRenderer.render(*this); + } + Collection meshes; + +private: + SceneRenderer sceneRenderer; +}; + +BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); +BOOST_AUTO_TEST_CASE(brush47) +{ + ModelFactory modelFactory; + { + auto wheel = std::make_shared("wheel"); + { + auto wheelCylinder = wheel->uses.emplace_back(std::make_shared()); + wheelCylinder->type = modelFactory.shapes.at("cylinder"); + wheelCylinder->position = {0, 0, 0.571}; + wheelCylinder->scale = {1.142, 1.142, 0.07}; + wheelCylinder->rotation = {0, 0, half_pi}; + wheelCylinder->colour = "#2C3539"; + } + modelFactory.shapes.emplace(wheel->id, wheel); + } + { + auto axel = std::make_shared("axel"); + for (float x : {-1.f, 1.f}) { + auto wheel = axel->uses.emplace_back(std::make_shared()); + wheel->type = modelFactory.shapes.at("wheel"); + wheel->position = {x * 0.717f, 0, 0}; + wheel->rotation = {0, x == 1.f ? pi : 0.f, 0}; + } + modelFactory.shapes.emplace(axel->id, axel); + } + { + auto bogey = std::make_shared("bogey"); + for (float y : {-2.f, 0.f, 2.f}) { + auto axel = bogey->uses.emplace_back(std::make_shared()); + axel->type = modelFactory.shapes.at("axel"); + axel->position = {0, y, 0}; + } + modelFactory.shapes.emplace(bogey->id, bogey); + } + FactoryMesh::Collection factoryMeshes; + { + unsigned short b {0}; + for (float y : {-6.f, 6.f}) { + auto bogey = factoryMeshes.emplace_back(std::make_shared()); + bogey->id = "bogey" + std::to_string(b); + auto bogeyUse = bogey->uses.emplace_back(std::make_shared()); + bogeyUse->type = modelFactory.shapes.at("bogey"); + bogeyUse->position = {0, y, 0}; + bogeyUse->rotation = {0, b * pi, 0}; + b++; + } + } + { + auto body = factoryMeshes.emplace_back(std::make_shared()); + body->id = "body"; + body->size = {2.69f, 19.38f, 3.9f}; + { + auto bodyLower = body->uses.emplace_back(std::make_shared()); + bodyLower->type = modelFactory.shapes.at("cuboid"); + bodyLower->position = {0, 0, 1.2}; + bodyLower->scale = {2.69, 19.38, 1.5}; + bodyLower->colour = "#1111DD"; + bodyLower->faceControllers["bottom"].colour = "#2C3539"; + auto & bodyUpper = bodyLower->faceControllers["top"]; + bodyUpper.type = "extrude"; + bodyUpper.scale = {1, .95f, 1}; + bodyUpper.position = {0, 0, 1.0}; + auto & roof = bodyUpper.faceControllers["top"]; + roof.type = "extrude"; + roof.scale = {.6f, .9f, 0}; + roof.position = {0, 0, 0.2}; + roof.smooth = true; + } + { + auto batteryBox = body->uses.emplace_back(std::make_shared()); + batteryBox->type = modelFactory.shapes.at("cuboid"); + batteryBox->position = {0, 0, .2}; + batteryBox->scale = {2.6, 4.5, 1}; + batteryBox->colour = "#2C3539"; + } + } + std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), + [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { + return factoryMesh->createMesh(); + }); + + render(20); +} +BOOST_AUTO_TEST_SUITE_END(); -- cgit v1.2.3 From c22c676dfee583bd48e1f962378f580f8310838b Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 16 Feb 2023 23:54:22 +0000 Subject: Refactor so ModelFactoryMesh can define the smooth property on faces --- assetFactory/assetFactoryConfig.h | 28 ---------------------------- assetFactory/assetFactoryConfig_fwd.h | 8 -------- assetFactory/cuboid.cpp | 2 +- assetFactory/cylinder.cpp | 2 +- assetFactory/faceController.cpp | 2 +- assetFactory/faceController.h | 2 +- assetFactory/factoryMesh.cpp | 2 +- assetFactory/modelFactory.cpp | 2 +- assetFactory/modelFactoryMesh.cpp | 6 ++++++ assetFactory/modelFactoryMesh.h | 34 ++++++++++++++++++++++++++++++++++ assetFactory/modelFactoryMesh_fwd.h | 3 +++ assetFactory/plane.cpp | 2 +- assetFactory/shape.cpp | 2 +- assetFactory/shape.h | 2 +- assetFactory/use.h | 2 +- 15 files changed, 53 insertions(+), 46 deletions(-) delete mode 100644 assetFactory/assetFactoryConfig.h delete mode 100644 assetFactory/assetFactoryConfig_fwd.h create mode 100644 assetFactory/modelFactoryMesh.cpp create mode 100644 assetFactory/modelFactoryMesh.h create mode 100644 assetFactory/modelFactoryMesh_fwd.h diff --git a/assetFactory/assetFactoryConfig.h b/assetFactory/assetFactoryConfig.h deleted file mode 100644 index 171b81a..0000000 --- a/assetFactory/assetFactoryConfig.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "assetFactoryConfig_fwd.h" -#include -#include -#include -#include - -namespace OpenMesh { - template struct glmvec : public VectorT { - using VectorT::VectorT; - glmvec(const VectorT & v) : VectorT {v} { } - operator glm::vec() const - { - glm::vec out; - std::copy(this->begin(), this->end(), &out[0]); - return out; - } - }; -} - -struct ModelFactoryTraits : public OpenMesh::DefaultTraits { - FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); - EdgeAttributes(OpenMesh::Attributes::Status); - VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); - using Point = OpenMesh::glmvec; - using Normal = OpenMesh::glmvec; -}; diff --git a/assetFactory/assetFactoryConfig_fwd.h b/assetFactory/assetFactoryConfig_fwd.h deleted file mode 100644 index 28489ff..0000000 --- a/assetFactory/assetFactoryConfig_fwd.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace OpenMesh { - template class PolyMesh_ArrayKernelT; -} -struct ModelFactoryTraits; - -using ModelFactoryMesh = OpenMesh::PolyMesh_ArrayKernelT; diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp index 90b2108..86114e5 100644 --- a/assetFactory/cuboid.cpp +++ b/assetFactory/cuboid.cpp @@ -1,5 +1,5 @@ #include "cuboid.h" -#include "assetFactoryConfig.h" +#include "modelFactoryMesh.h" Cuboid::CreatedFaces Cuboid::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const diff --git a/assetFactory/cylinder.cpp b/assetFactory/cylinder.cpp index 66d456d..83cf035 100644 --- a/assetFactory/cylinder.cpp +++ b/assetFactory/cylinder.cpp @@ -1,6 +1,6 @@ #include "cylinder.h" -#include "assetFactoryConfig.h" #include "maths.h" +#include "modelFactoryMesh.h" Cylinder::CreatedFaces Cylinder::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index bdef74b..c04a656 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -1,6 +1,6 @@ #include "faceController.h" -#include "assetFactoryConfig.h" #include "maths.h" +#include "modelFactoryMesh.h" void FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 64c33f3..9974caf 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -1,6 +1,6 @@ #pragma once -#include "assetFactoryConfig_fwd.h" +#include "modelFactoryMesh_fwd.h" #include "mutation.h" #include "shape.h" #include diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 1b89d14..9d30ac9 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -1,7 +1,7 @@ #include "factoryMesh.h" -#include "assetFactoryConfig.h" #include "collections.hpp" #include "gfx/models/vertex.hpp" +#include "modelFactoryMesh.h" #include Mesh::Ptr diff --git a/assetFactory/modelFactory.cpp b/assetFactory/modelFactory.cpp index 3a89157..4c25e48 100644 --- a/assetFactory/modelFactory.cpp +++ b/assetFactory/modelFactory.cpp @@ -1,7 +1,7 @@ #include "modelFactory.h" -#include "assetFactoryConfig.h" #include "cuboid.h" #include "cylinder.h" +#include "modelFactoryMesh_fwd.h" #include "plane.h" ModelFactory::ModelFactory() : diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp new file mode 100644 index 0000000..6c772ad --- /dev/null +++ b/assetFactory/modelFactoryMesh.cpp @@ -0,0 +1,6 @@ +#include "modelFactoryMesh.h" + +ModelFactoryMesh::ModelFactoryMesh() +{ + add_property(smoothFaceProperty); +} diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h new file mode 100644 index 0000000..d0ffc26 --- /dev/null +++ b/assetFactory/modelFactoryMesh.h @@ -0,0 +1,34 @@ +#pragma once + +#include "modelFactoryMesh_fwd.h" +#include +#include +#include +#include + +namespace OpenMesh { + template struct glmvec : public VectorT { + using VectorT::VectorT; + glmvec(const VectorT & v) : VectorT {v} { } + operator glm::vec() const + { + glm::vec out; + std::copy(this->begin(), this->end(), &out[0]); + return out; + } + }; +} + +struct ModelFactoryTraits : public OpenMesh::DefaultTraits { + FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + EdgeAttributes(OpenMesh::Attributes::Status); + VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + using Point = OpenMesh::glmvec; + using Normal = OpenMesh::glmvec; +}; + +struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT { + ModelFactoryMesh(); + + OpenMesh::FPropHandleT smoothFaceProperty; +}; 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/plane.cpp b/assetFactory/plane.cpp index 1e522ad..37c8194 100644 --- a/assetFactory/plane.cpp +++ b/assetFactory/plane.cpp @@ -1,5 +1,5 @@ #include "plane.h" -#include "assetFactoryConfig.h" +#include "modelFactoryMesh.h" Plane::CreatedFaces Plane::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const diff --git a/assetFactory/shape.cpp b/assetFactory/shape.cpp index 0994898..f6e55e8 100644 --- a/assetFactory/shape.cpp +++ b/assetFactory/shape.cpp @@ -1,7 +1,7 @@ #include "shape.h" -#include "assetFactoryConfig.h" #include "gfx/models/vertex.hpp" #include "maths.h" +#include "modelFactoryMesh.h" #include "shape.h" std::vector diff --git a/assetFactory/shape.h b/assetFactory/shape.h index 9510538..5a2b59c 100644 --- a/assetFactory/shape.h +++ b/assetFactory/shape.h @@ -1,6 +1,6 @@ #pragma once -#include "assetFactoryConfig_fwd.h" +#include "modelFactoryMesh_fwd.h" #include "mutation.h" #include "stdTypeDefs.hpp" #include diff --git a/assetFactory/use.h b/assetFactory/use.h index 4bfce97..28f5459 100644 --- a/assetFactory/use.h +++ b/assetFactory/use.h @@ -1,7 +1,7 @@ #pragma once -#include "assetFactoryConfig_fwd.h" #include "faceController.h" +#include "modelFactoryMesh_fwd.h" #include "shape.h" #include "stdTypeDefs.hpp" -- cgit v1.2.3 From 88d5b474f3ab1c021612b0730f1b8d327a7a32db Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 16 Feb 2023 23:56:10 +0000 Subject: Add support for smooth faces --- assetFactory/cylinder.cpp | 3 +++ assetFactory/faceController.cpp | 8 ++++++++ assetFactory/factoryMesh.cpp | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/assetFactory/cylinder.cpp b/assetFactory/cylinder.cpp index 83cf035..d51dc37 100644 --- a/assetFactory/cylinder.cpp +++ b/assetFactory/cylinder.cpp @@ -36,6 +36,9 @@ Cylinder::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) mesh.add_vertex({xyz1t.x, xyz1t.y, xyz1t.z}), })); }); + for (const auto & [name, face] : surface) { + mesh.property(mesh.smoothFaceProperty, face) = true; + } surface.emplace("bottom", mesh.add_face(bottom)); surface.emplace("top", mesh.add_face(top)); diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index c04a656..7ec7820 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -23,6 +23,9 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: const auto vertexCount = points.size(); const auto centre = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast(vertexCount); + if (smooth) { + mesh.property(mesh.smoothFaceProperty, cf.second) = true; + } if (type == "extrude") { Shape::CreatedFaces newFaces; // mutate points @@ -43,6 +46,11 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]})); } newFaces.emplace(name, mesh.add_face(vertices)); + if (smooth) { + for (const auto & [name, face] : newFaces) { + mesh.property(mesh.smoothFaceProperty, face) = true; + } + } for (const auto & [name, faceController] : faceControllers) { faceController.apply(mesh, name, newFaces); } diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 9d30ac9..baa031d 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -17,10 +17,14 @@ FactoryMesh::createMesh() const mesh.triangulate(); mesh.update_face_normals(); + mesh.update_vertex_normals(); std::vector vertices; for (const auto & face : mesh.faces()) { + const auto smooth = mesh.property(mesh.smoothFaceProperty, face); for (const auto & vertex : mesh.fv_range(face)) { - vertices.emplace_back(mesh.point(vertex), NullUV, mesh.property(mesh.face_normals_pph(), face)); + vertices.emplace_back(mesh.point(vertex), NullUV, + smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) + : mesh.property(mesh.face_normals_pph(), face)); } } return std::make_shared(vertices, vectorOfN(vertices.size())); -- cgit v1.2.3 From 375652641509b75e93a284deea44f6f1be13e7b3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Feb 2023 18:24:54 +0000 Subject: Swap @ prefix for p. prefix for special value names --- lib/persistence.cpp | 4 ++-- lib/persistence.h | 4 ++-- test/fixtures/json/abs.json | 2 +- test/fixtures/json/bad/empty_abs.json | 2 +- test/fixtures/json/bad/implicit_abs.json | 2 +- test/fixtures/json/bad/late_type.json | 2 +- test/fixtures/json/bad/wrong_type.json | 2 +- test/fixtures/json/bad_type.json | 2 +- test/fixtures/json/empty.json | 2 +- test/fixtures/json/implicit.json | 2 +- test/fixtures/json/load_object.json | 4 ++-- test/fixtures/json/nested.json | 8 ++++---- test/fixtures/json/shared_ptr_diff.json | 10 +++++----- test/fixtures/json/shared_ptr_diff_default.json | 6 +++--- test/fixtures/json/shared_ptr_null.json | 6 +++--- test/fixtures/json/shared_ptr_same.json | 6 +++--- test/fixtures/json/shared_ptr_wrong_type.json | 6 +++--- test/fixtures/json/vector_ptr.json | 2 +- test/test-persistence.cpp | 8 ++++---- 19 files changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 239e425..543f2fd 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -81,12 +81,12 @@ namespace Persistence { void PersistenceWrite::setType(const std::string_view tn, const Persistable * p) { - out.pushKey("@typeid"); + out.pushKey("p.typeid"); out.pushValue(tn); first = false; if (shared) { out.nextValue(); - out.pushKey("@id"); + out.pushKey("p.id"); out.pushValue(p->getId()); } } diff --git a/lib/persistence.h b/lib/persistence.h index 4fbff4c..c311067 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -340,14 +340,14 @@ namespace Persistence { select(const std::string & mbr) override { using namespace std::literals; - if (mbr == "@typeid"sv) { + if (mbr == "p.typeid"sv) { if (this->v) { throw std::runtime_error("cannot set object type after creation"); } return this->template make_s(this->v); } if constexpr (shared) { - if (mbr == "@id"sv) { + if (mbr == "p.id"sv) { return this->template make_s(this->v); } } diff --git a/test/fixtures/json/abs.json b/test/fixtures/json/abs.json index 8492df3..1b489ef 100644 --- a/test/fixtures/json/abs.json +++ b/test/fixtures/json/abs.json @@ -1,6 +1,6 @@ { "aptr": { - "@typeid": "SubObject", + "p.typeid": "SubObject", "base": "set base", "sub": "set sub" } diff --git a/test/fixtures/json/bad/empty_abs.json b/test/fixtures/json/bad/empty_abs.json index 7d22001..5cc3598 100644 --- a/test/fixtures/json/bad/empty_abs.json +++ b/test/fixtures/json/bad/empty_abs.json @@ -1,5 +1,5 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 1, "aptr": {}, "str": "after" diff --git a/test/fixtures/json/bad/implicit_abs.json b/test/fixtures/json/bad/implicit_abs.json index 573b323..bf81ee4 100644 --- a/test/fixtures/json/bad/implicit_abs.json +++ b/test/fixtures/json/bad/implicit_abs.json @@ -1,5 +1,5 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 1, "aptr": { "str": "trigger" diff --git a/test/fixtures/json/bad/late_type.json b/test/fixtures/json/bad/late_type.json index 171575a..d1d6f6c 100644 --- a/test/fixtures/json/bad/late_type.json +++ b/test/fixtures/json/bad/late_type.json @@ -1,4 +1,4 @@ { "str": "trigger", - "@typeid": "doesn't matter" + "p.typeid": "doesn't matter" } diff --git a/test/fixtures/json/bad/wrong_type.json b/test/fixtures/json/bad/wrong_type.json index 777d791..4f19033 100644 --- a/test/fixtures/json/bad/wrong_type.json +++ b/test/fixtures/json/bad/wrong_type.json @@ -1,5 +1,5 @@ { "ptr": { - "@typeid": "SubObject" + "p.typeid": "SubObject" } } diff --git a/test/fixtures/json/bad_type.json b/test/fixtures/json/bad_type.json index f316bd1..70b9d1a 100644 --- a/test/fixtures/json/bad_type.json +++ b/test/fixtures/json/bad_type.json @@ -1,3 +1,3 @@ { - "@typeid": "no such type" + "p.typeid": "no such type" } diff --git a/test/fixtures/json/empty.json b/test/fixtures/json/empty.json index a9193a3..9575565 100644 --- a/test/fixtures/json/empty.json +++ b/test/fixtures/json/empty.json @@ -1,5 +1,5 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 1, "ptr": {}, "str": "after" diff --git a/test/fixtures/json/implicit.json b/test/fixtures/json/implicit.json index 478cec6..6efc2ba 100644 --- a/test/fixtures/json/implicit.json +++ b/test/fixtures/json/implicit.json @@ -1,5 +1,5 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 1, "ptr": { "str": "trigger", diff --git a/test/fixtures/json/load_object.json b/test/fixtures/json/load_object.json index bb32298..c622885 100644 --- a/test/fixtures/json/load_object.json +++ b/test/fixtures/json/load_object.json @@ -1,5 +1,5 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 3.14, "str": "Lovely string", "bl": true, @@ -48,7 +48,7 @@ [] ], "ptr": { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 3.14, "str": "Lovely string" } diff --git a/test/fixtures/json/nested.json b/test/fixtures/json/nested.json index 98951fc..1e271e3 100644 --- a/test/fixtures/json/nested.json +++ b/test/fixtures/json/nested.json @@ -1,14 +1,14 @@ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 1, "ptr": { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 2, "ptr": { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 3, "ptr": { - "@typeid": "TestObject", + "p.typeid": "TestObject", "flt": 4, "ptr": null, "str": "four" diff --git a/test/fixtures/json/shared_ptr_diff.json b/test/fixtures/json/shared_ptr_diff.json index bb18e33..043be4a 100644 --- a/test/fixtures/json/shared_ptr_diff.json +++ b/test/fixtures/json/shared_ptr_diff.json @@ -1,11 +1,11 @@ { - "@typeid": "SharedTestObject", + "p.typeid": "SharedTestObject", "sptr": { - "@typeid": "SubObject", - "@id": "someid" + "p.typeid": "SubObject", + "p.id": "someid" }, "ssptr": { - "@typeid": "SubObject", - "@id": "some other id" + "p.typeid": "SubObject", + "p.id": "some other id" } } diff --git a/test/fixtures/json/shared_ptr_diff_default.json b/test/fixtures/json/shared_ptr_diff_default.json index 69f5e85..c78bc12 100644 --- a/test/fixtures/json/shared_ptr_diff_default.json +++ b/test/fixtures/json/shared_ptr_diff_default.json @@ -1,8 +1,8 @@ { - "@typeid": "SharedTestObject", + "p.typeid": "SharedTestObject", "sptr": { - "@typeid": "SubObject", - "@id": "someid" + "p.typeid": "SubObject", + "p.id": "someid" }, "ssptr": {} } diff --git a/test/fixtures/json/shared_ptr_null.json b/test/fixtures/json/shared_ptr_null.json index c2461e8..8669bf2 100644 --- a/test/fixtures/json/shared_ptr_null.json +++ b/test/fixtures/json/shared_ptr_null.json @@ -1,8 +1,8 @@ { - "@typeid": "SharedTestObject", + "p.typeid": "SharedTestObject", "sptr": { - "@typeid": "SubObject", - "@id": "someid" + "p.typeid": "SubObject", + "p.id": "someid" }, "ssptr": null } diff --git a/test/fixtures/json/shared_ptr_same.json b/test/fixtures/json/shared_ptr_same.json index 4115493..2838e82 100644 --- a/test/fixtures/json/shared_ptr_same.json +++ b/test/fixtures/json/shared_ptr_same.json @@ -1,8 +1,8 @@ { - "@typeid": "SharedTestObject", + "p.typeid": "SharedTestObject", "sptr": { - "@typeid": "SubObject", - "@id": "someid" + "p.typeid": "SubObject", + "p.id": "someid" }, "ssptr": "someid" } diff --git a/test/fixtures/json/shared_ptr_wrong_type.json b/test/fixtures/json/shared_ptr_wrong_type.json index 68f7533..5d4e655 100644 --- a/test/fixtures/json/shared_ptr_wrong_type.json +++ b/test/fixtures/json/shared_ptr_wrong_type.json @@ -1,8 +1,8 @@ { - "@typeid": "SharedTestObject", + "p.typeid": "SharedTestObject", "sptr": { - "@typeid": "SubObject2", - "@id": "someid" + "p.typeid": "SubObject2", + "p.id": "someid" }, "ssptr": "someid" } diff --git a/test/fixtures/json/vector_ptr.json b/test/fixtures/json/vector_ptr.json index 8a07a2e..654bd6c 100644 --- a/test/fixtures/json/vector_ptr.json +++ b/test/fixtures/json/vector_ptr.json @@ -1,7 +1,7 @@ { "vptr": [ { - "@typeid": "TestObject", + "p.typeid": "TestObject", "str": "type" }, { diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index e13cb7a..faa0d19 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(write_test_dfl) std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]})"); + R"({"p.typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded, JPP) @@ -329,7 +329,7 @@ BOOST_FIXTURE_TEST_CASE(write_test_loaded, JPP) std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":true,"pos":[3.14,6.28,1.57],"flts":[3.14,6.28,1.57,0,-1,-3.14],"poss":[[3.14,6.28,1.57],[0,-1,-3.14]],"nest":[[["a","b"],["c","d","e"]],[["f"]],[]],"ptr":{"@typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]},"vptr":[]})"); + R"({"p.typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":true,"pos":[3.14,6.28,1.57],"flts":[3.14,6.28,1.57,0,-1,-3.14],"poss":[[3.14,6.28,1.57],[0,-1,-3.14]],"nest":[[["a","b"],["c","d","e"]],[["f"]],[]],"ptr":{"p.typeid":"TestObject","flt":3.14,"str":"Lovely string","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"vptr":[]},"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded_abs, JPP) @@ -338,7 +338,7 @@ BOOST_FIXTURE_TEST_CASE(write_test_loaded_abs, JPP) std::stringstream ss; Persistence::JsonWritePersistence {ss}.saveState(to); BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"aptr":{"@typeid":"SubObject","base":"set base","sub":"set sub"},"vptr":[]})"); + R"({"p.typeid":"TestObject","flt":0,"str":"","bl":false,"pos":[0,0,0],"flts":[],"poss":[],"nest":[],"aptr":{"p.typeid":"SubObject","base":"set base","sub":"set sub"},"vptr":[]})"); } BOOST_FIXTURE_TEST_CASE(write_test_loaded_shared, JPP) @@ -349,7 +349,7 @@ BOOST_FIXTURE_TEST_CASE(write_test_loaded_shared, JPP) Persistence::JsonWritePersistence {ss}.saveState(to); BOOST_CHECK_EQUAL(Persistence::seenSharedObjects.size(), 1); BOOST_CHECK_EQUAL(ss.str(), - R"({"@typeid":"SharedTestObject","sptr":{"@typeid":"SubObject","@id":"someid","base":"","sub":""},"ssptr":"someid"})"); + R"({"p.typeid":"SharedTestObject","sptr":{"p.typeid":"SubObject","p.id":"someid","base":"","sub":""},"ssptr":"someid"})"); } BOOST_DATA_TEST_CASE(write_special_strings, TEST_STRINGS, exp, in) -- cgit v1.2.3 From 0724047fbbbf02e053e2686299d726fcb2c7d295 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Feb 2023 18:42:30 +0000 Subject: Add SAXParse base class --- Jamroot.jam | 2 ++ lib/saxParse.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ lib/saxParse.h | 21 +++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 lib/saxParse.cpp create mode 100644 lib/saxParse.h diff --git a/Jamroot.jam b/Jamroot.jam index 16c22ac..27db410 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -12,6 +12,7 @@ pkg-config.import sdl2 ; pkg-config.import glew ; pkg-config.import freetype2 ; pkg-config.import glib-2.0 ; +pkg-config.import mxml ; lib pthread ; lib OpenMeshCore ; @@ -112,6 +113,7 @@ lib ilt : glew freetype2 glib-2.0 + mxml pthread OpenMeshCore : : diff --git a/lib/saxParse.cpp b/lib/saxParse.cpp new file mode 100644 index 0000000..d592a1f --- /dev/null +++ b/lib/saxParse.cpp @@ -0,0 +1,41 @@ +#include "saxParse.h" +#include "mxml.h" + +namespace Persistence { + void + SAXParse::comment(mxml_node_t *) + { + // Default to just ignore comments + } + + void + SAXParse::parseFile(FILE * file) + { + mxmlSAXLoadFile( + nullptr, file, MXML_TEXT_CALLBACK, + [](mxml_node_t * n, mxml_sax_event_t e, void * data) { + SAXParse * self = static_cast(data); + switch (e) { + case MXML_SAX_ELEMENT_OPEN: + return self->elementOpen(n); + break; + case MXML_SAX_ELEMENT_CLOSE: + return self->elementClose(n); + break; + case MXML_SAX_COMMENT: + return self->comment(n); + break; + case MXML_SAX_DATA: + return self->data(n); + break; + case MXML_SAX_DIRECTIVE: + return self->directive(n); + break; + case MXML_SAX_CDATA:; + return self->cdata(n); + break; + } + }, + this); + } +} diff --git a/lib/saxParse.h b/lib/saxParse.h new file mode 100644 index 0000000..d5baaca --- /dev/null +++ b/lib/saxParse.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +typedef struct _mxml_node_s mxml_node_t; + +namespace Persistence { + class SAXParse { + public: + virtual ~SAXParse() = default; + + virtual void elementOpen(mxml_node_t *) = 0; + virtual void elementClose(mxml_node_t *) = 0; + virtual void comment(mxml_node_t *); + virtual void data(mxml_node_t *) = 0; + virtual void directive(mxml_node_t *) = 0; + virtual void cdata(mxml_node_t *) = 0; + + void parseFile(FILE * file); + }; +} -- cgit v1.2.3 From eeb3bceceacff5807011c83eeaa1f1497429b3ab Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Feb 2023 18:59:18 +0000 Subject: Make sure an object is created before remembering it --- lib/persistence.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/persistence.h b/lib/persistence.h index c311067..dbaee44 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -348,6 +348,7 @@ namespace Persistence { } if constexpr (shared) { if (mbr == "p.id"sv) { + make_default_as_needed(this->v); return this->template make_s(this->v); } } -- cgit v1.2.3 From 223ecbc450fede9f8ca246895d5f82495f8665e8 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Feb 2023 19:14:55 +0000 Subject: Allow override member variable names and selection helper when mapping members for persistence --- lib/persistence.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/persistence.h b/lib/persistence.h index dbaee44..e1f7605 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -113,14 +113,14 @@ namespace Persistence { template [[nodiscard]] inline bool persistType(const T * const, const std::type_info & ti); enum class NameAction { Push, HandleAndContinue, Ignore }; - template + template [[nodiscard]] inline bool persistValue(const std::string_view key, T & value) { - SelectionT s {value}; - const auto act {setName(key, s)}; + auto s = std::make_unique(value); + const auto act {setName(key, *s)}; if (act != NameAction::Ignore) { - sel = std::make_unique(std::move(s)); + sel = std::move(s); if (act == NameAction::HandleAndContinue) { selHandler(); } @@ -462,4 +462,7 @@ namespace Persistence { } #define STORE_TYPE store.persistType(this, typeid(*this)) -#define STORE_MEMBER(mbr) store.persistValue(#mbr, mbr) +#define STORE_MEMBER(mbr) STORE_NAME_MEMBER(#mbr, mbr) +#define STORE_NAME_MEMBER(name, mbr) store.persistValue>(name, mbr) +#define STORE_HELPER(mbr, Helper) STORE_NAME_HELPER(#mbr, mbr, Helper) +#define STORE_NAME_HELPER(name, mbr, Helper) store.persistValue(name, mbr) -- cgit v1.2.3 From a142b8da33e6c6657a7aa529ddf0f1c0882ff0d5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Feb 2023 23:59:24 +0000 Subject: Accept a CSV for glm::vec data --- lib/persistence.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/persistence.h b/lib/persistence.h index e1f7605..01b2a7e 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,17 @@ namespace Persistence { }; using SelectionV::SelectionV; + using SelectionV::setValue; + + void + setValue(std::string && s) override + { + std::stringstream ss {std::move(s)}; + for (glm::length_t n = 0; n < L; n += 1) { + ss >> this->v[n]; + ss.get(); + } + } void beginArray(Stack & stk) override -- cgit v1.2.3 From bf0a1a219e48007ca51e23ea45d08d295e398fcd Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 00:03:08 +0000 Subject: Add helper Selection to insert into a map based on a member value as key --- lib/persistence.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/persistence.h b/lib/persistence.h index 01b2a7e..d55062a 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -256,6 +256,23 @@ namespace Persistence { } }; + template + struct MapByMember : public Persistence::SelectionT> { + MapByMember(Map & m) : Persistence::SelectionT> {s}, map {m} { } + + using Persistence::SelectionT>::SelectionT; + void + endObject(Persistence::Stack & stk) override + { + map.emplace(std::invoke(Key, s), s); + stk.pop(); + } + + private: + std::shared_ptr s; + Map & map; + }; + struct Persistable { Persistable() = default; virtual ~Persistable() = default; -- cgit v1.2.3 From e2dae4345b7cb9ec92e204a8bf2ab3dee8fcb9ac Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 00:48:02 +0000 Subject: Add checked_fopen wrapper and FileStar container --- lib/filesystem.cpp | 9 +++++++++ lib/filesystem.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp index 0e19e8d..7e8ab9c 100644 --- a/lib/filesystem.cpp +++ b/lib/filesystem.cpp @@ -62,4 +62,13 @@ namespace filesystem { { return memmap {length, prot, flags, h, static_cast(offset)}; } + + FILE * + checked_fopen(const char * pathname, const char * mode) + { + if (auto file = fopen(pathname, mode)) { + return file; + } + throw_filesystem_error("fopen", errno, pathname); + } } diff --git a/lib/filesystem.h b/lib/filesystem.h index 0c44236..5315183 100644 --- a/lib/filesystem.h +++ b/lib/filesystem.h @@ -1,7 +1,9 @@ #pragma once +#include "ptr.hpp" #include "special_members.hpp" #include +#include #include namespace filesystem { @@ -39,4 +41,7 @@ namespace filesystem { private: int h; }; + + FILE * checked_fopen(const char * pathname, const char * mode); + using FileStar = wrapped_ptrt; } -- cgit v1.2.3 From 15d7196bf3a3244f078f63645783c4c3f2534d92 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 00:57:37 +0000 Subject: Implement XML deserializer with SAXParse --- lib/saxParse-persistence.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++ lib/saxParse-persistence.h | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 lib/saxParse-persistence.cpp create mode 100644 lib/saxParse-persistence.h diff --git a/lib/saxParse-persistence.cpp b/lib/saxParse-persistence.cpp new file mode 100644 index 0000000..a6a0d23 --- /dev/null +++ b/lib/saxParse-persistence.cpp @@ -0,0 +1,50 @@ +#include "saxParse-persistence.h" + +namespace Persistence { + + void + SAXParsePersistence::loadStateInternal(FILE * in) + { + stk.top()->beforeValue(stk); + stk.top()->beginObject(stk); + parseFile(in); + stk.pop(); + stk.pop(); + } + + void + SAXParsePersistence::elementOpen(mxml_node_t * n) + { + stk.push(stk.top()->select(mxmlGetElement(n))); + stk.top()->beforeValue(stk); + stk.top()->beginObject(stk); + for (int attrCount = mxmlElementGetAttrCount(n), attrIdx {0}; attrIdx < attrCount; ++attrIdx) { + const char *name, *value = mxmlElementGetAttrByIndex(n, attrIdx, &name); + auto sel = stk.top()->select(name); + sel->beforeValue(stk); + sel->setValue(std::string {value}); + } + } + + void + SAXParsePersistence::elementClose(mxml_node_t *) + { + stk.top()->endObject(stk); + stk.top()->endObject(stk); + } + + void + SAXParsePersistence::data(mxml_node_t *) + { + } + + void + SAXParsePersistence::directive(mxml_node_t *) + { + } + + void + SAXParsePersistence::cdata(mxml_node_t *) + { + } +} diff --git a/lib/saxParse-persistence.h b/lib/saxParse-persistence.h new file mode 100644 index 0000000..91daecc --- /dev/null +++ b/lib/saxParse-persistence.h @@ -0,0 +1,43 @@ +#pragma once + +#include "persistence.h" +#include "saxParse.h" +#include +#include + +namespace Persistence { + class SAXParsePersistence : public SAXParse { + private: + template struct Root : public Persistable { + T t {}; + bool + persist(PersistenceStore & store) + { + return STORE_TYPE && STORE_NAME_MEMBER("ilt", t); + } + }; + + void loadStateInternal(FILE * in); + + public: + template + auto + loadState(FILE * in) + { + std::unique_ptr> root; + stk.push(std::make_unique>(std::ref(root))); + loadStateInternal(in); + return std::move(root->t); + } + + protected: + void elementOpen(mxml_node_t * n) override; + void elementClose(mxml_node_t *) override; + void data(mxml_node_t *) override; + void directive(mxml_node_t *) override; + void cdata(mxml_node_t *) override; + + private: + Stack stk; + }; +} -- cgit v1.2.3 From 6772443b0ef78ce7cbdd815103d7653f2b243e36 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 01:05:38 +0000 Subject: Add missing override --- assetFactory/object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assetFactory/object.h b/assetFactory/object.h index f48b78a..da28c1f 100644 --- a/assetFactory/object.h +++ b/assetFactory/object.h @@ -8,7 +8,7 @@ class Object : public StdTypeDefs, public Shape { public: Object(std::string i); - CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const; + CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const override; Use::Collection uses; std::string id; -- cgit v1.2.3 From ed7c2a6a73f24f97a0f04c2e6be6862ffe54b585 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 01:25:34 +0000 Subject: Support for loading objects, uses and model factories from an XML resource --- assetFactory/modelFactory.cpp | 17 +++++++++++++++++ assetFactory/modelFactory.h | 9 ++++++++- assetFactory/object.cpp | 22 ++++++++++++++++++++++ assetFactory/object.h | 13 ++++++++++++- assetFactory/use.cpp | 20 ++++++++++++++++++++ assetFactory/use.h | 12 +++++++++++- res/brush47.xml | 15 +++++++++++++++ test/Jamfile.jam | 2 +- test/test-modelFactory.cpp | 23 +++++++++++++++++++++++ 9 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 res/brush47.xml diff --git a/assetFactory/modelFactory.cpp b/assetFactory/modelFactory.cpp index 4c25e48..2642900 100644 --- a/assetFactory/modelFactory.cpp +++ b/assetFactory/modelFactory.cpp @@ -2,7 +2,10 @@ #include "cuboid.h" #include "cylinder.h" #include "modelFactoryMesh_fwd.h" +#include "object.h" #include "plane.h" +#include "saxParse-persistence.h" +#include ModelFactory::ModelFactory() : shapes { @@ -12,3 +15,17 @@ ModelFactory::ModelFactory() : } { } + +std::shared_ptr +ModelFactory::loadXML(const std::filesystem::path & filename) +{ + filesystem::FileStar file {filename.c_str(), "r"}; + return Persistence::SAXParsePersistence {}.loadState>(file); +} + +bool +ModelFactory::persist(Persistence::PersistenceStore & store) +{ + using MapObjects = Persistence::MapByMember; + return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects); +} diff --git a/assetFactory/modelFactory.h b/assetFactory/modelFactory.h index f2b1b48..94db055 100644 --- a/assetFactory/modelFactory.h +++ b/assetFactory/modelFactory.h @@ -1,12 +1,19 @@ #pragma once +#include "persistence.h" #include "shape.h" +#include -class ModelFactory { +class ModelFactory : public Persistence::Persistable { public: using Shapes = std::map>; ModelFactory(); + [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); Shapes shapes; + +private: + friend Persistence::SelectionPtrBase, true>; + bool persist(Persistence::PersistenceStore & store) override; }; diff --git a/assetFactory/object.cpp b/assetFactory/object.cpp index 8b70676..faa9a17 100644 --- a/assetFactory/object.cpp +++ b/assetFactory/object.cpp @@ -15,3 +15,25 @@ Object::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) c } return faces; } + +template struct Appender : public Persistence::SelectionT> { + Appender(Container & c) : Persistence::SelectionT> {s}, container {c} { } + using Persistence::SelectionT>::SelectionT; + void + endObject(Persistence::Stack & stk) override + { + container.emplace_back(s); + stk.pop(); + } + +private: + std::shared_ptr s; + Container & container; +}; + +bool +Object::persist(Persistence::PersistenceStore & store) +{ + using UseAppend = Appender; + return STORE_TYPE && STORE_MEMBER(id) && STORE_NAME_HELPER("use", uses, UseAppend); +} diff --git a/assetFactory/object.h b/assetFactory/object.h index da28c1f..1069f66 100644 --- a/assetFactory/object.h +++ b/assetFactory/object.h @@ -1,15 +1,26 @@ #pragma once +#include "persistence.h" #include "shape.h" #include "stdTypeDefs.hpp" #include "use.h" -class Object : public StdTypeDefs, public Shape { +class Object : public StdTypeDefs, 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, true>; + bool persist(Persistence::PersistenceStore & store) override; + std::string + getId() const override + { + return id; + }; }; diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index d191329..21e26f3 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -1,4 +1,5 @@ #include "use.h" +#include "modelFactory.h" Shape::CreatedFaces Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const @@ -9,3 +10,22 @@ Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) cons } return faces; } + +struct Lookup : public Persistence::SelectionV { + using Persistence::SelectionV::SelectionV; + using Persistence::SelectionV::setValue; + void + setValue(std::string && str) override + { + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("modelFactory"))) { + 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); +} diff --git a/assetFactory/use.h b/assetFactory/use.h index 28f5459..96f07f6 100644 --- a/assetFactory/use.h +++ b/assetFactory/use.h @@ -2,10 +2,11 @@ #include "faceController.h" #include "modelFactoryMesh_fwd.h" +#include "persistence.h" #include "shape.h" #include "stdTypeDefs.hpp" -class Use : public StdTypeDefs, public Mutation { +class Use : public StdTypeDefs, public Mutation, public Persistence::Persistable { public: using FaceControllers = std::map; @@ -14,4 +15,13 @@ public: Shape::CPtr type; std::string colour; FaceControllers faceControllers; + +private: + friend Persistence::SelectionPtrBase, true>; + bool persist(Persistence::PersistenceStore & store) override; + std::string + getId() const override + { + return {}; + }; }; diff --git a/res/brush47.xml b/res/brush47.xml new file mode 100644 index 0000000..8f1d934 --- /dev/null +++ b/res/brush47.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/test/Jamfile.jam b/test/Jamfile.jam index d802975..027b880 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -52,6 +52,6 @@ run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; -run test-modelFactory.cpp : : : test ; +run test-modelFactory.cpp : -- : ../res/brush47.xml : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; diff --git a/test/test-modelFactory.cpp b/test/test-modelFactory.cpp index 39362f5..8f2d56c 100644 --- a/test/test-modelFactory.cpp +++ b/test/test-modelFactory.cpp @@ -147,4 +147,27 @@ BOOST_AUTO_TEST_CASE(brush47) render(20); } +BOOST_AUTO_TEST_CASE(brush47xml) +{ + auto mf = ModelFactory::loadXML(RESDIR "/brush47.xml"); + BOOST_REQUIRE(mf); + BOOST_REQUIRE_EQUAL(6, mf->shapes.size()); + BOOST_CHECK(mf->shapes.at("plane")); + BOOST_CHECK(mf->shapes.at("cylinder")); + BOOST_CHECK(mf->shapes.at("cuboid")); + BOOST_CHECK(mf->shapes.at("wheel")); + BOOST_CHECK(mf->shapes.at("axel")); + auto bogey = mf->shapes.at("bogey"); + BOOST_REQUIRE(bogey); + auto bogeyObj = std::dynamic_pointer_cast(bogey); + BOOST_CHECK_EQUAL(3, bogeyObj->uses.size()); + + FactoryMesh::Collection factoryMeshes; + std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), + [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { + return factoryMesh->createMesh(); + }); + + render(20); +} BOOST_AUTO_TEST_SUITE_END(); -- cgit v1.2.3 From 5e561d390dc82b08c20532de0952f428e7b14283 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 19:34:16 +0000 Subject: Rename ModelFactory to AssetFactory --- assetFactory/assetFactory.cpp | 31 ++++++++ assetFactory/assetFactory.h | 19 +++++ assetFactory/modelFactory.cpp | 31 -------- assetFactory/modelFactory.h | 19 ----- assetFactory/use.cpp | 4 +- res/brush47.xml | 2 +- test/Jamfile.jam | 2 +- test/test-assetFactory.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++ test/test-modelFactory.cpp | 173 ------------------------------------------ 9 files changed, 227 insertions(+), 227 deletions(-) create mode 100644 assetFactory/assetFactory.cpp create mode 100644 assetFactory/assetFactory.h delete mode 100644 assetFactory/modelFactory.cpp delete mode 100644 assetFactory/modelFactory.h create mode 100644 test/test-assetFactory.cpp delete mode 100644 test/test-modelFactory.cpp diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp new file mode 100644 index 0000000..0ee1f94 --- /dev/null +++ b/assetFactory/assetFactory.cpp @@ -0,0 +1,31 @@ +#include "assetFactory.h" +#include "cuboid.h" +#include "cylinder.h" +#include "modelFactoryMesh_fwd.h" +#include "object.h" +#include "plane.h" +#include "saxParse-persistence.h" +#include + +AssetFactory::AssetFactory() : + shapes { + {"plane", std::make_shared()}, + {"cuboid", std::make_shared()}, + {"cylinder", std::make_shared()}, + } +{ +} + +std::shared_ptr +AssetFactory::loadXML(const std::filesystem::path & filename) +{ + filesystem::FileStar file {filename.c_str(), "r"}; + return Persistence::SAXParsePersistence {}.loadState>(file); +} + +bool +AssetFactory::persist(Persistence::PersistenceStore & store) +{ + using MapObjects = Persistence::MapByMember; + return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects); +} diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h new file mode 100644 index 0000000..5cf90dd --- /dev/null +++ b/assetFactory/assetFactory.h @@ -0,0 +1,19 @@ +#pragma once + +#include "persistence.h" +#include "shape.h" +#include + +class AssetFactory : public Persistence::Persistable { +public: + using Shapes = std::map>; + + AssetFactory(); + [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); + + Shapes shapes; + +private: + friend Persistence::SelectionPtrBase, true>; + bool persist(Persistence::PersistenceStore & store) override; +}; diff --git a/assetFactory/modelFactory.cpp b/assetFactory/modelFactory.cpp deleted file mode 100644 index 2642900..0000000 --- a/assetFactory/modelFactory.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "modelFactory.h" -#include "cuboid.h" -#include "cylinder.h" -#include "modelFactoryMesh_fwd.h" -#include "object.h" -#include "plane.h" -#include "saxParse-persistence.h" -#include - -ModelFactory::ModelFactory() : - shapes { - {"plane", std::make_shared()}, - {"cuboid", std::make_shared()}, - {"cylinder", std::make_shared()}, - } -{ -} - -std::shared_ptr -ModelFactory::loadXML(const std::filesystem::path & filename) -{ - filesystem::FileStar file {filename.c_str(), "r"}; - return Persistence::SAXParsePersistence {}.loadState>(file); -} - -bool -ModelFactory::persist(Persistence::PersistenceStore & store) -{ - using MapObjects = Persistence::MapByMember; - return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects); -} diff --git a/assetFactory/modelFactory.h b/assetFactory/modelFactory.h deleted file mode 100644 index 94db055..0000000 --- a/assetFactory/modelFactory.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "persistence.h" -#include "shape.h" -#include - -class ModelFactory : public Persistence::Persistable { -public: - using Shapes = std::map>; - - ModelFactory(); - [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); - - Shapes shapes; - -private: - friend Persistence::SelectionPtrBase, true>; - bool persist(Persistence::PersistenceStore & store) override; -}; diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index 21e26f3..3b574c3 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -1,5 +1,5 @@ #include "use.h" -#include "modelFactory.h" +#include "assetFactory.h" Shape::CreatedFaces Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const @@ -17,7 +17,7 @@ struct Lookup : public Persistence::SelectionV { void setValue(std::string && str) override { - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("modelFactory"))) { + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { v = mf->shapes.at(str); } } diff --git a/res/brush47.xml b/res/brush47.xml index 8f1d934..3c6705a 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -1,5 +1,5 @@ - + diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 027b880..06f907a 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -52,6 +52,6 @@ run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; -run test-modelFactory.cpp : -- : ../res/brush47.xml : test ; +run test-assetFactory.cpp : -- : ../res/brush47.xml : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp new file mode 100644 index 0000000..64d6a62 --- /dev/null +++ b/test/test-assetFactory.cpp @@ -0,0 +1,173 @@ +#define BOOST_TEST_MODULE test_asset_factory + +#include "testHelpers.h" +#include "testRenderOutput.h" +#include +#include + +#include "assetFactory/assetFactory.h" +#include "assetFactory/factoryMesh.h" +#include "assetFactory/object.h" +#include "gfx/gl/sceneRenderer.h" +#include "lib/collection.hpp" +#include "lib/location.hpp" +#include "testMainWindow.h" +#include "ui/applicationBase.h" + +BOOST_GLOBAL_FIXTURE(ApplicationBase); +BOOST_GLOBAL_FIXTURE(TestMainWindow); + +const std::filesystem::path TMP {"/tmp"}; +class FactoryFixture : public TestRenderOutputSize, public SceneProvider { +public: + FactoryFixture() : sceneRenderer {size, output} { } + ~FactoryFixture() + { + glDisable(GL_DEBUG_OUTPUT); + auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); + std::filesystem::create_directories(outpath.parent_path()); + Texture::save(outImage, size, outpath.c_str()); + } + void + content(const SceneShader & shader) const override + { + shader.basic.use(Location {{0, 0, 0}, {0, 0, 0}}); + meshes.apply(&Mesh::Draw); + } + void + lights(const SceneShader & shader) const override + { + shader.pointLight.add({-3, 1, 5}, {1, 1, 1}, .1F); + } + void + environment(const SceneShader &, const SceneRenderer & sceneRenderer) const override + { + sceneRenderer.setAmbientLight({.2, .2, .2}); + sceneRenderer.setDirectionalLight({.3, .3, .3}, east + south + south + down, *this); + } + void + shadows(const ShadowMapper & mapper) const override + { + mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); + meshes.apply(&Mesh::Draw); + } + void + render(float dist = 10.f) + { + sceneRenderer.camera.setView({dist, dist, dist}, south + west + down); + sceneRenderer.render(*this); + } + Collection meshes; + +private: + SceneRenderer sceneRenderer; +}; + +BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); +BOOST_AUTO_TEST_CASE(brush47) +{ + AssetFactory assetFactory; + { + auto wheel = std::make_shared("wheel"); + { + auto wheelCylinder = wheel->uses.emplace_back(std::make_shared()); + wheelCylinder->type = assetFactory.shapes.at("cylinder"); + wheelCylinder->position = {0, 0, 0.571}; + wheelCylinder->scale = {1.142, 1.142, 0.07}; + wheelCylinder->rotation = {0, 0, half_pi}; + wheelCylinder->colour = "#2C3539"; + } + assetFactory.shapes.emplace(wheel->id, wheel); + } + { + auto axel = std::make_shared("axel"); + for (float x : {-1.f, 1.f}) { + auto wheel = axel->uses.emplace_back(std::make_shared()); + wheel->type = assetFactory.shapes.at("wheel"); + wheel->position = {x * 0.717f, 0, 0}; + wheel->rotation = {0, x == 1.f ? pi : 0.f, 0}; + } + assetFactory.shapes.emplace(axel->id, axel); + } + { + auto bogey = std::make_shared("bogey"); + for (float y : {-2.f, 0.f, 2.f}) { + auto axel = bogey->uses.emplace_back(std::make_shared()); + axel->type = assetFactory.shapes.at("axel"); + axel->position = {0, y, 0}; + } + assetFactory.shapes.emplace(bogey->id, bogey); + } + FactoryMesh::Collection factoryMeshes; + { + unsigned short b {0}; + for (float y : {-6.f, 6.f}) { + auto bogey = factoryMeshes.emplace_back(std::make_shared()); + bogey->id = "bogey" + std::to_string(b); + auto bogeyUse = bogey->uses.emplace_back(std::make_shared()); + bogeyUse->type = assetFactory.shapes.at("bogey"); + bogeyUse->position = {0, y, 0}; + bogeyUse->rotation = {0, b * pi, 0}; + b++; + } + } + { + auto body = factoryMeshes.emplace_back(std::make_shared()); + body->id = "body"; + body->size = {2.69f, 19.38f, 3.9f}; + { + auto bodyLower = body->uses.emplace_back(std::make_shared()); + bodyLower->type = assetFactory.shapes.at("cuboid"); + bodyLower->position = {0, 0, 1.2}; + bodyLower->scale = {2.69, 19.38, 1.5}; + bodyLower->colour = "#1111DD"; + bodyLower->faceControllers["bottom"].colour = "#2C3539"; + auto & bodyUpper = bodyLower->faceControllers["top"]; + bodyUpper.type = "extrude"; + bodyUpper.scale = {1, .95f, 1}; + bodyUpper.position = {0, 0, 1.0}; + auto & roof = bodyUpper.faceControllers["top"]; + roof.type = "extrude"; + roof.scale = {.6f, .9f, 0}; + roof.position = {0, 0, 0.2}; + roof.smooth = true; + } + { + auto batteryBox = body->uses.emplace_back(std::make_shared()); + batteryBox->type = assetFactory.shapes.at("cuboid"); + batteryBox->position = {0, 0, .2}; + batteryBox->scale = {2.6, 4.5, 1}; + batteryBox->colour = "#2C3539"; + } + } + std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), + [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { + return factoryMesh->createMesh(); + }); + + render(20); +} +BOOST_AUTO_TEST_CASE(brush47xml) +{ + auto mf = AssetFactory::loadXML(RESDIR "/brush47.xml"); + BOOST_REQUIRE(mf); + BOOST_REQUIRE_EQUAL(6, mf->shapes.size()); + BOOST_CHECK(mf->shapes.at("plane")); + BOOST_CHECK(mf->shapes.at("cylinder")); + BOOST_CHECK(mf->shapes.at("cuboid")); + BOOST_CHECK(mf->shapes.at("wheel")); + BOOST_CHECK(mf->shapes.at("axel")); + auto bogey = mf->shapes.at("bogey"); + BOOST_REQUIRE(bogey); + auto bogeyObj = std::dynamic_pointer_cast(bogey); + BOOST_CHECK_EQUAL(3, bogeyObj->uses.size()); + + FactoryMesh::Collection factoryMeshes; + std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), + [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { + return factoryMesh->createMesh(); + }); + + render(20); +} +BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/test-modelFactory.cpp b/test/test-modelFactory.cpp deleted file mode 100644 index 8f2d56c..0000000 --- a/test/test-modelFactory.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#define BOOST_TEST_MODULE test_model_factory - -#include "testHelpers.h" -#include "testRenderOutput.h" -#include -#include - -#include "assetFactory/factoryMesh.h" -#include "assetFactory/modelFactory.h" -#include "assetFactory/object.h" -#include "gfx/gl/sceneRenderer.h" -#include "lib/collection.hpp" -#include "lib/location.hpp" -#include "testMainWindow.h" -#include "ui/applicationBase.h" - -BOOST_GLOBAL_FIXTURE(ApplicationBase); -BOOST_GLOBAL_FIXTURE(TestMainWindow); - -const std::filesystem::path TMP {"/tmp"}; -class FactoryFixture : public TestRenderOutputSize, public SceneProvider { -public: - FactoryFixture() : sceneRenderer {size, output} { } - ~FactoryFixture() - { - glDisable(GL_DEBUG_OUTPUT); - auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); - std::filesystem::create_directories(outpath.parent_path()); - Texture::save(outImage, size, outpath.c_str()); - } - void - content(const SceneShader & shader) const override - { - shader.basic.use(Location {{0, 0, 0}, {0, 0, 0}}); - meshes.apply(&Mesh::Draw); - } - void - lights(const SceneShader & shader) const override - { - shader.pointLight.add({-3, 1, 5}, {1, 1, 1}, .1F); - } - void - environment(const SceneShader &, const SceneRenderer & sceneRenderer) const override - { - sceneRenderer.setAmbientLight({.2, .2, .2}); - sceneRenderer.setDirectionalLight({.3, .3, .3}, east + south + south + down, *this); - } - void - shadows(const ShadowMapper & mapper) const override - { - mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); - meshes.apply(&Mesh::Draw); - } - void - render(float dist = 10.f) - { - sceneRenderer.camera.setView({dist, dist, dist}, south + west + down); - sceneRenderer.render(*this); - } - Collection meshes; - -private: - SceneRenderer sceneRenderer; -}; - -BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); -BOOST_AUTO_TEST_CASE(brush47) -{ - ModelFactory modelFactory; - { - auto wheel = std::make_shared("wheel"); - { - auto wheelCylinder = wheel->uses.emplace_back(std::make_shared()); - wheelCylinder->type = modelFactory.shapes.at("cylinder"); - wheelCylinder->position = {0, 0, 0.571}; - wheelCylinder->scale = {1.142, 1.142, 0.07}; - wheelCylinder->rotation = {0, 0, half_pi}; - wheelCylinder->colour = "#2C3539"; - } - modelFactory.shapes.emplace(wheel->id, wheel); - } - { - auto axel = std::make_shared("axel"); - for (float x : {-1.f, 1.f}) { - auto wheel = axel->uses.emplace_back(std::make_shared()); - wheel->type = modelFactory.shapes.at("wheel"); - wheel->position = {x * 0.717f, 0, 0}; - wheel->rotation = {0, x == 1.f ? pi : 0.f, 0}; - } - modelFactory.shapes.emplace(axel->id, axel); - } - { - auto bogey = std::make_shared("bogey"); - for (float y : {-2.f, 0.f, 2.f}) { - auto axel = bogey->uses.emplace_back(std::make_shared()); - axel->type = modelFactory.shapes.at("axel"); - axel->position = {0, y, 0}; - } - modelFactory.shapes.emplace(bogey->id, bogey); - } - FactoryMesh::Collection factoryMeshes; - { - unsigned short b {0}; - for (float y : {-6.f, 6.f}) { - auto bogey = factoryMeshes.emplace_back(std::make_shared()); - bogey->id = "bogey" + std::to_string(b); - auto bogeyUse = bogey->uses.emplace_back(std::make_shared()); - bogeyUse->type = modelFactory.shapes.at("bogey"); - bogeyUse->position = {0, y, 0}; - bogeyUse->rotation = {0, b * pi, 0}; - b++; - } - } - { - auto body = factoryMeshes.emplace_back(std::make_shared()); - body->id = "body"; - body->size = {2.69f, 19.38f, 3.9f}; - { - auto bodyLower = body->uses.emplace_back(std::make_shared()); - bodyLower->type = modelFactory.shapes.at("cuboid"); - bodyLower->position = {0, 0, 1.2}; - bodyLower->scale = {2.69, 19.38, 1.5}; - bodyLower->colour = "#1111DD"; - bodyLower->faceControllers["bottom"].colour = "#2C3539"; - auto & bodyUpper = bodyLower->faceControllers["top"]; - bodyUpper.type = "extrude"; - bodyUpper.scale = {1, .95f, 1}; - bodyUpper.position = {0, 0, 1.0}; - auto & roof = bodyUpper.faceControllers["top"]; - roof.type = "extrude"; - roof.scale = {.6f, .9f, 0}; - roof.position = {0, 0, 0.2}; - roof.smooth = true; - } - { - auto batteryBox = body->uses.emplace_back(std::make_shared()); - batteryBox->type = modelFactory.shapes.at("cuboid"); - batteryBox->position = {0, 0, .2}; - batteryBox->scale = {2.6, 4.5, 1}; - batteryBox->colour = "#2C3539"; - } - } - std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), - [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { - return factoryMesh->createMesh(); - }); - - render(20); -} -BOOST_AUTO_TEST_CASE(brush47xml) -{ - auto mf = ModelFactory::loadXML(RESDIR "/brush47.xml"); - BOOST_REQUIRE(mf); - BOOST_REQUIRE_EQUAL(6, mf->shapes.size()); - BOOST_CHECK(mf->shapes.at("plane")); - BOOST_CHECK(mf->shapes.at("cylinder")); - BOOST_CHECK(mf->shapes.at("cuboid")); - BOOST_CHECK(mf->shapes.at("wheel")); - BOOST_CHECK(mf->shapes.at("axel")); - auto bogey = mf->shapes.at("bogey"); - BOOST_REQUIRE(bogey); - auto bogeyObj = std::dynamic_pointer_cast(bogey); - BOOST_CHECK_EQUAL(3, bogeyObj->uses.size()); - - FactoryMesh::Collection factoryMeshes; - std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), - [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { - return factoryMesh->createMesh(); - }); - - render(20); -} -BOOST_AUTO_TEST_SUITE_END(); -- cgit v1.2.3 From f91e2ea8d9e30b1e62c4f8784fddfd4eb9578d2d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Feb 2023 20:09:41 +0000 Subject: Move Appender in Persistence NS and simplify types --- assetFactory/object.cpp | 18 +----------------- lib/persistence.h | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/assetFactory/object.cpp b/assetFactory/object.cpp index faa9a17..ae5a301 100644 --- a/assetFactory/object.cpp +++ b/assetFactory/object.cpp @@ -16,24 +16,8 @@ Object::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) c return faces; } -template struct Appender : public Persistence::SelectionT> { - Appender(Container & c) : Persistence::SelectionT> {s}, container {c} { } - using Persistence::SelectionT>::SelectionT; - void - endObject(Persistence::Stack & stk) override - { - container.emplace_back(s); - stk.pop(); - } - -private: - std::shared_ptr s; - Container & container; -}; - bool Object::persist(Persistence::PersistenceStore & store) { - using UseAppend = Appender; - return STORE_TYPE && STORE_MEMBER(id) && STORE_NAME_HELPER("use", uses, UseAppend); + return STORE_TYPE && STORE_MEMBER(id) && STORE_NAME_HELPER("use", uses, Persistence::Appender); } diff --git a/lib/persistence.h b/lib/persistence.h index d55062a..5c8454c 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -273,6 +273,22 @@ namespace Persistence { Map & map; }; + template + struct Appender : public Persistence::SelectionT { + Appender(Container & c) : Persistence::SelectionT {s}, container {c} { } + using Persistence::SelectionT::SelectionT; + void + endObject(Persistence::Stack & stk) override + { + container.emplace_back(std::move(s)); + stk.pop(); + } + + private: + Type s; + Container & container; + }; + struct Persistable { Persistable() = default; virtual ~Persistable() = default; -- cgit v1.2.3 From 320e5cc574f0c8f83def034e36f6d0c57b1f75ac Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 22 Feb 2023 20:54:39 +0000 Subject: Fixup MapByMember to work with shared or unique ptr --- assetFactory/assetFactory.cpp | 2 +- lib/persistence.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 0ee1f94..470eacf 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -26,6 +26,6 @@ AssetFactory::loadXML(const std::filesystem::path & filename) bool AssetFactory::persist(Persistence::PersistenceStore & store) { - using MapObjects = Persistence::MapByMember; + using MapObjects = Persistence::MapByMember>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects); } diff --git a/lib/persistence.h b/lib/persistence.h index 5c8454c..0fc0200 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -256,20 +256,20 @@ namespace Persistence { } }; - template - struct MapByMember : public Persistence::SelectionT> { - MapByMember(Map & m) : Persistence::SelectionT> {s}, map {m} { } + template + struct MapByMember : public Persistence::SelectionT { + MapByMember(Map & m) : Persistence::SelectionT {s}, map {m} { } - using Persistence::SelectionT>::SelectionT; + using Persistence::SelectionT::SelectionT; void endObject(Persistence::Stack & stk) override { - map.emplace(std::invoke(Key, s), s); + map.emplace(std::invoke(Key, s), std::move(s)); stk.pop(); } private: - std::shared_ptr s; + Type s; Map & map; }; -- cgit v1.2.3 From df2a078c51cee464905c6fb1d1c7c4aa7873f6a1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 22 Feb 2023 23:40:46 +0000 Subject: Implement loading asset, mesh and face definitions --- assetFactory/assetFactory.cpp | 11 ++++++- assetFactory/assetFactory.h | 18 +++++++++- assetFactory/faceController.cpp | 10 +++++- assetFactory/faceController.h | 14 ++++++-- assetFactory/factoryMesh.cpp | 7 ++++ assetFactory/factoryMesh.h | 6 +++- assetFactory/use.cpp | 5 +-- assetFactory/use.h | 2 +- res/brush47.xml | 24 ++++++++++++-- test/test-assetFactory.cpp | 73 +++++++++++++++++++++++------------------ 10 files changed, 126 insertions(+), 44 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 470eacf..564ea28 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -27,5 +27,14 @@ bool AssetFactory::persist(Persistence::PersistenceStore & store) { using MapObjects = Persistence::MapByMember>; - return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects); + using MapAssets = Persistence::MapByMember; + return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) + && STORE_NAME_HELPER("asset", assets, MapAssets); +} + +bool +Asset::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(name) + && STORE_NAME_HELPER("mesh", meshes, Persistence::Appender); } diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 5cf90dd..9d79827 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -1,17 +1,33 @@ #pragma once +#include "factoryMesh.h" #include "persistence.h" #include "shape.h" #include +#include + +class Asset : public Persistence::Persistable, public StdTypeDefs { +public: + std::string id; + std::string name; + + FactoryMesh::Collection meshes; + +private: + friend Persistence::SelectionPtrBase, true>; + bool persist(Persistence::PersistenceStore & store) override; +}; class AssetFactory : public Persistence::Persistable { public: - using Shapes = std::map>; + using Shapes = std::map>; + using Assets = std::map>; AssetFactory(); [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); Shapes shapes; + Assets assets; private: friend Persistence::SelectionPtrBase, true>; diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 7ec7820..499f7e4 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -52,10 +52,18 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: } } for (const auto & [name, faceController] : faceControllers) { - faceController.apply(mesh, name, newFaces); + faceController->apply(mesh, name, newFaces); } faces.merge(std::move(newFaces)); } } } } + +bool +FaceController::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(colour) && STORE_MEMBER(type) && STORE_MEMBER(smooth) + && STORE_MEMBER(scale) && STORE_MEMBER(position) && STORE_MEMBER(rotation) + && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember); +} diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 9974caf..296210d 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -2,13 +2,14 @@ #include "modelFactoryMesh_fwd.h" #include "mutation.h" +#include "persistence.h" #include "shape.h" #include #include -class FaceController : public Mutation { +class FaceController : public Mutation, public Persistence::Persistable { public: - using FaceControllers = std::map; + using FaceControllers = std::map>; void apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const; @@ -17,4 +18,13 @@ public: std::string type; bool smooth {false}; FaceControllers faceControllers; + +private: + friend Persistence::SelectionPtrBase, false>; + bool persist(Persistence::PersistenceStore & store) override; + std::string + getId() const override + { + return {}; + }; }; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index baa031d..0fb72ad 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -29,3 +29,10 @@ FactoryMesh::createMesh() const } return std::make_shared(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); +} diff --git a/assetFactory/factoryMesh.h b/assetFactory/factoryMesh.h index f850893..4b6d3e5 100644 --- a/assetFactory/factoryMesh.h +++ b/assetFactory/factoryMesh.h @@ -4,11 +4,15 @@ #include "stdTypeDefs.hpp" #include "use.h" -class FactoryMesh : public StdTypeDefs { +class FactoryMesh : public Persistence::Persistable, public StdTypeDefs { public: Mesh::Ptr createMesh() const; std::string id; glm::vec3 size; Use::Collection uses; + +private: + friend Persistence::SelectionPtrBase, true>; + bool persist(Persistence::PersistenceStore & store) override; }; diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index 3b574c3..1f28332 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -6,7 +6,7 @@ Use::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) cons { auto faces = type->createMesh(mesh, mutation * getMatrix()); for (const auto & [name, faceController] : faceControllers) { - faceController.apply(mesh, name, faces); + faceController->apply(mesh, name, faces); } return faces; } @@ -27,5 +27,6 @@ bool Use::persist(Persistence::PersistenceStore & store) { return STORE_TYPE && STORE_HELPER(type, Lookup) && STORE_MEMBER(position) && STORE_MEMBER(scale) - && STORE_MEMBER(rotation); + && STORE_MEMBER(rotation) && STORE_MEMBER(colour) + && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember); } diff --git a/assetFactory/use.h b/assetFactory/use.h index 96f07f6..853af23 100644 --- a/assetFactory/use.h +++ b/assetFactory/use.h @@ -8,7 +8,7 @@ class Use : public StdTypeDefs, public Mutation, public Persistence::Persistable { public: - using FaceControllers = std::map; + using FaceControllers = std::map>; Shape::CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const; diff --git a/res/brush47.xml b/res/brush47.xml index 3c6705a..9cd654f 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -1,15 +1,33 @@ - + - + - + + + + + + + + + + + + + + + + + + + diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 64d6a62..fd7a41d 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -90,24 +90,24 @@ BOOST_AUTO_TEST_CASE(brush47) assetFactory.shapes.emplace(axel->id, axel); } { - auto bogey = std::make_shared("bogey"); + auto bogie = std::make_shared("bogie"); for (float y : {-2.f, 0.f, 2.f}) { - auto axel = bogey->uses.emplace_back(std::make_shared()); + auto axel = bogie->uses.emplace_back(std::make_shared()); axel->type = assetFactory.shapes.at("axel"); axel->position = {0, y, 0}; } - assetFactory.shapes.emplace(bogey->id, bogey); + assetFactory.shapes.emplace(bogie->id, bogie); } FactoryMesh::Collection factoryMeshes; { unsigned short b {0}; for (float y : {-6.f, 6.f}) { - auto bogey = factoryMeshes.emplace_back(std::make_shared()); - bogey->id = "bogey" + std::to_string(b); - auto bogeyUse = bogey->uses.emplace_back(std::make_shared()); - bogeyUse->type = assetFactory.shapes.at("bogey"); - bogeyUse->position = {0, y, 0}; - bogeyUse->rotation = {0, b * pi, 0}; + auto bogie = factoryMeshes.emplace_back(std::make_shared()); + bogie->id = "bogie" + std::to_string(b); + auto bogieUse = bogie->uses.emplace_back(std::make_shared()); + bogieUse->type = assetFactory.shapes.at("bogie"); + bogieUse->position = {0, y, 0}; + bogieUse->rotation = {0, b * pi, 0}; b++; } } @@ -115,23 +115,25 @@ BOOST_AUTO_TEST_CASE(brush47) auto body = factoryMeshes.emplace_back(std::make_shared()); body->id = "body"; body->size = {2.69f, 19.38f, 3.9f}; - { - auto bodyLower = body->uses.emplace_back(std::make_shared()); - bodyLower->type = assetFactory.shapes.at("cuboid"); - bodyLower->position = {0, 0, 1.2}; - bodyLower->scale = {2.69, 19.38, 1.5}; - bodyLower->colour = "#1111DD"; - bodyLower->faceControllers["bottom"].colour = "#2C3539"; - auto & bodyUpper = bodyLower->faceControllers["top"]; - bodyUpper.type = "extrude"; - bodyUpper.scale = {1, .95f, 1}; - bodyUpper.position = {0, 0, 1.0}; - auto & roof = bodyUpper.faceControllers["top"]; - roof.type = "extrude"; - roof.scale = {.6f, .9f, 0}; - roof.position = {0, 0, 0.2}; - roof.smooth = true; - } + auto bodyLower = body->uses.emplace_back(std::make_shared()); + bodyLower->type = assetFactory.shapes.at("cuboid"); + bodyLower->position = {0, 0, 1.2}; + bodyLower->scale = {2.69, 19.38, 1.5}; + bodyLower->colour = "#1111DD"; + auto & bottom = bodyLower->faceControllers["bottom"]; + bottom = std::make_unique(); + bottom->colour = "#2C3539"; + auto & bodyUpper = bodyLower->faceControllers["top"]; + bodyUpper = std::make_unique(); + bodyUpper->type = "extrude"; + bodyUpper->scale = {1, .95f, 1}; + bodyUpper->position = {0, 0, 1.0}; + auto & roof = bodyUpper->faceControllers["top"]; + roof = std::make_unique(); + roof->type = "extrude"; + roof->scale = {.6f, .9f, 0}; + roof->position = {0, 0, 0.2}; + roof->smooth = true; { auto batteryBox = body->uses.emplace_back(std::make_shared()); batteryBox->type = assetFactory.shapes.at("cuboid"); @@ -157,13 +159,20 @@ BOOST_AUTO_TEST_CASE(brush47xml) BOOST_CHECK(mf->shapes.at("cuboid")); BOOST_CHECK(mf->shapes.at("wheel")); BOOST_CHECK(mf->shapes.at("axel")); - auto bogey = mf->shapes.at("bogey"); - BOOST_REQUIRE(bogey); - auto bogeyObj = std::dynamic_pointer_cast(bogey); - BOOST_CHECK_EQUAL(3, bogeyObj->uses.size()); + auto bogie = mf->shapes.at("bogie"); + BOOST_REQUIRE(bogie); + auto bogieObj = std::dynamic_pointer_cast(bogie); + BOOST_CHECK_EQUAL(3, bogieObj->uses.size()); + BOOST_CHECK_EQUAL(1, mf->assets.size()); + auto brush47 = mf->assets.at("brush-47"); + BOOST_REQUIRE(brush47); + BOOST_CHECK_EQUAL(3, brush47->meshes.size()); + auto body = brush47->meshes.at(0); + BOOST_REQUIRE(body); + BOOST_CHECK_EQUAL("body", body->id); + BOOST_CHECK_EQUAL(2, body->uses.size()); - FactoryMesh::Collection factoryMeshes; - std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), + std::transform(brush47->meshes.begin(), brush47->meshes.end(), std::back_inserter(meshes.objects), [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { return factoryMesh->createMesh(); }); -- cgit v1.2.3 From b59ba638083122456bfeab0ff1fc7e3f3af99423 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 23 Feb 2023 00:39:06 +0000 Subject: Support parsing string values in persistence read --- lib/persistence.h | 47 +++++++++++++++++++++++++++++++++++++++++--- test/fixtures/json/conv.json | 4 ++++ test/test-persistence.cpp | 8 ++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/json/conv.json diff --git a/lib/persistence.h b/lib/persistence.h index 0fc0200..bfd27f0 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -87,13 +88,53 @@ namespace Persistence { T & v; }; - template struct SelectionT : public SelectionV { + template + concept Scalar = std::is_scalar_v; + template + concept NotScalar = (!Scalar); + + template struct SelectionT : public SelectionV { + using SelectionV::SelectionV; + using Selection::setValue; + + void + setValue(T evalue) override + { + std::swap(this->v, evalue); + } + + void + setValue(std::string && evalue) override + { + if constexpr (std::same_as) { + using namespace std::literals; + if (!(this->v = evalue == "true"sv)) { + if (evalue != "false"sv) { + throw std::runtime_error("Value conversion failure"); + } + } + } + else { + if (auto res = std::from_chars(evalue.c_str(), evalue.c_str() + evalue.length(), this->v).ec; + res != std::errc {}) { + throw std::runtime_error("Value conversion failure"); + } + } + } + + void + write(const Writer & out) const override + { + out.pushValue(this->v); + } + }; + + template struct SelectionT : public SelectionV { using SelectionV::SelectionV; using Selection::setValue; - using P = std::conditional_t, T, T &&>; void - setValue(P evalue) override + setValue(T && evalue) override { std::swap(this->v, evalue); } diff --git a/test/fixtures/json/conv.json b/test/fixtures/json/conv.json new file mode 100644 index 0000000..1b690d5 --- /dev/null +++ b/test/fixtures/json/conv.json @@ -0,0 +1,4 @@ +{ + "bl": "true", + "flt": "3.14" +} diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index faa0d19..6bee010 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -208,6 +208,14 @@ BOOST_FIXTURE_TEST_CASE(load_vector_ptr, JPP) BOOST_CHECK(to->vptr.at(3)->str.empty()); } +BOOST_FIXTURE_TEST_CASE(test_conversion, JPP) +{ + auto to = load_json>(FIXTURESDIR "json/conv.json"); + BOOST_REQUIRE(to); + BOOST_CHECK_EQUAL(to->bl, true); + BOOST_CHECK_EQUAL(to->flt, 3.14F); +} + struct SharedTestObject : public Persistence::Persistable { SharedTestObject() = default; -- cgit v1.2.3 From 96560a2f927831d1b98e654e80ee085f3ef562d8 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 23 Feb 2023 00:42:40 +0000 Subject: Smooth roof now supported --- res/brush47.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/brush47.xml b/res/brush47.xml index 9cd654f..841c951 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -17,8 +17,7 @@ - - + -- cgit v1.2.3 From 27c9f248d374d1327e90e1627ae3a98d148f8972 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 23 Feb 2023 00:55:36 +0000 Subject: Fix axel definition Test output images now identical --- res/brush47.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/brush47.xml b/res/brush47.xml index 841c951..9fd4fe2 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -4,8 +4,8 @@ - - + + -- cgit v1.2.3 From ba761a571ab9d62fa21e78d1b53f885f82b48446 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 24 Feb 2023 19:26:57 +0000 Subject: Fixed up vector operator+ element --- lib/collections.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/collections.hpp b/lib/collections.hpp index 47967b2..31e5ab8 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -82,13 +82,13 @@ operator+=(std::vector & in, std::vector && src) return in; } -template +template constexpr auto -operator+(std::vector && in, std::vector && src) +operator+(const std::vector & in, Vn && vn) { - in.reserve(in.size() + src.size()); - std::move(src.begin(), src.end(), std::back_inserter(in)); - return in; + auto out(in); + out.emplace_back(std::forward(vn)); + return out; } template typename Direction = std::plus> -- cgit v1.2.3 From 2c6257a4460de7b0c75723aebf96cf265e078530 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 24 Feb 2023 19:29:27 +0000 Subject: Support for model colours mixed with textures --- gfx/gl/shaders/basicShader.fs | 3 ++- gfx/gl/shaders/basicShader.vs | 3 +++ gfx/gl/shaders/landmassShader.vs | 3 +++ gfx/models/mesh.cpp | 2 +- gfx/models/vertex.hpp | 5 +++-- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/gfx/gl/shaders/basicShader.fs b/gfx/gl/shaders/basicShader.fs index 9c4945b..93f0a3f 100644 --- a/gfx/gl/shaders/basicShader.fs +++ b/gfx/gl/shaders/basicShader.fs @@ -3,6 +3,7 @@ in vec3 FragPos; in vec2 TexCoords; in vec3 Normal; +in vec4 Colour; out vec4 gPosition; out vec4 gNormal; @@ -16,5 +17,5 @@ main() float clear = round(texture(texture0, TexCoords).a); gPosition = vec4(FragPos, clear); gNormal = vec4(Normal, clear); - gAlbedoSpec = texture(texture0, TexCoords); + gAlbedoSpec = mix(texture(texture0, TexCoords), vec4(Colour.rgb, 1), Colour.a); } diff --git a/gfx/gl/shaders/basicShader.vs b/gfx/gl/shaders/basicShader.vs index bc7ea5d..ff9a401 100644 --- a/gfx/gl/shaders/basicShader.vs +++ b/gfx/gl/shaders/basicShader.vs @@ -3,10 +3,12 @@ in vec3 position; in vec2 texCoord; in vec3 normal; +in vec4 colour; out vec3 FragPos; out vec2 TexCoords; out vec3 Normal; +out vec4 Colour; uniform mat4 viewProjection; uniform mat4 model; @@ -19,6 +21,7 @@ main() FragPos = worldPos.xyz; TexCoords = texCoord; Normal = (model * vec4(normal, 0.0)).xyz; + Colour = colour; gl_Position = viewProjection * worldPos; } diff --git a/gfx/gl/shaders/landmassShader.vs b/gfx/gl/shaders/landmassShader.vs index 6bf39b0..30c4ef4 100644 --- a/gfx/gl/shaders/landmassShader.vs +++ b/gfx/gl/shaders/landmassShader.vs @@ -3,10 +3,12 @@ in vec3 position; in vec2 texCoord; in vec3 normal; +in vec4 colour; out vec3 FragPos; out vec2 TexCoords; out vec3 Normal; +out vec4 Colour; uniform mat4 viewProjection; @@ -16,6 +18,7 @@ main() FragPos = position; TexCoords = texCoord; Normal = normal; + Colour = colour; gl_Position = viewProjection * vec4(position, 1.0); } diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index 85be84c..3db1ad5 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -7,7 +7,7 @@ Mesh::Mesh(const std::span vertices, const std::span indices, GLenum m) : m_numIndices {static_cast(indices.size())}, mode {m} { - VertexArrayObject::configure<&Vertex::pos, &Vertex::texCoord, &Vertex::normal>( + VertexArrayObject::configure<&Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour>( m_vertexArrayObject, m_vertexArrayBuffers[0], m_vertexArrayBuffers[1], vertices, indices); } diff --git a/gfx/models/vertex.hpp b/gfx/models/vertex.hpp index 095c82f..325aab1 100644 --- a/gfx/models/vertex.hpp +++ b/gfx/models/vertex.hpp @@ -4,12 +4,13 @@ class Vertex { public: - constexpr Vertex(glm::vec3 pos, glm::vec2 texCoord, glm::vec3 normal) : - pos {std::move(pos)}, texCoord {std::move(texCoord)}, normal {std::move(normal)} + constexpr Vertex(glm::vec3 pos, glm::vec2 texCoord, glm::vec3 normal, glm::vec4 colour = {}) : + pos {std::move(pos)}, texCoord {std::move(texCoord)}, normal {std::move(normal)}, colour {std::move(colour)} { } glm::vec3 pos; glm::vec2 texCoord; glm::vec3 normal; + glm::vec4 colour; }; -- cgit v1.2.3 From 7d0decccaac3aa564b549d91a36279e7aca0814e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 24 Feb 2023 19:30:30 +0000 Subject: Support for recursive colouring of asset factory faces Updates colours in sample model. --- assetFactory/faceController.cpp | 29 +++++++++++++------- assetFactory/faceController.h | 7 ++--- assetFactory/factoryMesh.cpp | 4 ++- assetFactory/modelFactoryMesh.h | 3 ++- assetFactory/style.cpp | 59 +++++++++++++++++++++++++++++++++++++++++ assetFactory/style.h | 22 +++++++++++++++ assetFactory/use.cpp | 5 ++-- assetFactory/use.h | 4 +-- res/brush47.xml | 16 ++++++----- 9 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 assetFactory/style.cpp create mode 100644 assetFactory/style.h diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 499f7e4..9f057da 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -1,14 +1,19 @@ #include "faceController.h" +#include "collections.hpp" #include "maths.h" #include "modelFactoryMesh.h" void -FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const +FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, + Shape::CreatedFaces & faces) const { + const auto controlledFacesRange = faces.equal_range(name); + const std::vector controlledFaces(controlledFacesRange.first, controlledFacesRange.second); + if (controlledFaces.empty()) { + throw std::runtime_error("Named face(s) do not exist: " + name); + } if (!type.empty()) { const auto mutation = getMatrix(); - const auto controlledFacesRange = faces.equal_range(name); - const std::vector controlledFaces(controlledFacesRange.first, controlledFacesRange.second); faces.erase(name); for (const auto & cf : controlledFaces) { // get face vertices @@ -23,9 +28,6 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: const auto vertexCount = points.size(); const auto centre = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast(vertexCount); - if (smooth) { - mesh.property(mesh.smoothFaceProperty, cf.second) = true; - } if (type == "extrude") { Shape::CreatedFaces newFaces; // mutate points @@ -38,7 +40,6 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: return mesh.add_vertex({p.x, p.y, p.z}); }); // create new faces - mesh.delete_face(cf.second); for (size_t idx {}; idx < vertexCount; ++idx) { const auto next = (idx + 1) % vertexCount; @@ -51,11 +52,21 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: mesh.property(mesh.smoothFaceProperty, face) = true; } } + applyStyle(mesh, parents + this, newFaces); for (const auto & [name, faceController] : faceControllers) { - faceController->apply(mesh, name, newFaces); + 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); } } } @@ -63,7 +74,7 @@ FaceController::apply(ModelFactoryMesh & mesh, const std::string & name, Shape:: bool FaceController::persist(Persistence::PersistenceStore & store) { - return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(colour) && STORE_MEMBER(type) && STORE_MEMBER(smooth) + 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); } diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 296210d..0618388 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -4,17 +4,18 @@ #include "mutation.h" #include "persistence.h" #include "shape.h" +#include "style.h" #include #include -class FaceController : public Mutation, public Persistence::Persistable { +class FaceController : public Mutation, public Style, public Persistence::Persistable { public: using FaceControllers = std::map>; - void apply(ModelFactoryMesh & mesh, const std::string & name, Shape::CreatedFaces & faces) const; + void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, + Shape::CreatedFaces & faces) const; std::string id; - std::string colour; std::string type; bool smooth {false}; FaceControllers faceControllers; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 0fb72ad..0cfed85 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -21,10 +21,12 @@ FactoryMesh::createMesh() const std::vector 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)); + : mesh.property(mesh.face_normals_pph(), face), + colour); } } return std::make_shared(vertices, vectorOfN(vertices.size())); diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index d0ffc26..7d222f5 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -20,11 +20,12 @@ namespace OpenMesh { } struct ModelFactoryTraits : public OpenMesh::DefaultTraits { - FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + 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; using Normal = OpenMesh::glmvec; + using Color = OpenMesh::glmvec; }; struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT { diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp new file mode 100644 index 0000000..099d81a --- /dev/null +++ b/assetFactory/style.cpp @@ -0,0 +1,59 @@ +#include "style.h" + +ModelFactoryMesh::Color +Style::parseColour(const std::string_view & in) +{ + if (in.empty()) { + return {}; + } + if (in[0] == '#') { + ModelFactoryMesh::Color out {0, 0, 0, 1}; + std::generate_n(out.begin(), (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(channel) / 256.F; + }); + return out; + } + return {}; +} + +void +Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const +{ + if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { + const auto parsedColour = parseColour(effectiveColour); + for (const auto & face : faces) { + mesh.set_color(face.second, parsedColour); + } + } +} + +void +Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const +{ + if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { + const auto parsedColour = parseColour(effectiveColour); + mesh.set_color(face, parsedColour); + } +} + +std::string_view +Style::getProperty(const StyleStack & parents, std::string Style::*member) +{ + if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), + [&member](auto && s) { + return !(s->*member).empty(); + }); + itr != parents.rend()) { + return (*itr)->*member; + } + return {}; +} + +bool +Style::persist(Persistence::PersistenceStore & store) +{ + return STORE_MEMBER(colour); +} diff --git a/assetFactory/style.h b/assetFactory/style.h new file mode 100644 index 0000000..0c7ad5a --- /dev/null +++ b/assetFactory/style.h @@ -0,0 +1,22 @@ +#pragma once + +#include "modelFactoryMesh.h" +#include "persistence.h" +#include "shape.h" +#include + +class Style { +public: + using StyleStack = std::vector; + + static ModelFactoryMesh::Color parseColour(const std::string_view &); + void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const; + void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const; + + static std::string_view getProperty(const StyleStack & parents, std::string Style::*member); + + std::string colour; + +protected: + bool persist(Persistence::PersistenceStore & store); +}; diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index 1f28332..a6fac5c 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -5,8 +5,9 @@ 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, name, faces); + faceController->apply(mesh, {this}, name, faces); } return faces; } @@ -27,6 +28,6 @@ bool Use::persist(Persistence::PersistenceStore & store) { return STORE_TYPE && STORE_HELPER(type, Lookup) && STORE_MEMBER(position) && STORE_MEMBER(scale) - && STORE_MEMBER(rotation) && STORE_MEMBER(colour) + && STORE_MEMBER(rotation) && Style::persist(store) && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember); } diff --git a/assetFactory/use.h b/assetFactory/use.h index 853af23..5b61eca 100644 --- a/assetFactory/use.h +++ b/assetFactory/use.h @@ -5,15 +5,15 @@ #include "persistence.h" #include "shape.h" #include "stdTypeDefs.hpp" +#include "style.h" -class Use : public StdTypeDefs, public Mutation, public Persistence::Persistable { +class Use : public StdTypeDefs, public Mutation, public Style, public Persistence::Persistable { public: using FaceControllers = std::map>; Shape::CreatedFaces createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const; Shape::CPtr type; - std::string colour; FaceControllers faceControllers; private: diff --git a/res/brush47.xml b/res/brush47.xml index 9fd4fe2..fe3d114 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -14,19 +14,21 @@ - - - - + + + + + + - + - + - + -- cgit v1.2.3 From b76d161a968ff4a2817e9cfe5c02d3538ef6029f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 25 Feb 2023 03:05:24 +0000 Subject: Add face name property and wrapper to add a named face --- assetFactory/modelFactoryMesh.cpp | 9 +++++++++ assetFactory/modelFactoryMesh.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp index 6c772ad..f9ee6a0 100644 --- a/assetFactory/modelFactoryMesh.cpp +++ b/assetFactory/modelFactoryMesh.cpp @@ -3,4 +3,13 @@ ModelFactoryMesh::ModelFactoryMesh() { add_property(smoothFaceProperty); + add_property(nameFaceProperty); +} + +std::pair +ModelFactoryMesh::add_namedFace(std::string name, std::vector 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 index 7d222f5..b4f5254 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -32,4 +32,7 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT smoothFaceProperty; + OpenMesh::FPropHandleT nameFaceProperty; + + std::pair add_namedFace(std::string name, std::vector p); }; -- cgit v1.2.3 From f6f085d3744990a6e80927aa17f4c5d77456d4f9 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 25 Feb 2023 03:10:25 +0000 Subject: Primitives add named faces --- assetFactory/cuboid.cpp | 12 ++++++------ assetFactory/cylinder.cpp | 10 +++++----- assetFactory/plane.cpp | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp index 86114e5..24fe4a4 100644 --- a/assetFactory/cuboid.cpp +++ b/assetFactory/cuboid.cpp @@ -19,11 +19,11 @@ Cuboid::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) c const auto vhs = addMutatedToMesh(mesh, VERTICES, mutation); return { - {"top", mesh.add_face({vhs[4], vhs[5], vhs[6], vhs[7]})}, - {"bottom", mesh.add_face({vhs[0], vhs[1], vhs[2], vhs[3]})}, - {"left", mesh.add_face({vhs[0], vhs[7], vhs[6], vhs[1]})}, - {"right", mesh.add_face({vhs[2], vhs[5], vhs[4], vhs[3]})}, - {"front", mesh.add_face({vhs[0], vhs[3], vhs[4], vhs[7]})}, - {"back", mesh.add_face({vhs[2], vhs[1], vhs[6], vhs[5]})}, + 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/cylinder.cpp b/assetFactory/cylinder.cpp index d51dc37..cf0dbfb 100644 --- a/assetFactory/cylinder.cpp +++ b/assetFactory/cylinder.cpp @@ -28,19 +28,19 @@ Cylinder::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) const auto xyz2b = (xy2 ^ 0) % mutation; const auto xyz1t = (xy1 ^ 1) % mutation; const auto xyz2t = (xy2 ^ 1) % mutation; - return std::make_pair(std::string {"edge"}, - mesh.add_face({ + 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.emplace("bottom", mesh.add_face(bottom)); - surface.emplace("top", mesh.add_face(top)); + surface.insert(mesh.add_namedFace("bottom", bottom)); + surface.insert(mesh.add_namedFace("top", top)); return surface; } diff --git a/assetFactory/plane.cpp b/assetFactory/plane.cpp index 37c8194..563c4e9 100644 --- a/assetFactory/plane.cpp +++ b/assetFactory/plane.cpp @@ -11,5 +11,5 @@ Plane::createMesh(ModelFactoryMesh & mesh, const Mutation::Matrix & mutation) co {n, y, z}, }}; - return {{"plane", mesh.add_face(addMutatedToMesh(mesh, VERTICES, mutation))}}; + return {mesh.add_namedFace("plane", addMutatedToMesh(mesh, VERTICES, mutation))}; } -- cgit v1.2.3 From f25a47f022e45137149a0726a73cbd8b1fda4f04 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 25 Feb 2023 03:23:07 +0000 Subject: Helpers to create container instances from ranges --- lib/collections.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/collections.hpp b/lib/collections.hpp index 31e5ab8..18e6147 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -15,6 +15,8 @@ concept SequentialCollection = requires(T c) { c.data() } -> std::same_as; }; +template +concept IterableCollection = std::is_same_v().begin()), decltype(std::declval().end())>; template constexpr std::array @@ -102,3 +104,17 @@ vectorOfN(std::integral auto N, unsigned int start = {}, unsigned int step = 1) }); return v; } + +template typename Rtn = std::vector, IterableCollection In> +auto +materializeRange(In && in) +{ + return Rtn(in.begin(), in.end()); +} + +template typename Rtn = std::vector, typename In> +auto +materializeRange(const std::pair & in) +{ + return Rtn(in.first, in.second); +} -- cgit v1.2.3 From 43856fbd3b4a23e9ce41a85a641479a11413602d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 25 Feb 2023 03:31:12 +0000 Subject: Name new faces based on adjacent faces when extruding --- assetFactory/faceController.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 9f057da..b485c80 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -7,23 +7,30 @@ void FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const { - const auto controlledFacesRange = faces.equal_range(name); - const std::vector controlledFaces(controlledFacesRange.first, controlledFacesRange.second); + 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 face vertices - const auto faceVertexRange = mesh.fv_range(cf.second); // get points - const std::vector baseVertices(faceVertexRange.begin(), faceVertexRange.end()); - std::vector points; - std::transform( - faceVertexRange.begin(), faceVertexRange.end(), std::back_inserter(points), [&mesh](auto && v) { - return mesh.point(v); + const auto baseVertices {materializeRange(mesh.fv_range(cf.second))}; + auto points = std::accumulate(baseVertices.begin(), baseVertices.end(), std::vector {}, + [&mesh](auto && out, auto && v) { + out.push_back(mesh.point(v)); + return std::move(out); }); const auto vertexCount = points.size(); const auto centre @@ -40,11 +47,15 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const 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; - newFaces.emplace("extrusion", - mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]})); + 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) { -- cgit v1.2.3 From 29faadd99b2c6fd8dd5bfd9842d1361d82690f96 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 25 Feb 2023 03:32:24 +0000 Subject: Colour windows of sample model --- res/brush47.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/brush47.xml b/res/brush47.xml index fe3d114..1ece78f 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -19,6 +19,8 @@ + + -- cgit v1.2.3 From 2e440265e212c18208e683edab833fb9d3b735b4 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 00:26:20 +0000 Subject: Add a perf test over the asset factory --- test/Jamfile.jam | 5 ++++- test/perf-assetFactory.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/perf-assetFactory.cpp diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 06f907a..907632e 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -2,6 +2,7 @@ import testing ; import sequence ; lib boost_unit_test_framework ; +lib benchmark ; path-constant res : ../res ; path-constant fixtures : fixtures ; @@ -39,7 +40,7 @@ project : requirements tidy:hicpp-vararg tidy:boost ; -lib test : [ glob *.cpp : test-*.cpp ] ; +lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] ; run test-collection.cpp ; run test-obj.cpp ; @@ -53,5 +54,7 @@ run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; run test-assetFactory.cpp : -- : ../res/brush47.xml : test ; +run perf-assetFactory.cpp : : : benchmark test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; +explicit perf-assetFactory ; diff --git a/test/perf-assetFactory.cpp b/test/perf-assetFactory.cpp new file mode 100644 index 0000000..f702fe7 --- /dev/null +++ b/test/perf-assetFactory.cpp @@ -0,0 +1,32 @@ +#include "assetFactory/assetFactory.h" +#include "assetFactory/factoryMesh.h" +#include "testMainWindow.h" +#include "ui/applicationBase.h" +#include + +static void +brush47xml_load(benchmark::State & state) +{ + for (auto _ : state) { + benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/brush47.xml")); + } +} + +static void +brush47xml_mesh(benchmark::State & state) +{ + TestMainWindow window; + + const auto mf = AssetFactory::loadXML(RESDIR "/brush47.xml"); + const auto brush47 = mf->assets.at("brush-47"); + for (auto _ : state) { + std::for_each(brush47->meshes.begin(), brush47->meshes.end(), [](const FactoryMesh::CPtr & factoryMesh) { + factoryMesh->createMesh(); + }); + } +} + +BENCHMARK(brush47xml_load); +BENCHMARK(brush47xml_mesh); + +BENCHMARK_MAIN(); -- cgit v1.2.3 From aac5672de67798337618d0bceb93b2c799d3edc3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 01:05:11 +0000 Subject: Add nodiscard to many collections helpers --- lib/collections.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/collections.hpp b/lib/collections.hpp index 18e6147..391647e 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -19,7 +19,7 @@ template concept IterableCollection = std::is_same_v().begin()), decltype(std::declval().end())>; template -constexpr std::array +[[nodiscard]] constexpr std::array operator+(const std::array & a, const std::array & b) { std::array r; @@ -30,7 +30,7 @@ operator+(const std::array & a, const std::array & b) } template -constexpr std::array, first * second> +[[nodiscard]] constexpr std::array, first * second> operator*(const std::array & a, const std::array & b) { std::array, first * second> r; @@ -44,7 +44,7 @@ operator*(const std::array & a, const std::array & b) } template -constexpr auto +[[nodiscard]] constexpr auto operator*(const std::array & in, auto && f) { std::array out; @@ -66,7 +66,7 @@ operator*=(std::span & in, auto && f) } template typename C, typename... T> -constexpr auto +[[nodiscard]] constexpr auto operator*(const C & in, auto && f) { C out; @@ -85,7 +85,7 @@ operator+=(std::vector & in, std::vector && src) } template -constexpr auto +[[nodiscard]] constexpr auto operator+(const std::vector & in, Vn && vn) { auto out(in); @@ -94,7 +94,7 @@ operator+(const std::vector & in, Vn && vn) } template typename Direction = std::plus> -static auto +[[nodiscard]] static auto vectorOfN(std::integral auto N, unsigned int start = {}, unsigned int step = 1) { std::vector v; @@ -106,14 +106,14 @@ vectorOfN(std::integral auto N, unsigned int start = {}, unsigned int step = 1) } template typename Rtn = std::vector, IterableCollection In> -auto +[[nodiscard]] auto materializeRange(In && in) { return Rtn(in.begin(), in.end()); } template typename Rtn = std::vector, typename In> -auto +[[nodiscard]] auto materializeRange(const std::pair & in) { return Rtn(in.first, in.second); -- cgit v1.2.3 From 90b4839af82e9c0188f4d37c0587e613756abcf9 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 19:37:05 +0000 Subject: operator*= can work on any iterable collection --- lib/collections.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/collections.hpp b/lib/collections.hpp index 391647e..16870be 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -55,9 +55,8 @@ operator*(const std::array & in, auto && f) return out; } -template constexpr auto & -operator*=(std::span & in, auto && f) +operator*=(IterableCollection auto & in, auto && f) { for (auto & v : in) { f(v); -- cgit v1.2.3 From b7b9eb7bb9deb6704f276fb3b8fd67ba370caf3e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 19:38:14 +0000 Subject: Load the X11 RGB colour definitions into a map --- assetFactory/assetFactory.cpp | 28 +++++++++++++++++++++++++++- assetFactory/assetFactory.h | 6 ++++++ test/fixtures/rgb.txt | 20 ++++++++++++++++++++ test/test-assetFactory.cpp | 30 ++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/rgb.txt diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 564ea28..016f29e 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -1,4 +1,5 @@ #include "assetFactory.h" +#include "collections.hpp" #include "cuboid.h" #include "cylinder.h" #include "modelFactoryMesh_fwd.h" @@ -12,7 +13,8 @@ AssetFactory::AssetFactory() : {"plane", std::make_shared()}, {"cuboid", std::make_shared()}, {"cylinder", std::make_shared()}, - } + }, + colours {parseX11RGB("/usr/share/X11/rgb.txt")} { } @@ -23,6 +25,30 @@ AssetFactory::loadXML(const std::filesystem::path & filename) return Persistence::SAXParsePersistence {}.loadState>(file); } +AssetFactory::Colours +AssetFactory::parseX11RGB(const char * path) +{ + filesystem::FileStar rgb {path, "r"}; + Colours out; + glm::u8vec3 colour; + char inname[BUFSIZ]; + while (fscanf(rgb, "%hhu %hhu %hhu %[^\n\r]s", &colour.r, &colour.g, &colour.b, inname) == 4) { + std::string name {inname}; + normalizeColourName(name); + out.emplace(std::move(name), colour); + } + return out; +} + +void +AssetFactory::normalizeColourName(std::string & name) +{ + std::erase_if(name, ::isblank); + name *= [l = std::locale {}](auto & ch) { + ch = std::tolower(ch, l); + }; +} + bool AssetFactory::persist(Persistence::PersistenceStore & store) { diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 9d79827..a68b460 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -22,12 +22,18 @@ class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; using Assets = std::map>; + using Colour = glm::u8vec3; + using Colours = std::map>; AssetFactory(); [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); Shapes shapes; Assets assets; + Colours colours; + + static Colours parseX11RGB(const char * rgbtxtpath); + static void normalizeColourName(std::string &); private: friend Persistence::SelectionPtrBase, true>; diff --git a/test/fixtures/rgb.txt b/test/fixtures/rgb.txt new file mode 100644 index 0000000..2fab7af --- /dev/null +++ b/test/fixtures/rgb.txt @@ -0,0 +1,20 @@ +127 255 0 chartreuse1 +190 190 190 x11 gray +169 169 169 DarkGrey + 0 255 255 cyan +173 173 173 gray68 +202 225 255 LightSteelBlue1 + 72 209 204 medium turquoise +224 238 224 honeydew2 +238 197 145 burlywood2 +205 133 63 peru + 28 28 28 gray11 + 83 134 139 CadetBlue4 +139 76 57 salmon4 +238 232 170 pale goldenrod +112 128 144 slate grey +255 255 0 yellow1 +159 121 238 MediumPurple2 +190 190 190 gray + 66 66 66 grey26 +0 0 139 DarkBlue diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index fd7a41d..89f6bf0 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -11,6 +11,7 @@ #include "gfx/gl/sceneRenderer.h" #include "lib/collection.hpp" #include "lib/location.hpp" +#include "lib/stream_support.hpp" #include "testMainWindow.h" #include "ui/applicationBase.h" @@ -180,3 +181,32 @@ BOOST_AUTO_TEST_CASE(brush47xml) render(20); } BOOST_AUTO_TEST_SUITE_END(); + +template using InOut = std::tuple; +BOOST_DATA_TEST_CASE(normalizeColourName, + boost::unit_test::data::make>({ + {"", ""}, + {"black", "black"}, + {" black ", "black"}, + {" b l a c k ", "black"}, + {" B L A c k ", "black"}, + {"BLAck ", "black"}, + {"BLACK ", "black"}, + {"BlAck ", "black"}, + {"Bl Ack ", "black"}, + }), + in_, exp) +{ + auto in {in_}; + BOOST_CHECK_NO_THROW(AssetFactory::normalizeColourName(in)); + BOOST_CHECK_EQUAL(in, exp); +} + +BOOST_AUTO_TEST_CASE(parseX11RGB) +{ + const auto parsedColours = AssetFactory::parseX11RGB(FIXTURESDIR "rgb.txt"); + BOOST_REQUIRE_EQUAL(parsedColours.size(), 20); + BOOST_CHECK_EQUAL(parsedColours.at("cyan"), AssetFactory::Colour(0, 255, 255)); + BOOST_CHECK_EQUAL(parsedColours.at("slategrey"), AssetFactory::Colour(112, 128, 144)); + BOOST_CHECK_EQUAL(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(202, 225, 255)); +} -- cgit v1.2.3 From 5c703a1549b88339ca48ac3c48f67ab7503d223a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 23:56:03 +0000 Subject: Support for named colours in assets Fixes up some error handling in colour parser. --- assetFactory/style.cpp | 14 ++++++++++++-- res/brush47.xml | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 099d81a..d2977a7 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -1,12 +1,16 @@ #include "style.h" +#include "assetFactory.h" ModelFactoryMesh::Color Style::parseColour(const std::string_view & in) { if (in.empty()) { - return {}; + 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"); + } ModelFactoryMesh::Color out {0, 0, 0, 1}; std::generate_n(out.begin(), (in.length() - 1) / 2, [in = in.data() + 1]() mutable { uint8_t channel; @@ -16,7 +20,13 @@ Style::parseColour(const std::string_view & in) }); return out; } - return {}; + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { + const auto out = glm::vec3 {colour->second} / 256.F; + return {out.r, out.g, out.b, 1.f}; + } + } + throw std::runtime_error("No such asset factory colour"); } void diff --git a/res/brush47.xml b/res/brush47.xml index 1ece78f..477610c 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -16,15 +16,15 @@ - - + + - + -- cgit v1.2.3 From 7a70e75656d31428c9bbbd51fbf1ca920e577ed1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 28 Feb 2023 01:24:18 +0000 Subject: Remove to specify if the Selection pointer type is shared or not Inferred based on whether the pointer is copyable or not. --- assetFactory/assetFactory.h | 4 ++-- assetFactory/faceController.h | 2 +- assetFactory/factoryMesh.h | 2 +- assetFactory/object.h | 2 +- assetFactory/use.h | 2 +- lib/persistence.h | 13 +++++++------ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index a68b460..42082eb 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -14,7 +14,7 @@ public: FactoryMesh::Collection meshes; private: - friend Persistence::SelectionPtrBase, true>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; }; @@ -36,6 +36,6 @@ public: static void normalizeColourName(std::string &); private: - friend Persistence::SelectionPtrBase, true>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; }; diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 0618388..10a226a 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -21,7 +21,7 @@ public: FaceControllers faceControllers; private: - friend Persistence::SelectionPtrBase, false>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; std::string getId() const override diff --git a/assetFactory/factoryMesh.h b/assetFactory/factoryMesh.h index 4b6d3e5..bbeb870 100644 --- a/assetFactory/factoryMesh.h +++ b/assetFactory/factoryMesh.h @@ -13,6 +13,6 @@ public: Use::Collection uses; private: - friend Persistence::SelectionPtrBase, true>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; }; diff --git a/assetFactory/object.h b/assetFactory/object.h index 1069f66..f3726c7 100644 --- a/assetFactory/object.h +++ b/assetFactory/object.h @@ -16,7 +16,7 @@ public: std::string id; private: - friend Persistence::SelectionPtrBase, true>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; std::string getId() const override diff --git a/assetFactory/use.h b/assetFactory/use.h index 5b61eca..5e4c35f 100644 --- a/assetFactory/use.h +++ b/assetFactory/use.h @@ -17,7 +17,7 @@ public: FaceControllers faceControllers; private: - friend Persistence::SelectionPtrBase, true>; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; std::string getId() const override diff --git a/lib/persistence.h b/lib/persistence.h index bfd27f0..458ba4d 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -381,7 +381,8 @@ namespace Persistence { using SeenSharedObjects = std::map; inline SeenSharedObjects seenSharedObjects; - template struct SelectionPtrBase : public SelectionV { + template struct SelectionPtrBase : public SelectionV { + static constexpr auto shared = std::is_copy_assignable_v; using T = typename Ptr::element_type; struct SelectionObj : public SelectionV { struct MakeObjectByTypeName : public SelectionV { @@ -526,13 +527,13 @@ namespace Persistence { } }; - template struct SelectionT> : public SelectionPtrBase, false> { - using SelectionPtrBase, false>::SelectionPtrBase; + template struct SelectionT> : public SelectionPtrBase> { + using SelectionPtrBase>::SelectionPtrBase; }; - template struct SelectionT> : public SelectionPtrBase, true> { - using SelectionPtrBase, true>::SelectionPtrBase; - using SelectionPtrBase, true>::setValue; + template struct SelectionT> : public SelectionPtrBase> { + using SelectionPtrBase>::SelectionPtrBase; + using SelectionPtrBase>::setValue; void setValue(std::string && id) override -- cgit v1.2.3 From 98df33e0a52e086b68df2a62ce2f41cc6b67db63 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 2 Mar 2023 18:18:38 +0000 Subject: Parse colour values as they're read --- assetFactory/assetFactory.cpp | 32 ++++++++++++++++-- assetFactory/assetFactory.h | 4 ++- assetFactory/modelFactoryMesh.h | 3 +- assetFactory/style.cpp | 72 ++++++++++++++--------------------------- assetFactory/style.h | 18 +++++++++-- assetFactory/use.cpp | 3 +- test/test-assetFactory.cpp | 14 ++++---- 7 files changed, 83 insertions(+), 63 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 016f29e..70f5337 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -30,12 +30,12 @@ AssetFactory::parseX11RGB(const char * path) { filesystem::FileStar rgb {path, "r"}; Colours out; - glm::u8vec3 colour; + Colour colour; char inname[BUFSIZ]; - while (fscanf(rgb, "%hhu %hhu %hhu %[^\n\r]s", &colour.r, &colour.g, &colour.b, inname) == 4) { + 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); + out.emplace(std::move(name), colour / 255.f); } return out; } @@ -49,6 +49,32 @@ AssetFactory::normalizeColourName(std::string & name) }; } +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(channel) / 255.f; + }); + return out; + } + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("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) { diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 42082eb..2bd576b 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -22,11 +22,13 @@ class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; using Assets = std::map>; - using Colour = glm::u8vec3; + using Colour = glm::vec3; + using ColourAlpha = glm::vec4; using Colours = std::map>; AssetFactory(); [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); + [[nodiscard]] ColourAlpha parseColour(std::string_view) const; Shapes shapes; Assets assets; diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index b4f5254..258913b 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace OpenMesh { template struct glmvec : public VectorT { @@ -25,7 +26,7 @@ struct ModelFactoryTraits : public OpenMesh::DefaultTraits { VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); using Point = OpenMesh::glmvec; using Normal = OpenMesh::glmvec; - using Color = OpenMesh::glmvec; + using Color = glm::vec4; }; struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT { diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index d2977a7..b2a2cf7 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -1,41 +1,16 @@ #include "style.h" #include "assetFactory.h" -ModelFactoryMesh::Color -Style::parseColour(const std::string_view & in) -{ - 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"); - } - ModelFactoryMesh::Color out {0, 0, 0, 1}; - std::generate_n(out.begin(), (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(channel) / 256.F; - }); - return out; - } - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { - if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { - const auto out = glm::vec3 {colour->second} / 256.F; - return {out.r, out.g, out.b, 1.f}; - } - } - throw std::runtime_error("No such asset factory colour"); -} - void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { - const auto parsedColour = parseColour(effectiveColour); + 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, parsedColour); + mesh.set_color(face.second, effectiveColour->get()); } } } @@ -43,27 +18,30 @@ Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Sha void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { - const auto parsedColour = parseColour(effectiveColour); - mesh.set_color(face, parsedColour); - } -} - -std::string_view -Style::getProperty(const StyleStack & parents, std::string Style::*member) -{ - if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), - [&member](auto && s) { - return !(s->*member).empty(); + if (const auto effectiveColour = getProperty(parents, &Style::colour, + [](auto && style) { + return style->colour.a > 0; }); - itr != parents.rend()) { - return (*itr)->*member; + effectiveColour.has_value()) { + mesh.set_color(face, effectiveColour->get()); } - return {}; } bool Style::persist(Persistence::PersistenceStore & store) { - return STORE_MEMBER(colour); + struct ColourParser : public Persistence::SelectionV { + using Persistence::SelectionV::SelectionV; + using Persistence::SelectionV::setValue; + void + setValue(std::string && str) override + { + if (auto mf + = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + v = mf->parseColour(str); + } + } + }; + + return STORE_HELPER(colour, ColourParser); } diff --git a/assetFactory/style.h b/assetFactory/style.h index 0c7ad5a..e8fd012 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -3,19 +3,31 @@ #include "modelFactoryMesh.h" #include "persistence.h" #include "shape.h" +#include #include +#include class Style { public: using StyleStack = std::vector; + using Colour = glm::vec3; + using ColourAlpha = glm::vec4; - static ModelFactoryMesh::Color parseColour(const std::string_view &); void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const; - static std::string_view getProperty(const StyleStack & parents, std::string Style::*member); + template + static std::optional> + getProperty(const StyleStack & parents, T Style::*member, auto && test) + { + if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), std::forward(test)); + itr != parents.rend()) { + return (*itr)->*member; + } + return {}; + } - std::string colour; + ColourAlpha colour {}; protected: bool persist(Persistence::PersistenceStore & store); diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index a6fac5c..53fc9b9 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -18,7 +18,8 @@ struct Lookup : public Persistence::SelectionV { void setValue(std::string && str) override { - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = std::dynamic_pointer_cast( + Persistence::sharedObjects.at("assetFactory"))) { v = mf->shapes.at(str); } } diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 89f6bf0..8feb831 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(brush47) wheelCylinder->position = {0, 0, 0.571}; wheelCylinder->scale = {1.142, 1.142, 0.07}; wheelCylinder->rotation = {0, 0, half_pi}; - wheelCylinder->colour = "#2C3539"; + wheelCylinder->colour = assetFactory.parseColour("#2C3539"); } assetFactory.shapes.emplace(wheel->id, wheel); } @@ -120,10 +120,10 @@ BOOST_AUTO_TEST_CASE(brush47) bodyLower->type = assetFactory.shapes.at("cuboid"); bodyLower->position = {0, 0, 1.2}; bodyLower->scale = {2.69, 19.38, 1.5}; - bodyLower->colour = "#1111DD"; + bodyLower->colour = assetFactory.parseColour("#1111DD"); auto & bottom = bodyLower->faceControllers["bottom"]; bottom = std::make_unique(); - bottom->colour = "#2C3539"; + bottom->colour = assetFactory.parseColour("#2C3539"); auto & bodyUpper = bodyLower->faceControllers["top"]; bodyUpper = std::make_unique(); bodyUpper->type = "extrude"; @@ -140,7 +140,7 @@ BOOST_AUTO_TEST_CASE(brush47) batteryBox->type = assetFactory.shapes.at("cuboid"); batteryBox->position = {0, 0, .2}; batteryBox->scale = {2.6, 4.5, 1}; - batteryBox->colour = "#2C3539"; + batteryBox->colour = assetFactory.parseColour("#2C3539"); } } std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(parseX11RGB) { const auto parsedColours = AssetFactory::parseX11RGB(FIXTURESDIR "rgb.txt"); BOOST_REQUIRE_EQUAL(parsedColours.size(), 20); - BOOST_CHECK_EQUAL(parsedColours.at("cyan"), AssetFactory::Colour(0, 255, 255)); - BOOST_CHECK_EQUAL(parsedColours.at("slategrey"), AssetFactory::Colour(112, 128, 144)); - BOOST_CHECK_EQUAL(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(202, 225, 255)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("cyan"), AssetFactory::Colour(0, 1, 1)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("slategrey"), AssetFactory::Colour(0.44F, 0.5, 0.56F)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(0.79, 0.88, 1)); } -- cgit v1.2.3 From 17ac090dd1dd245cf1e24b62b7333ba9be571bde Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 2 Mar 2023 18:36:34 +0000 Subject: Add ParseBase Acts as a base class for persistence parser, encompasses the parse stack and manages shared objects --- assetFactory/assetFactory.cpp | 2 +- assetFactory/style.cpp | 3 +-- assetFactory/use.cpp | 3 +-- lib/jsonParse-persistence.h | 4 +--- lib/persistence.cpp | 5 +++++ lib/persistence.h | 35 +++++++++++++++++++++++++++++++---- lib/saxParse-persistence.h | 5 +---- test/test-persistence.cpp | 10 ++++------ 8 files changed, 45 insertions(+), 22 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 70f5337..b9724c9 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -68,7 +68,7 @@ AssetFactory::parseColour(std::string_view in) const }); return out; } - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { return {colour->second, 1}; } diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index b2a2cf7..fc5c34e 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -36,8 +36,7 @@ Style::persist(Persistence::PersistenceStore & store) void setValue(std::string && str) override { - if (auto mf - = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { v = mf->parseColour(str); } } diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index 53fc9b9..708e310 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -18,8 +18,7 @@ struct Lookup : public Persistence::SelectionV { void setValue(std::string && str) override { - if (auto mf = std::dynamic_pointer_cast( - Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { v = mf->shapes.at(str); } } diff --git a/lib/jsonParse-persistence.h b/lib/jsonParse-persistence.h index fa5e772..a676282 100644 --- a/lib/jsonParse-persistence.h +++ b/lib/jsonParse-persistence.h @@ -9,7 +9,7 @@ #include namespace Persistence { - class JsonParsePersistence : public json::jsonParser { + class JsonParsePersistence : public json::jsonParser, ParseBase { public: template inline T @@ -34,8 +34,6 @@ namespace Persistence { void endArray() override; void endObject() override; - Stack stk; - template inline void pushValue(T && value); inline SelectionPtr & current(); }; diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 543f2fd..8c7c6a4 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -157,4 +157,9 @@ namespace Persistence { throw std::logic_error("Default write op shouldn't ever get called"); } /// LCOV_EXCL_STOP + + ParseBase::ParseBase() : sharedObjectsInstance {std::make_shared()} + { + sharedObjects = sharedObjectsInstance; + } } diff --git a/lib/persistence.h b/lib/persistence.h index 458ba4d..05cb49b 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -375,9 +375,36 @@ namespace Persistence { return true; } + class ParseBase { + public: + using SharedObjects = std::map>; + using SharedObjectsWPtr = std::weak_ptr; + using SharedObjectsPtr = std::shared_ptr; + + ParseBase(); + DEFAULT_MOVE_NO_COPY(ParseBase); + + template + static auto + getShared(auto && k) + { + return std::dynamic_pointer_cast(Persistence::ParseBase::sharedObjects.lock()->at(k)); + } + template + static auto + emplaceShared(T &&... v) + { + return sharedObjects.lock()->emplace(std::forward(v)...); + } + + protected: + Stack stk; + + private: + inline static thread_local SharedObjectsWPtr sharedObjects; + SharedObjectsPtr sharedObjectsInstance; + }; // TODO Move these - using SharedObjects = std::map>; - inline SharedObjects sharedObjects; using SeenSharedObjects = std::map; inline SeenSharedObjects seenSharedObjects; @@ -417,7 +444,7 @@ namespace Persistence { void setValue(std::string && id) override { - sharedObjects.emplace(id, this->v); + ParseBase::emplaceShared(id, this->v); } }; @@ -538,7 +565,7 @@ namespace Persistence { void setValue(std::string && id) override { - if (auto teo = std::dynamic_pointer_cast(sharedObjects.at(id))) { + if (auto teo = ParseBase::getShared(id)) { this->v = std::move(teo); } else { diff --git a/lib/saxParse-persistence.h b/lib/saxParse-persistence.h index 91daecc..6043b25 100644 --- a/lib/saxParse-persistence.h +++ b/lib/saxParse-persistence.h @@ -6,7 +6,7 @@ #include namespace Persistence { - class SAXParsePersistence : public SAXParse { + class SAXParsePersistence : public SAXParse, ParseBase { private: template struct Root : public Persistable { T t {}; @@ -36,8 +36,5 @@ namespace Persistence { void data(mxml_node_t *) override; void directive(mxml_node_t *) override; void cdata(mxml_node_t *) override; - - private: - Stack stk; }; } diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index 6bee010..7bca91a 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -83,16 +83,14 @@ struct TestObject : public Persistence::Persistable { } }; -struct JPP : public Persistence::JsonParsePersistence { +struct JPP { template T load_json(const std::filesystem::path & path) { BOOST_TEST_CONTEXT(path) { std::ifstream ss {path}; - auto to = loadState(ss); - Persistence::sharedObjects.clear(); - BOOST_CHECK(stk.empty()); + auto to = Persistence::JsonParsePersistence {}.loadState(ss); BOOST_REQUIRE(to); return to; } @@ -289,10 +287,10 @@ auto const TEST_STRINGS_DECODE_ONLY = boost::unit_test::data::make({ {R"J("\u056b ARMENIAN SMALL LETTER INI")J", "ի ARMENIAN SMALL LETTER INI"}, {R"J("\u0833 SAMARITAN PUNCTUATION BAU")J", "࠳ SAMARITAN PUNCTUATION BAU"}, }); -BOOST_DATA_TEST_CASE_F(JPP, load_strings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, in, exp) +BOOST_DATA_TEST_CASE(load_strings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, in, exp) { std::stringstream str {in}; - BOOST_CHECK_EQUAL(loadState(str), exp); + BOOST_CHECK_EQUAL(Persistence::JsonParsePersistence {}.loadState(str), exp); } using cpstr = std::tuple; -- cgit v1.2.3 From d1390c8673b178744acd6aa3a681f7b51ed9438f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 2 Mar 2023 23:41:10 +0000 Subject: Don't leak root node when parsing XML --- lib/saxParse.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/saxParse.cpp b/lib/saxParse.cpp index d592a1f..5c597aa 100644 --- a/lib/saxParse.cpp +++ b/lib/saxParse.cpp @@ -11,7 +11,7 @@ namespace Persistence { void SAXParse::parseFile(FILE * file) { - mxmlSAXLoadFile( + mxmlRelease(mxmlSAXLoadFile( nullptr, file, MXML_TEXT_CALLBACK, [](mxml_node_t * n, mxml_sax_event_t e, void * data) { SAXParse * self = static_cast(data); @@ -36,6 +36,6 @@ namespace Persistence { break; } }, - this); + this)); } } -- cgit v1.2.3 From 5b7f5f2de723d30d3461f653e90fa4ae95941786 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 3 Mar 2023 19:52:55 +0000 Subject: Split Asset into its own file --- assetFactory/asset.cpp | 8 ++++++++ assetFactory/asset.h | 17 +++++++++++++++++ assetFactory/assetFactory.cpp | 7 ------- assetFactory/assetFactory.h | 15 +-------------- 4 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 assetFactory/asset.cpp create mode 100644 assetFactory/asset.h diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp new file mode 100644 index 0000000..fa30a78 --- /dev/null +++ b/assetFactory/asset.cpp @@ -0,0 +1,8 @@ +#include "asset.h" + +bool +Asset::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(name) + && STORE_NAME_HELPER("mesh", meshes, Persistence::Appender); +} diff --git a/assetFactory/asset.h b/assetFactory/asset.h new file mode 100644 index 0000000..d86b804 --- /dev/null +++ b/assetFactory/asset.h @@ -0,0 +1,17 @@ +#pragma once + +#include "factoryMesh.h" +#include "persistence.h" +#include + +class Asset : public Persistence::Persistable, public StdTypeDefs { +public: + std::string id; + std::string name; + + FactoryMesh::Collection meshes; + +protected: + friend Persistence::SelectionPtrBase>; + bool persist(Persistence::PersistenceStore & store) override; +}; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index b9724c9..f5fc2b3 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -83,10 +83,3 @@ AssetFactory::persist(Persistence::PersistenceStore & store) return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) && STORE_NAME_HELPER("asset", assets, MapAssets); } - -bool -Asset::persist(Persistence::PersistenceStore & store) -{ - return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(name) - && STORE_NAME_HELPER("mesh", meshes, Persistence::Appender); -} diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 2bd576b..b47d408 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -1,22 +1,9 @@ #pragma once -#include "factoryMesh.h" +#include "asset.h" #include "persistence.h" #include "shape.h" #include -#include - -class Asset : public Persistence::Persistable, public StdTypeDefs { -public: - std::string id; - std::string name; - - FactoryMesh::Collection meshes; - -private: - friend Persistence::SelectionPtrBase>; - bool persist(Persistence::PersistenceStore & store) override; -}; class AssetFactory : public Persistence::Persistable { public: -- cgit v1.2.3 From 29ce7770c3e8373d2efea0b47fe50a13a81e75a2 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 14:17:00 +0000 Subject: Add Asset helpers to construct OpenGL ready meshes from Asset Factory meshes during load --- assetFactory/asset.cpp | 29 ++++++++++++++++++++++++++++- assetFactory/asset.h | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp index fa30a78..659a950 100644 --- a/assetFactory/asset.cpp +++ b/assetFactory/asset.cpp @@ -3,6 +3,33 @@ bool Asset::persist(Persistence::PersistenceStore & store) { - return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(name) + return STORE_MEMBER(id) && STORE_MEMBER(name) && STORE_NAME_HELPER("mesh", meshes, Persistence::Appender); } + +Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) : + Persistence::SelectionPtrBase {fmesh}, out {m} { } + +void +Asset::MeshConstruct::endObject(Persistence::Stack & stk) +{ + out = fmesh->createMesh(); + Persistence::SelectionPtrBase::endObject(stk); +} + +Asset::MeshArrayConstruct::MeshArrayConstruct(std::span m) : + Persistence::SelectionPtrBase {fmesh}, out {m} +{ +} + +void +Asset::MeshArrayConstruct::endObject(Persistence::Stack & stk) +{ + for (auto & outMesh : out) { + if (!outMesh) { + outMesh = fmesh->createMesh(); + break; + } + } + Persistence::SelectionPtrBase::endObject(stk); +} diff --git a/assetFactory/asset.h b/assetFactory/asset.h index d86b804..8dd1ecb 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -12,6 +12,28 @@ public: FactoryMesh::Collection meshes; protected: + struct MeshConstruct : public Persistence::SelectionPtrBase { + using Persistence::SelectionPtrBase::setValue; + + MeshConstruct(Mesh::Ptr & m); + + void endObject(Persistence::Stack & stk) override; + + FactoryMesh::Ptr fmesh; + Mesh::Ptr & out; + }; + + struct MeshArrayConstruct : public Persistence::SelectionPtrBase { + using Persistence::SelectionPtrBase::setValue; + + MeshArrayConstruct(std::span m); + + void endObject(Persistence::Stack & stk) override; + + FactoryMesh::Ptr fmesh; + std::span out; + }; + friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; }; -- cgit v1.2.3 From 54ddcd3da0916b6a46f5fcf3f33ff0fbffe375ce Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 14:27:23 +0000 Subject: Support and load factory asset directly into a RailVehicleClass instance --- assetFactory/asset.cpp | 3 +-- assetFactory/asset.h | 2 -- game/vehicles/railVehicleClass.cpp | 9 +++++++++ game/vehicles/railVehicleClass.h | 8 +++++++- res/brush47.xml | 14 +++++++------- test/test-assetFactory.cpp | 18 +++++++++--------- 6 files changed, 33 insertions(+), 21 deletions(-) diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp index 659a950..3ab2f1c 100644 --- a/assetFactory/asset.cpp +++ b/assetFactory/asset.cpp @@ -3,8 +3,7 @@ bool Asset::persist(Persistence::PersistenceStore & store) { - return STORE_MEMBER(id) && STORE_MEMBER(name) - && STORE_NAME_HELPER("mesh", meshes, Persistence::Appender); + return STORE_MEMBER(id) && STORE_MEMBER(name); } Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) : diff --git a/assetFactory/asset.h b/assetFactory/asset.h index 8dd1ecb..e3318e4 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -9,8 +9,6 @@ public: std::string id; std::string name; - FactoryMesh::Collection meshes; - protected: struct MeshConstruct : public Persistence::SelectionPtrBase { using Persistence::SelectionPtrBase::setValue; diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index dff1416..7052396 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -39,6 +39,15 @@ RailVehicleClass::RailVehicleClass(std::unique_ptr o, std::shared_ptr bogies[1] = m.at("Bogie2"); } +RailVehicleClass::RailVehicleClass() { } + +bool +RailVehicleClass::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(maxSpeed) && STORE_NAME_HELPER("bogie", bogies, Asset::MeshArrayConstruct) + && STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store); +} + void RailVehicleClass::render( const SceneShader & shader, const Location & location, const std::array & bl) const diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index bd74ab9..c50dacc 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -1,5 +1,6 @@ #pragma once +#include "assetFactory/asset.h" #include "gfx/models/mesh.h" #include #include @@ -11,9 +12,10 @@ class Texture; class ObjParser; class Location; -class RailVehicleClass { +class RailVehicleClass : public Asset { public: explicit RailVehicleClass(const std::string & name); + RailVehicleClass(); void render(const SceneShader &, const Location &, const std::array &) const; void shadows(const ShadowMapper &, const Location &) const; @@ -25,6 +27,10 @@ public: float length; float maxSpeed; +protected: + friend Persistence::SelectionPtrBase>; + bool persist(Persistence::PersistenceStore & store) override; + private: RailVehicleClass(std::unique_ptr obj, std::shared_ptr); static float bogieOffset(ObjParser & o); diff --git a/res/brush47.xml b/res/brush47.xml index 477610c..796641a 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -12,8 +12,8 @@ - - + + @@ -25,12 +25,12 @@ - - + + - - + + - + diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 8feb831..9930714 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -8,6 +8,7 @@ #include "assetFactory/assetFactory.h" #include "assetFactory/factoryMesh.h" #include "assetFactory/object.h" +#include "game/vehicles/railVehicleClass.h" #include "gfx/gl/sceneRenderer.h" #include "lib/collection.hpp" #include "lib/location.hpp" @@ -167,16 +168,15 @@ BOOST_AUTO_TEST_CASE(brush47xml) BOOST_CHECK_EQUAL(1, mf->assets.size()); auto brush47 = mf->assets.at("brush-47"); BOOST_REQUIRE(brush47); - BOOST_CHECK_EQUAL(3, brush47->meshes.size()); - auto body = brush47->meshes.at(0); - BOOST_REQUIRE(body); - BOOST_CHECK_EQUAL("body", body->id); - BOOST_CHECK_EQUAL(2, body->uses.size()); + auto brush47rvc = std::dynamic_pointer_cast(brush47); + BOOST_REQUIRE(brush47rvc); + BOOST_REQUIRE(brush47rvc->bodyMesh); + BOOST_REQUIRE(brush47rvc->bogies.front()); + BOOST_REQUIRE(brush47rvc->bogies.back()); - std::transform(brush47->meshes.begin(), brush47->meshes.end(), std::back_inserter(meshes.objects), - [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { - return factoryMesh->createMesh(); - }); + meshes.objects.push_back(brush47rvc->bodyMesh); + meshes.objects.push_back(brush47rvc->bogies.front()); + meshes.objects.push_back(brush47rvc->bogies.back()); render(20); } -- cgit v1.2.3 From ca7611dcf999f55ea17d5cd4f53e5a836ec51cd1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 14:59:02 +0000 Subject: RailVehicleClass texture might be null --- game/vehicles/railVehicleClass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 7052396..c0740cc 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -53,7 +53,9 @@ RailVehicleClass::render( const SceneShader & shader, const Location & location, const std::array & bl) const { shader.basic.use(location); - texture->bind(); + if (texture) { + texture->bind(); + } bodyMesh->Draw(); for (auto b = 0U; b < bogies.size(); ++b) { shader.basic.setModel(bl[b]); -- cgit v1.2.3 From c8ecaaddeee46900540e24e096f86306e77493f7 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 15:12:30 +0000 Subject: Load length, wheelBase and maxSpeed RailVehicleClass properties from XML Adjusts bogie position to appear correct according to wheelBase --- game/vehicles/railVehicleClass.cpp | 3 ++- res/brush47.xml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index c0740cc..90a06bd 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -44,7 +44,8 @@ RailVehicleClass::RailVehicleClass() { } bool RailVehicleClass::persist(Persistence::PersistenceStore & store) { - return STORE_TYPE && STORE_MEMBER(maxSpeed) && STORE_NAME_HELPER("bogie", bogies, Asset::MeshArrayConstruct) + return STORE_TYPE && STORE_MEMBER(length) && STORE_MEMBER(wheelBase) && STORE_MEMBER(maxSpeed) + && STORE_NAME_HELPER("bogie", bogies, Asset::MeshArrayConstruct) && STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store); } diff --git a/res/brush47.xml b/res/brush47.xml index 796641a..9c7d2a8 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -12,7 +12,7 @@ - + @@ -27,10 +27,10 @@ - + - + -- cgit v1.2.3 From 7fc588638feb1a83ab58f78f9e2a9d0d23a083e0 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 15:18:32 +0000 Subject: DynamicPoint shadow render now supports updating the position without calling use again --- gfx/gl/shadowMapper.cpp | 7 +++++++ gfx/gl/shadowMapper.h | 1 + 2 files changed, 8 insertions(+) diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 1787ee6..7649a54 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -184,6 +184,13 @@ void ShadowMapper::DynamicPoint::use(const Location & location) const { glUseProgram(*this); + setModel(location); + const auto model = glm::translate(location.pos) * rotate_ypr(location.rot); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); +} +void +ShadowMapper::DynamicPoint::setModel(const Location & location) const +{ const auto model = glm::translate(location.pos) * rotate_ypr(location.rot); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); } diff --git a/gfx/gl/shadowMapper.h b/gfx/gl/shadowMapper.h index b4793ed..a5c2c7b 100644 --- a/gfx/gl/shadowMapper.h +++ b/gfx/gl/shadowMapper.h @@ -34,6 +34,7 @@ public: DynamicPoint(); void setViewProjection(const glm::mat4 &) const; void use(const Location &) const; + void setModel(const Location &) const; private: RequiredUniformLocation viewProjectionLoc; -- cgit v1.2.3 From 74b86470aec99b87a1f3545b7d3799dbf0c1e90c Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 15:26:35 +0000 Subject: RailVehicleClass now renders bogie shadows as well as body --- game/vehicles/railVehicle.cpp | 2 +- game/vehicles/railVehicleClass.cpp | 7 ++++++- game/vehicles/railVehicleClass.h | 2 +- test/test-render.cpp | 8 ++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp index bcde68f..cbe36a6 100644 --- a/game/vehicles/railVehicle.cpp +++ b/game/vehicles/railVehicle.cpp @@ -20,7 +20,7 @@ RailVehicle::render(const SceneShader & shader) const void RailVehicle::shadows(const ShadowMapper & shadowMapper) const { - rvClass->shadows(shadowMapper, location); + rvClass->shadows(shadowMapper, location, bogies); } void diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 90a06bd..41ef5e9 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -64,10 +64,15 @@ RailVehicleClass::render( } } void -RailVehicleClass::shadows(const ShadowMapper & shadowMapper, const Location & location) const +RailVehicleClass::shadows( + const ShadowMapper & shadowMapper, const Location & location, const std::array & bl) const { shadowMapper.dynamicPoint.use(location); bodyMesh->Draw(); + for (auto b = 0U; b < bogies.size(); ++b) { + shadowMapper.dynamicPoint.setModel(bl[b]); + bogies[b]->Draw(); + } } float diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index c50dacc..a2222fb 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -18,7 +18,7 @@ public: RailVehicleClass(); void render(const SceneShader &, const Location &, const std::array &) const; - void shadows(const ShadowMapper &, const Location &) const; + void shadows(const ShadowMapper &, const Location &, const std::array &) const; std::array bogies; Mesh::Ptr bodyMesh; diff --git a/test/test-render.cpp b/test/test-render.cpp index 7db847d..7771760 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -29,8 +29,8 @@ class TestScene : public SceneProvider { content(const SceneShader & shader) const override { terrain.render(shader); - train.render(shader, Location {{52, 50, 2}}, {Location {}, Location {}}); - train.render(shader, Location {{52, 30, 2}}, {Location {}, Location {}}); + train.render(shader, Location {{52, 50, 2}}, {Location {{52, 56, 2}}, Location {{52, 44, 2}}}); + train.render(shader, Location {{52, 30, 2}}, {Location {{52, 36, 2}}, Location {{52, 24, 2}}}); } void lights(const SceneShader &) const override @@ -40,8 +40,8 @@ class TestScene : public SceneProvider { shadows(const ShadowMapper & shadowMapper) const override { terrain.shadows(shadowMapper); - train.shadows(shadowMapper, Location {{52, 50, 2}}); - train.shadows(shadowMapper, Location {{52, 30, 2}}); + train.shadows(shadowMapper, Location {{52, 50, 2}}, {Location {{52, 56, 2}}, Location {{52, 44, 2}}}); + train.shadows(shadowMapper, Location {{52, 30, 2}}, {Location {{52, 36, 2}}, Location {{52, 24, 2}}}); } }; -- cgit v1.2.3 From c32674d9bac2cf6283cafc3bf446a45be22b2851 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 15:47:22 +0000 Subject: Default RailVehicle bogie positions according to class wheelBase Useful for render tests so everything looks sane --- game/vehicles/railVehicle.cpp | 6 ++++++ game/vehicles/railVehicle.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp index cbe36a6..77907be 100644 --- a/game/vehicles/railVehicle.cpp +++ b/game/vehicles/railVehicle.cpp @@ -11,6 +11,12 @@ #include #include +RailVehicle::RailVehicle(RailVehicleClassPtr rvc) : rvClass {std::move(rvc)} +{ + bogies.front().pos.y = rvClass->wheelBase / 2.F; + bogies.back().pos.y = -bogies.front().pos.y; +} + void RailVehicle::render(const SceneShader & shader) const { diff --git a/game/vehicles/railVehicle.h b/game/vehicles/railVehicle.h index f172a28..bbf4df9 100644 --- a/game/vehicles/railVehicle.h +++ b/game/vehicles/railVehicle.h @@ -16,7 +16,7 @@ class Ray; class Train; class RailVehicle : public Renderable, Selectable { public: - explicit RailVehicle(RailVehicleClassPtr rvc) : rvClass {std::move(rvc)} { } + explicit RailVehicle(RailVehicleClassPtr rvc); void move(const Train *, float & trailBy); -- cgit v1.2.3 From 73a8abf0762a113a04b05e6f1364aa0b961fa7f6 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 4 Mar 2023 15:48:53 +0000 Subject: Remove old hard coded asset factory test, run entirely from XML load and render a RailVehicle instance --- test/test-assetFactory.cpp | 99 ++++------------------------------------------ 1 file changed, 7 insertions(+), 92 deletions(-) diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 9930714..ae5a88a 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -6,10 +6,11 @@ #include #include "assetFactory/assetFactory.h" -#include "assetFactory/factoryMesh.h" #include "assetFactory/object.h" +#include "game/vehicles/railVehicle.h" #include "game/vehicles/railVehicleClass.h" #include "gfx/gl/sceneRenderer.h" +#include "gfx/renderable.h" #include "lib/collection.hpp" #include "lib/location.hpp" #include "lib/stream_support.hpp" @@ -34,7 +35,7 @@ public: content(const SceneShader & shader) const override { shader.basic.use(Location {{0, 0, 0}, {0, 0, 0}}); - meshes.apply(&Mesh::Draw); + objects.apply(&Renderable::render, shader); } void lights(const SceneShader & shader) const override @@ -51,7 +52,7 @@ public: shadows(const ShadowMapper & mapper) const override { mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); - meshes.apply(&Mesh::Draw); + objects.apply(&Renderable::shadows, mapper); } void render(float dist = 10.f) @@ -59,98 +60,13 @@ public: sceneRenderer.camera.setView({dist, dist, dist}, south + west + down); sceneRenderer.render(*this); } - Collection meshes; + Collection objects; private: SceneRenderer sceneRenderer; }; BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); -BOOST_AUTO_TEST_CASE(brush47) -{ - AssetFactory assetFactory; - { - auto wheel = std::make_shared("wheel"); - { - auto wheelCylinder = wheel->uses.emplace_back(std::make_shared()); - wheelCylinder->type = assetFactory.shapes.at("cylinder"); - wheelCylinder->position = {0, 0, 0.571}; - wheelCylinder->scale = {1.142, 1.142, 0.07}; - wheelCylinder->rotation = {0, 0, half_pi}; - wheelCylinder->colour = assetFactory.parseColour("#2C3539"); - } - assetFactory.shapes.emplace(wheel->id, wheel); - } - { - auto axel = std::make_shared("axel"); - for (float x : {-1.f, 1.f}) { - auto wheel = axel->uses.emplace_back(std::make_shared()); - wheel->type = assetFactory.shapes.at("wheel"); - wheel->position = {x * 0.717f, 0, 0}; - wheel->rotation = {0, x == 1.f ? pi : 0.f, 0}; - } - assetFactory.shapes.emplace(axel->id, axel); - } - { - auto bogie = std::make_shared("bogie"); - for (float y : {-2.f, 0.f, 2.f}) { - auto axel = bogie->uses.emplace_back(std::make_shared()); - axel->type = assetFactory.shapes.at("axel"); - axel->position = {0, y, 0}; - } - assetFactory.shapes.emplace(bogie->id, bogie); - } - FactoryMesh::Collection factoryMeshes; - { - unsigned short b {0}; - for (float y : {-6.f, 6.f}) { - auto bogie = factoryMeshes.emplace_back(std::make_shared()); - bogie->id = "bogie" + std::to_string(b); - auto bogieUse = bogie->uses.emplace_back(std::make_shared()); - bogieUse->type = assetFactory.shapes.at("bogie"); - bogieUse->position = {0, y, 0}; - bogieUse->rotation = {0, b * pi, 0}; - b++; - } - } - { - auto body = factoryMeshes.emplace_back(std::make_shared()); - body->id = "body"; - body->size = {2.69f, 19.38f, 3.9f}; - auto bodyLower = body->uses.emplace_back(std::make_shared()); - bodyLower->type = assetFactory.shapes.at("cuboid"); - bodyLower->position = {0, 0, 1.2}; - bodyLower->scale = {2.69, 19.38, 1.5}; - bodyLower->colour = assetFactory.parseColour("#1111DD"); - auto & bottom = bodyLower->faceControllers["bottom"]; - bottom = std::make_unique(); - bottom->colour = assetFactory.parseColour("#2C3539"); - auto & bodyUpper = bodyLower->faceControllers["top"]; - bodyUpper = std::make_unique(); - bodyUpper->type = "extrude"; - bodyUpper->scale = {1, .95f, 1}; - bodyUpper->position = {0, 0, 1.0}; - auto & roof = bodyUpper->faceControllers["top"]; - roof = std::make_unique(); - roof->type = "extrude"; - roof->scale = {.6f, .9f, 0}; - roof->position = {0, 0, 0.2}; - roof->smooth = true; - { - auto batteryBox = body->uses.emplace_back(std::make_shared()); - batteryBox->type = assetFactory.shapes.at("cuboid"); - batteryBox->position = {0, 0, .2}; - batteryBox->scale = {2.6, 4.5, 1}; - batteryBox->colour = assetFactory.parseColour("#2C3539"); - } - } - std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), - [](const FactoryMesh::CPtr & factoryMesh) -> Mesh::Ptr { - return factoryMesh->createMesh(); - }); - - render(20); -} BOOST_AUTO_TEST_CASE(brush47xml) { auto mf = AssetFactory::loadXML(RESDIR "/brush47.xml"); @@ -174,9 +90,8 @@ BOOST_AUTO_TEST_CASE(brush47xml) BOOST_REQUIRE(brush47rvc->bogies.front()); BOOST_REQUIRE(brush47rvc->bogies.back()); - meshes.objects.push_back(brush47rvc->bodyMesh); - meshes.objects.push_back(brush47rvc->bogies.front()); - meshes.objects.push_back(brush47rvc->bogies.back()); + auto railVehicle = std::make_shared(brush47rvc); + objects.objects.push_back(railVehicle); render(20); } -- cgit v1.2.3