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/use.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 assetFactory/use.cpp (limited to 'assetFactory/use.cpp') 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; +} -- 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 (limited to 'assetFactory/use.cpp') 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 (limited to 'assetFactory/use.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 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(-) (limited to 'assetFactory/use.cpp') 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 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 (limited to 'assetFactory/use.cpp') 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 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(-) (limited to 'assetFactory/use.cpp') 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(-) (limited to 'assetFactory/use.cpp') 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