From 5dc841c1a4428f1a1ea353583e3b51ea24b4c608 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 11 Mar 2023 12:05:57 +0000 Subject: CLOG includes line number --- lib/stream_support.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stream_support.hpp b/lib/stream_support.hpp index 6a3c2cf..52cc9d9 100644 --- a/lib/stream_support.hpp +++ b/lib/stream_support.hpp @@ -83,4 +83,4 @@ streamed_string(const T & v) return std::move(ss).str(); } -#define CLOG(x) std::cerr << #x " : " << x << "\n"; +#define CLOG(x) std::cerr << __LINE__ << " : " #x " : " << x << "\n"; -- cgit v1.2.3 From d7d5cd4265aab0b939b57ea7237b56f2f5840642 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 11 Mar 2023 12:07:29 +0000 Subject: Initial version of texture packer Determines where a collection of smaller textures can be tiled into a single bigger image. Probably non-optimal. --- assetFactory/texturePacker.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++ assetFactory/texturePacker.h | 36 ++++++++++++++++++++ test/test-assetFactory.cpp | 43 +++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 assetFactory/texturePacker.cpp create mode 100644 assetFactory/texturePacker.h diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp new file mode 100644 index 0000000..31c9a0e --- /dev/null +++ b/assetFactory/texturePacker.cpp @@ -0,0 +1,77 @@ +#include "texturePacker.h" +#include +#include +#include +#include +#include + +TexturePacker::TexturePacker(std::vector in) : inputImages {std::move(in)} +{ + std::sort(inputImages.rbegin(), inputImages.rend(), [](const auto & a, const auto & b) { + return area(a) < area(b); + }); +} + +TexturePacker::Result +TexturePacker::pack() const +{ + return pack(minSize()); +} + +TexturePacker::Result +TexturePacker::pack(Size size) const +{ + using Spaces = std::set; + Spaces spaces {{{}, size}}; + + Positions result; + for (const auto & image : inputImages) { + if (const auto spaceItr = std::find_if(spaces.begin(), spaces.end(), + [image](const Space & s) { + return image.x <= s.size.x && image.y <= s.size.y; + }); + spaceItr != spaces.end()) { + auto space = *spaceItr; + result.push_back(space.position); + spaces.erase(spaceItr); + if (space.size.x > image.x) { + spaces.emplace(Position {space.position.x + image.x, space.position.y}, + Size {space.size.x - image.x, image.y}); + } + if (space.size.y > image.y) { + spaces.emplace(Position {space.position.x, space.position.y + image.y}, + Size {space.size.x, space.size.y - image.y}); + } + } + else { + if (size.x < size.y) { + return pack({size.x * 2, size.y}); + } + else { + return pack({size.x, size.y * 2}); + } + } + } + + return {result, size}; +} + +TexturePacker::Size +TexturePacker::minSize() const +{ + return std::accumulate(inputImages.begin(), inputImages.end(), Size {1}, [](Size size, const Image & i) { + while (size.x < i.x) { + size.x *= 2; + } + while (size.y < i.y) { + size.y *= 2; + } + return size; + }); +} + +decltype(TexturePacker::Size::x) +TexturePacker::area(const Size & size) +{ + return size.x * size.y; +} diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h new file mode 100644 index 0000000..8e2061b --- /dev/null +++ b/assetFactory/texturePacker.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +class TexturePacker { +public: + using Position = glm::uvec2; + using Size = glm::uvec2; + + struct Area { + Position position; + Size size; + bool + operator<(const Area & other) const + { + return area(size) < area(other.size); + } + }; + using Image = Size; + using Space = Area; + using Positions = std::vector; + using Result = std::pair; + + TexturePacker(std::vector); + + Result pack(Size) const; + Result pack() const; + + Size minSize() const; + static decltype(Size::x) area(const Size & size); + +private: + std::vector inputImages; +}; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 204ffb3..ad62a93 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -7,6 +7,7 @@ #include "assetFactory/assetFactory.h" #include "assetFactory/object.h" +#include "assetFactory/texturePacker.h" #include "game/vehicles/railVehicle.h" #include "game/vehicles/railVehicleClass.h" #include "gfx/gl/sceneRenderer.h" @@ -125,3 +126,45 @@ BOOST_AUTO_TEST_CASE(parseX11RGB) 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)); } + +BOOST_AUTO_TEST_CASE(texturePacker) +{ + TexturePacker tp {{ + {10, 10}, + {10, 10}, + {10, 10}, + {100, 10}, + {10, 200}, + {5, 5}, + }}; + BOOST_CHECK_EQUAL(TexturePacker::Size(128, 256), tp.minSize()); + const auto result = tp.pack(); +} + +BOOST_AUTO_TEST_CASE(texturePacker_many, *boost::unit_test::timeout(5)) +{ + std::vector images(256); + std::fill(images.begin(), images.end(), TexturePacker::Image {32, 32}); + const auto totalSize = std::accumulate(images.begin(), images.end(), 0U, [](auto t, const auto & i) { + return t + TexturePacker::area(i); + }); + TexturePacker tp {images}; + BOOST_CHECK_EQUAL(TexturePacker::Size(32, 32), tp.minSize()); + const auto result = tp.pack(); + BOOST_CHECK_EQUAL(result.first.size(), images.size()); + BOOST_CHECK_GE(TexturePacker::area(result.second), TexturePacker::area(images.front()) * images.size()); + BOOST_CHECK_EQUAL(totalSize, TexturePacker::area(result.second)); +} + +BOOST_AUTO_TEST_CASE(texturePacker_many_random, *boost::unit_test::timeout(5)) +{ + std::vector images(2048); + std::mt19937 gen(std::random_device {}()); + std::uniform_int_distribution<> dim {1, 10}; + std::generate(images.begin(), images.end(), [&dim, &gen]() { + return TexturePacker::Image {2 ^ dim(gen), 2 ^ dim(gen)}; + }); + TexturePacker tp {images}; + const auto result = tp.pack(); + BOOST_CHECK_EQUAL(result.first.size(), images.size()); +} -- cgit v1.2.3 From 88c9d66352c2f4856682efd48482b5b72aca18bf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 12 Mar 2023 00:54:24 +0000 Subject: Support loading references to texture fragments --- assetFactory/assetFactory.cpp | 3 +++ assetFactory/assetFactory.h | 3 +++ assetFactory/textureFragment.cpp | 7 +++++++ assetFactory/textureFragment.h | 14 ++++++++++++++ res/brush47.xml | 2 ++ res/rail/roofSideWithVents.png | Bin 0 -> 17011 bytes res/rail/roofTopWithVents.png | Bin 0 -> 11921 bytes 7 files changed, 29 insertions(+) create mode 100644 assetFactory/textureFragment.cpp create mode 100644 assetFactory/textureFragment.h create mode 100644 res/rail/roofSideWithVents.png create mode 100644 res/rail/roofTopWithVents.png diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index f5fc2b3..de13579 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -75,11 +75,14 @@ AssetFactory::parseColour(std::string_view in) const } throw std::runtime_error("No such asset factory colour"); } + bool AssetFactory::persist(Persistence::PersistenceStore & store) { using MapObjects = Persistence::MapByMember>; using MapAssets = Persistence::MapByMember; + using MapTextureFragments = Persistence::MapByMember; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) + && STORE_NAME_HELPER("textureFragment", textureFragments, MapTextureFragments) && STORE_NAME_HELPER("asset", assets, MapAssets); } diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index b47d408..3d52f7f 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -3,12 +3,14 @@ #include "asset.h" #include "persistence.h" #include "shape.h" +#include "textureFragment.h" #include class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; using Assets = std::map>; + using TextureFragments = std::map>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; using Colours = std::map>; @@ -20,6 +22,7 @@ public: Shapes shapes; Assets assets; Colours colours; + TextureFragments textureFragments; static Colours parseX11RGB(const char * rgbtxtpath); static void normalizeColourName(std::string &); diff --git a/assetFactory/textureFragment.cpp b/assetFactory/textureFragment.cpp new file mode 100644 index 0000000..72107a5 --- /dev/null +++ b/assetFactory/textureFragment.cpp @@ -0,0 +1,7 @@ +#include "textureFragment.h" + +bool +TextureFragment::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(path); +} diff --git a/assetFactory/textureFragment.h b/assetFactory/textureFragment.h new file mode 100644 index 0000000..52f2591 --- /dev/null +++ b/assetFactory/textureFragment.h @@ -0,0 +1,14 @@ +#pragma once + +#include "persistence.h" +#include "stdTypeDefs.hpp" + +class TextureFragment : public Persistence::Persistable, public StdTypeDefs { +public: + std::string id; + std::string path; + +private: + friend Persistence::SelectionPtrBase; + bool persist(Persistence::PersistenceStore & store) override; +}; diff --git a/res/brush47.xml b/res/brush47.xml index dbd3327..a7e3894 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -59,6 +59,8 @@ + + diff --git a/res/rail/roofSideWithVents.png b/res/rail/roofSideWithVents.png new file mode 100644 index 0000000..67ed851 Binary files /dev/null and b/res/rail/roofSideWithVents.png differ diff --git a/res/rail/roofTopWithVents.png b/res/rail/roofTopWithVents.png new file mode 100644 index 0000000..0f64ced Binary files /dev/null and b/res/rail/roofTopWithVents.png differ -- cgit v1.2.3 From 0ee9b8c6036899f95dfd54f151cb60a03645f87a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 12 Mar 2023 01:05:18 +0000 Subject: Support loading texture references into faces --- assetFactory/style.cpp | 2 +- assetFactory/style.h | 2 ++ res/brush47.xml | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index fc5c34e..5303d96 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -42,5 +42,5 @@ Style::persist(Persistence::PersistenceStore & store) } }; - return STORE_HELPER(colour, ColourParser); + return STORE_HELPER(colour, ColourParser) && STORE_MEMBER(texture) && STORE_MEMBER(textureRotation); } diff --git a/assetFactory/style.h b/assetFactory/style.h index e8fd012..84e5dab 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -28,6 +28,8 @@ public: } ColourAlpha colour {}; + std::string texture; + std::string textureRotation; // Multiples of 90deg, no int/enum support protected: bool persist(Persistence::PersistenceStore & store); diff --git a/res/brush47.xml b/res/brush47.xml index a7e3894..c345c82 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -71,7 +71,11 @@ - + + + + + -- cgit v1.2.3 From 0d5b57f31e17b8e46979d8c29b5a3205fbdbd44b Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 12 Mar 2023 01:29:38 +0000 Subject: Dedupe looking up the style stack for colour --- assetFactory/style.cpp | 20 ++++++++++---------- assetFactory/style.h | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 5303d96..fa585ac 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -4,11 +4,7 @@ void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour, - [](auto && style) { - return style->colour.a > 0; - }); - effectiveColour.has_value()) { + if (const auto effectiveColour = getColour(parents); effectiveColour.has_value()) { for (const auto & face : faces) { mesh.set_color(face.second, effectiveColour->get()); } @@ -18,15 +14,19 @@ 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, - [](auto && style) { - return style->colour.a > 0; - }); - effectiveColour.has_value()) { + if (const auto effectiveColour = getColour(parents); effectiveColour.has_value()) { mesh.set_color(face, effectiveColour->get()); } } +std::optional> +Style::getColour(const StyleStack & parents) +{ + return getProperty(parents, &Style::colour, [](auto && style) { + return style->colour.a > 0; + }); +} + bool Style::persist(Persistence::PersistenceStore & store) { diff --git a/assetFactory/style.h b/assetFactory/style.h index 84e5dab..8c64276 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -27,6 +27,8 @@ public: return {}; } + static std::optional> getColour(const StyleStack & parents); + ColourAlpha colour {}; std::string texture; std::string textureRotation; // Multiples of 90deg, no int/enum support -- cgit v1.2.3 From 7bf24d23a7db872f7bd13060da9817a9ee5f816c Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 12 Mar 2023 01:36:50 +0000 Subject: Dedupe applying style to a face --- assetFactory/style.cpp | 17 +++++++++++------ assetFactory/style.h | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index fa585ac..b5ddcf1 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -4,22 +4,27 @@ void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const { - if (const auto effectiveColour = getColour(parents); effectiveColour.has_value()) { - for (const auto & face : faces) { - mesh.set_color(face.second, effectiveColour->get()); - } + for (const auto & face : faces) { + applyStyle(mesh, face.second, getColour(parents)); } } void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const { - if (const auto effectiveColour = getColour(parents); effectiveColour.has_value()) { + applyStyle(mesh, face, getColour(parents)); +} + +void +Style::applyStyle( + ModelFactoryMesh & mesh, const ModelFactoryMesh::FaceHandle & face, EffectiveColour effectiveColour) const +{ + if (effectiveColour.has_value()) { mesh.set_color(face, effectiveColour->get()); } } -std::optional> +Style::EffectiveColour Style::getColour(const StyleStack & parents) { return getProperty(parents, &Style::colour, [](auto && style) { diff --git a/assetFactory/style.h b/assetFactory/style.h index 8c64276..f3bc707 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -12,6 +12,7 @@ public: using StyleStack = std::vector; using Colour = glm::vec3; using ColourAlpha = glm::vec4; + using EffectiveColour = std::optional>; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const; @@ -27,7 +28,7 @@ public: return {}; } - static std::optional> getColour(const StyleStack & parents); + static EffectiveColour getColour(const StyleStack & parents); ColourAlpha colour {}; std::string texture; @@ -35,4 +36,5 @@ public: protected: bool persist(Persistence::PersistenceStore & store); + void applyStyle(ModelFactoryMesh &, const ModelFactoryMesh::FaceHandle &, EffectiveColour) const; }; -- cgit v1.2.3 From 14e1b0def33339f2221f1101e115d0aff69727ad Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 14 Mar 2023 01:00:08 +0000 Subject: Basic fragment ahader should use final colour to determine clear flag --- gfx/gl/shaders/basicShader.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gfx/gl/shaders/basicShader.fs b/gfx/gl/shaders/basicShader.fs index 93f0a3f..24b2791 100644 --- a/gfx/gl/shaders/basicShader.fs +++ b/gfx/gl/shaders/basicShader.fs @@ -14,8 +14,9 @@ uniform sampler2D texture0; void main() { - float clear = round(texture(texture0, TexCoords).a); + vec4 textureColour = texture(texture0, TexCoords); + float clear = round(mix(textureColour.a, 1, Colour.a)); gPosition = vec4(FragPos, clear); gNormal = vec4(Normal, clear); - gAlbedoSpec = mix(texture(texture0, TexCoords), vec4(Colour.rgb, 1), Colour.a); + gAlbedoSpec = mix(textureColour, vec4(Colour.rgb, 1), Colour.a); } -- cgit v1.2.3 From cfcc293fb24dcbe5f1ed3ea53aaffa769690d5ed Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 14 Mar 2023 01:01:18 +0000 Subject: Globally enable GLM vector swizzles --- Jamroot.jam | 1 + 1 file changed, 1 insertion(+) diff --git a/Jamroot.jam b/Jamroot.jam index 27db410..e2075cf 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -90,6 +90,7 @@ lib ilt : off ] : + GLM_FORCE_SWIZZLE debug:pedantic debug:on debug:-Wnon-virtual-dtor -- cgit v1.2.3 From 2d6772cb1c592e4bd75be40f1fdfa924bbbc3c07 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 14 Mar 2023 18:14:54 +0000 Subject: Add postLoad support to persistence --- lib/persistence.cpp | 5 +++++ lib/persistence.h | 10 ++++++++-- test/test-persistence.cpp | 7 +++++++ test/testStructures.cpp | 6 ++++++ test/testStructures.h | 3 +++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 8c7c6a4..06e40c7 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -41,6 +41,11 @@ namespace Persistence { return ss.str(); } + void + Persistable::postLoad() + { + } + PersistenceSelect::PersistenceSelect(const std::string & n) : name {n} { } PersistenceStore::NameAction diff --git a/lib/persistence.h b/lib/persistence.h index 35d60ca..1cb3af0 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -311,8 +311,9 @@ namespace Persistence { void endObject(Persistence::Stack & stk) override { + // TODO test with unique_ptr map.emplace(std::invoke(Key, s), std::move(s)); - stk.pop(); + Persistence::SelectionT::endObject(stk); } private: @@ -327,8 +328,9 @@ namespace Persistence { void endObject(Persistence::Stack & stk) override { + // TODO test with unique_ptr container.emplace_back(std::move(s)); - stk.pop(); + Persistence::SelectionT::endObject(stk); } private: @@ -342,6 +344,7 @@ namespace Persistence { DEFAULT_MOVE_COPY(Persistable); virtual bool persist(PersistenceStore & store) = 0; + virtual void postLoad(); [[nodiscard]] virtual std::string getId() const; @@ -484,6 +487,9 @@ namespace Persistence { endObject(Stack & stk) override { make_default_as_needed(this->v); + if (this->v) { + this->v->postLoad(); + } stk.pop(); } diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index a72c481..38bbf2f 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -34,6 +34,7 @@ struct JPP { BOOST_FIXTURE_TEST_CASE(load_object, JPP) { auto to = load_json>(FIXTURESDIR "json/load_object.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_CHECK_CLOSE(to->flt, 3.14, 0.01); BOOST_CHECK_EQUAL(to->str, "Lovely string"); BOOST_CHECK_EQUAL(to->bl, true); @@ -69,6 +70,7 @@ BOOST_FIXTURE_TEST_CASE(load_object, JPP) BOOST_FIXTURE_TEST_CASE(load_nested_object, JPP) { auto to = load_json>(FIXTURESDIR "json/nested.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_CHECK_EQUAL(to->flt, 1.F); BOOST_CHECK_EQUAL(to->str, "one"); BOOST_REQUIRE(to->ptr); @@ -86,6 +88,7 @@ BOOST_FIXTURE_TEST_CASE(load_nested_object, JPP) BOOST_FIXTURE_TEST_CASE(load_implicit_object, JPP) { auto to = load_json>(FIXTURESDIR "json/implicit.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_CHECK(to->ptr); BOOST_CHECK_EQUAL(to->flt, 1.F); BOOST_CHECK_EQUAL(to->ptr->str, "trigger"); @@ -95,6 +98,7 @@ BOOST_FIXTURE_TEST_CASE(load_implicit_object, JPP) BOOST_FIXTURE_TEST_CASE(load_empty_object, JPP) { auto to = load_json>(FIXTURESDIR "json/empty.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_CHECK_EQUAL(to->flt, 1.F); BOOST_CHECK(to->ptr); BOOST_CHECK_EQUAL(to->str, "after"); @@ -119,6 +123,7 @@ BOOST_FIXTURE_TEST_CASE(load_obj_no_such_type, JPP) BOOST_FIXTURE_TEST_CASE(load_abs_object, JPP) { auto to = load_json>(FIXTURESDIR "json/abs.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_REQUIRE(to->aptr); BOOST_CHECK_NO_THROW(to->aptr->dummy()); BOOST_CHECK_EQUAL(to->aptr->base, "set base"); @@ -130,6 +135,7 @@ BOOST_FIXTURE_TEST_CASE(load_abs_object, JPP) BOOST_FIXTURE_TEST_CASE(load_vector_ptr, JPP) { auto to = load_json>(FIXTURESDIR "json/vector_ptr.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_CHECK(to->str.empty()); BOOST_CHECK_EQUAL(to->vptr.size(), 4); BOOST_CHECK_EQUAL(to->vptr.at(0)->str, "type"); @@ -141,6 +147,7 @@ BOOST_FIXTURE_TEST_CASE(load_vector_ptr, JPP) BOOST_FIXTURE_TEST_CASE(test_conversion, JPP) { auto to = load_json>(FIXTURESDIR "json/conv.json"); + BOOST_CHECK_EQUAL(to->postLoadCalled, 1); BOOST_REQUIRE(to); BOOST_CHECK_EQUAL(to->bl, true); BOOST_CHECK_EQUAL(to->flt, 3.14F); diff --git a/test/testStructures.cpp b/test/testStructures.cpp index 8305078..469ec37 100644 --- a/test/testStructures.cpp +++ b/test/testStructures.cpp @@ -42,6 +42,12 @@ TestObject::persist(Persistence::PersistenceStore & store) && STORE_MEMBER(vptr); } +void +TestObject::postLoad() +{ + postLoadCalled++; +} + bool SharedTestObject::persist(Persistence::PersistenceStore & store) { diff --git a/test/testStructures.h b/test/testStructures.h index 666562e..6966052 100644 --- a/test/testStructures.h +++ b/test/testStructures.h @@ -39,7 +39,10 @@ struct TestObject : public Persistence::Persistable { std::unique_ptr aptr; std::vector> vptr; + unsigned int postLoadCalled {}; + bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; }; struct SharedTestObject : public Persistence::Persistable { -- cgit v1.2.3 From 3fea21a2d8f2aa67fd212837fe7e09e4f29ad515 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 14 Mar 2023 19:16:57 +0000 Subject: Support creating a super texture from fragments Currently makes wild assumptions about vertices and doesn't actually populate the texture, it's just grey --- assetFactory/asset.cpp | 10 ++++++ assetFactory/asset.h | 6 ++++ assetFactory/assetFactory.cpp | 62 +++++++++++++++++++++++++++++++++++++- assetFactory/assetFactory.h | 10 ++++++ assetFactory/factoryMesh.cpp | 6 ++-- assetFactory/modelFactoryMesh.h | 4 ++- assetFactory/style.cpp | 17 +++++++++-- game/vehicles/railVehicleClass.cpp | 6 ++++ game/vehicles/railVehicleClass.h | 1 + 9 files changed, 114 insertions(+), 8 deletions(-) diff --git a/assetFactory/asset.cpp b/assetFactory/asset.cpp index 3ab2f1c..e3f5feb 100644 --- a/assetFactory/asset.cpp +++ b/assetFactory/asset.cpp @@ -1,4 +1,5 @@ #include "asset.h" +#include "assetFactory.h" bool Asset::persist(Persistence::PersistenceStore & store) @@ -6,6 +7,15 @@ Asset::persist(Persistence::PersistenceStore & store) return STORE_MEMBER(id) && STORE_MEMBER(name); } +Asset::TexturePtr +Asset::getTexture() const +{ + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { + return mf->getTexture(); + } + return nullptr; +} + Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) : Persistence::SelectionPtrBase {fmesh}, out {m} { } diff --git a/assetFactory/asset.h b/assetFactory/asset.h index e3318e4..30f40cd 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -4,12 +4,18 @@ #include "persistence.h" #include +class Texture; + class Asset : public Persistence::Persistable, public StdTypeDefs { public: + using TexturePtr = std::shared_ptr; + std::string id; std::string name; protected: + TexturePtr getTexture() const; + struct MeshConstruct : public Persistence::SelectionPtrBase { using Persistence::SelectionPtrBase::setValue; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index de13579..276c258 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -2,11 +2,16 @@ #include "collections.hpp" #include "cuboid.h" #include "cylinder.h" +#include "filesystem.h" +#include "gfx/image.h" +#include "gfx/models/texture.h" #include "modelFactoryMesh_fwd.h" #include "object.h" #include "plane.h" +#include "resource.h" #include "saxParse-persistence.h" -#include +#include "texturePacker.h" +#include AssetFactory::AssetFactory() : shapes { @@ -76,6 +81,61 @@ AssetFactory::parseColour(std::string_view in) const throw std::runtime_error("No such asset factory colour"); } +AssetFactory::TextureFragmentCoords +AssetFactory::getTextureCoords(std::string_view id) const +{ + createTexutre(); + const auto & fragmentUV = textureFragmentPositions.at(id); + return { + fragmentUV.xy(), + fragmentUV.zy(), + fragmentUV.zw(), + fragmentUV.xw(), + }; +} + +Asset::TexturePtr +AssetFactory::getTexture() const +{ + createTexutre(); + return texture; +} + +void +AssetFactory::createTexutre() const +{ + if (!textureFragments.empty() && (!texture || textureFragmentPositions.empty())) { + // * load images + std::vector> images; + std::transform( + textureFragments.begin(), textureFragments.end(), std::back_inserter(images), [](const auto & tf) { + return std::make_unique(Resource::mapPath(tf.second->path), STBI_rgb_alpha); + }); + // * layout images + std::vector imageSizes; + std::transform(images.begin(), images.end(), std::back_inserter(imageSizes), [](const auto & image) { + return TexturePacker::Image {image->width, image->height}; + }); + const auto [layout, outSize] = TexturePacker {imageSizes}.pack(); + // * create texture + std::vector textureData(TexturePacker::area(outSize), {127, 127, 127, 255}); + std::transform(textureFragments.begin(), textureFragments.end(), + std::inserter(textureFragmentPositions, textureFragmentPositions.end()), + [position = layout.begin(), size = imageSizes.begin(), outSize = outSize](const auto & tf) mutable { + const glm::vec4 positionFraction { + static_cast(position->x) / static_cast(outSize.x), + static_cast(position->y) / static_cast(outSize.y), + static_cast(position->x + size->x) / static_cast(outSize.x), + static_cast(position->y + size->y) / static_cast(outSize.y), + }; + position++; + size++; + return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction}; + }); + texture = std::make_shared(outSize.x, outSize.y, textureData.data()); + } +} + bool AssetFactory::persist(Persistence::PersistenceStore & store) { diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 3d52f7f..52692c4 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -6,6 +6,8 @@ #include "textureFragment.h" #include +class Texture; + class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; @@ -14,10 +16,13 @@ public: using Colour = glm::vec3; using ColourAlpha = glm::vec4; using Colours = std::map>; + using TextureFragmentCoords = std::array; AssetFactory(); [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); [[nodiscard]] ColourAlpha parseColour(std::string_view) const; + [[nodiscard]] TextureFragmentCoords getTextureCoords(std::string_view) const; + [[nodiscard]] Asset::TexturePtr getTexture() const; Shapes shapes; Assets assets; @@ -30,4 +35,9 @@ public: private: friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; + + void createTexutre() const; + + mutable Asset::TexturePtr texture; + mutable std::map> textureFragmentPositions; }; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 1665b90..c47e80e 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -2,13 +2,10 @@ #include "collections.hpp" #include "gfx/models/vertex.hpp" #include "modelFactoryMesh.h" -#include Mesh::Ptr FactoryMesh::createMesh() const { - constexpr glm::vec2 NullUV {}; - ModelFactoryMesh mesh; for (const auto & use : uses) { use->createMesh(mesh, 1); @@ -23,7 +20,8 @@ FactoryMesh::createMesh() const 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, + const auto textureUV = mesh.texcoord2D(vertex); + vertices.emplace_back(mesh.point(vertex), textureUV, smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) : mesh.property(mesh.face_normals_pph(), face), colour); diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index 149ff1a..493ce1b 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -34,10 +35,11 @@ namespace OpenMesh { struct ModelFactoryTraits : public OpenMesh::DefaultTraits { FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status | OpenMesh::Attributes::Color); EdgeAttributes(OpenMesh::Attributes::Status); - VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status | OpenMesh::Attributes::TexCoord2D); using Point = glm::vec3; using Normal = glm::vec3; using Color = glm::vec4; + using TexCoord2D = glm::vec2; }; struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT { diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index b5ddcf1..8aecc71 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -19,8 +19,21 @@ void Style::applyStyle( ModelFactoryMesh & mesh, const ModelFactoryMesh::FaceHandle & face, EffectiveColour effectiveColour) const { - if (effectiveColour.has_value()) { - mesh.set_color(face, effectiveColour->get()); + if (texture.empty()) { + if (effectiveColour.has_value()) { + mesh.set_color(face, effectiveColour->get()); + } + } + else { + mesh.set_color(face, {}); + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { + auto coords = mf->getTextureCoords(texture); + auto coord = coords.begin(); + // Wild assumption that face is a quad and the texture should apply linearly + for (const auto & vh : mesh.fv_range(face)) { + mesh.set_texcoord2D(vh, *coord++); + } + } } } diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 41ef5e9..4e9263c 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -49,6 +49,12 @@ RailVehicleClass::persist(Persistence::PersistenceStore & store) && STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store); } +void +RailVehicleClass::postLoad() +{ + texture = getTexture(); +} + 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 a2222fb..61ec4ec 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -30,6 +30,7 @@ public: protected: friend Persistence::SelectionPtrBase>; bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; private: RailVehicleClass(std::unique_ptr obj, std::shared_ptr); -- cgit v1.2.3 From ee1912e4f181594df40203f9c227b89d02a91242 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 15 Mar 2023 01:04:05 +0000 Subject: Add support for setting Texture options on construction --- gfx/models/texture.cpp | 20 +++++++++++--------- gfx/models/texture.h | 12 +++++++++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index ef6d7e7..87b73e7 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -12,26 +12,28 @@ Cache Texture::cachedTexture; -Texture::Texture(const std::filesystem::path & fileName) : - Texture {Image {Resource::mapPath(fileName).c_str(), STBI_rgb_alpha}} +Texture::Texture(const std::filesystem::path & fileName, TextureOptions to) : + Texture {Image {Resource::mapPath(fileName).c_str(), STBI_rgb_alpha}, to} { } -Texture::Texture(const Image & tex) : - Texture {static_cast(tex.width), static_cast(tex.height), tex.data.data()} +Texture::Texture(const Image & tex, TextureOptions to) : + Texture {static_cast(tex.width), static_cast(tex.height), tex.data.data(), to} { } -Texture::Texture(GLsizei width, GLsizei height, const void * data) +Texture::Texture(GLsizei width, GLsizei height, TextureOptions to) : Texture {width, height, nullptr, to} { } + +Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOptions to) { glBindTexture(GL_TEXTURE_2D, m_texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, to.wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, to.wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, to.minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, to.magFilter); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } diff --git a/gfx/models/texture.h b/gfx/models/texture.h index 1aad1e0..31ffaa5 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -8,11 +8,17 @@ // IWYU pragma: no_forward_declare Cache class Image; +struct TextureOptions { + GLint wrap {GL_REPEAT}; + GLint minFilter {GL_LINEAR}, magFilter {GL_LINEAR}; +}; + class Texture { public: - explicit Texture(const std::filesystem::path & fileName); - explicit Texture(const Image & image); - explicit Texture(GLsizei width, GLsizei height, const void * data); + explicit Texture(const std::filesystem::path & fileName, TextureOptions = {}); + explicit Texture(const Image & image, TextureOptions = {}); + explicit Texture(GLsizei width, GLsizei height, TextureOptions = {}); + explicit Texture(GLsizei width, GLsizei height, const void * data, TextureOptions = {}); static Cache cachedTexture; -- cgit v1.2.3 From 5d7bb934068b0f2ce8b6315517a005cc2c1305f5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 15 Mar 2023 02:08:43 +0000 Subject: Texture member to save the texture as a TGA --- gfx/models/texture.cpp | 6 ++++++ gfx/models/texture.h | 1 + 2 files changed, 7 insertions(+) diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index 87b73e7..639ae0f 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -62,6 +62,12 @@ Texture::save(const glTexture & texture, GLenum format, GLenum type, const glm:: tga.msync(MS_ASYNC); } +void +Texture::save(const glm::ivec2 & size, const char * path) const +{ + save(m_texture, GL_BGR, GL_UNSIGNED_BYTE, size, 3, path, 2); +} + void Texture::save(const glTexture & texture, const glm::ivec2 & size, const char * path) { diff --git a/gfx/models/texture.h b/gfx/models/texture.h index 31ffaa5..cc0c07e 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -24,6 +24,7 @@ public: void bind(GLenum unit = GL_TEXTURE0) const; + void save(const glm::ivec2 & size, const char * path) const; static void save(const glTexture &, const glm::ivec2 & size, const char * path); static void saveDepth(const glTexture &, const glm::ivec2 & size, const char * path); static void saveNormal(const glTexture &, const glm::ivec2 & size, const char * path); -- cgit v1.2.3 From ac90f8e60b1c971c342cdd98769388563e157557 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 15 Mar 2023 02:10:31 +0000 Subject: Populate super texture with fragments This is kinda buggy due to TexturePacker sorting its input array and the client having no idea the order of the results. --- assetFactory/assetFactory.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 276c258..77dd554 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -118,21 +118,25 @@ AssetFactory::createTexutre() const }); const auto [layout, outSize] = TexturePacker {imageSizes}.pack(); // * create texture - std::vector textureData(TexturePacker::area(outSize), {127, 127, 127, 255}); + texture = std::make_shared(outSize.x, outSize.y, TextureOptions {.wrap = GL_CLAMP_TO_EDGE}); std::transform(textureFragments.begin(), textureFragments.end(), std::inserter(textureFragmentPositions, textureFragmentPositions.end()), - [position = layout.begin(), size = imageSizes.begin(), outSize = outSize](const auto & tf) mutable { - const glm::vec4 positionFraction { - static_cast(position->x) / static_cast(outSize.x), - static_cast(position->y) / static_cast(outSize.y), - static_cast(position->x + size->x) / static_cast(outSize.x), - static_cast(position->y + size->y) / static_cast(outSize.y), + [position = layout.begin(), image = images.begin(), size = imageSizes.begin(), + outSize = glm::vec2 {outSize}](const auto & tf) mutable { + glm::vec4 positionFraction { + static_cast(position->x) / outSize.x, + static_cast(position->y) / outSize.y, + static_cast(position->x + size->x) / outSize.x, + static_cast(position->y + size->y) / outSize.y, }; + glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast(position->x), static_cast(position->y), + static_cast(size->x), static_cast(size->y), GL_RGBA, GL_UNSIGNED_BYTE, + image->get()->data.data()); position++; + image++; size++; return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction}; }); - texture = std::make_shared(outSize.x, outSize.y, textureData.data()); } } -- cgit v1.2.3 From 393eba5f9f72fdec566b1d6771d40916793c2bc1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 16 Mar 2023 23:21:25 +0000 Subject: Allow specifiying the texture type/target --- gfx/models/texture.cpp | 16 ++++++++-------- gfx/models/texture.h | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index 639ae0f..efc76e1 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -24,24 +24,24 @@ Texture::Texture(const Image & tex, TextureOptions to) : Texture::Texture(GLsizei width, GLsizei height, TextureOptions to) : Texture {width, height, nullptr, to} { } -Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOptions to) +Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOptions to) : type {to.type} { - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(type, m_texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, to.wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, to.wrap); + glTexParameteri(type, GL_TEXTURE_WRAP_S, to.wrap); + glTexParameteri(type, GL_TEXTURE_WRAP_T, to.wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, to.minFilter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, to.magFilter); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(type, GL_TEXTURE_MIN_FILTER, to.minFilter); + glTexParameteri(type, GL_TEXTURE_MAG_FILTER, to.magFilter); + glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } void Texture::bind(GLenum unit) const { glActiveTexture(unit); - glBindTexture(GL_TEXTURE_2D, m_texture); + glBindTexture(type, m_texture); } void diff --git a/gfx/models/texture.h b/gfx/models/texture.h index cc0c07e..ffc9a4a 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -11,6 +11,7 @@ class Image; struct TextureOptions { GLint wrap {GL_REPEAT}; GLint minFilter {GL_LINEAR}, magFilter {GL_LINEAR}; + GLenum type {GL_TEXTURE_2D}; }; class Texture { @@ -34,4 +35,5 @@ private: const char * path, short tgaFormat); glTexture m_texture; + GLenum type; }; -- cgit v1.2.3 From 07dc690837f6a665490e370e8a07410e970384d7 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 16 Mar 2023 23:23:04 +0000 Subject: Simplify calculation of texture position fraction --- assetFactory/assetFactory.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 77dd554..ed1af58 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -123,12 +123,7 @@ AssetFactory::createTexutre() const std::inserter(textureFragmentPositions, textureFragmentPositions.end()), [position = layout.begin(), image = images.begin(), size = imageSizes.begin(), outSize = glm::vec2 {outSize}](const auto & tf) mutable { - glm::vec4 positionFraction { - static_cast(position->x) / outSize.x, - static_cast(position->y) / outSize.y, - static_cast(position->x + size->x) / outSize.x, - static_cast(position->y + size->y) / outSize.y, - }; + const auto positionFraction = glm::vec4 {*position, *position + *size} / outSize.xyxy(); glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast(position->x), static_cast(position->y), static_cast(size->x), static_cast(size->y), GL_RGBA, GL_UNSIGNED_BYTE, image->get()->data.data()); -- cgit v1.2.3 From 7867a8a91a2210e1fb4f4bfb71d24ef4c1e8bd86 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 17 Mar 2023 00:35:57 +0000 Subject: Update test/fixture/resource dependencies --- test/Jamfile.jam | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Jamfile.jam b/test/Jamfile.jam index e178573..e38a80e 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -1,5 +1,6 @@ import testing ; import sequence ; +import path : glob-tree ; lib boost_unit_test_framework ; lib benchmark ; @@ -48,12 +49,12 @@ run test-maths.cpp ; run test-lib.cpp ; run test-geo.cpp ; run test-network.cpp ; -run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob fixtures/json/*.json fixtures/json/bad/*.json ] ] : test ; +run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob-tree fixtures : *.json ] ] : test ; run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; -run test-assetFactory.cpp : -- : ../res/brush47.xml : test ; +run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.xml *.png ] fixtures/rgb.txt ] : test ; run perf-assetFactory.cpp : : : benchmark test ; run perf-persistence.cpp : : : benchmark test ; compile test-static-enumDetails.cpp ; -- cgit v1.2.3 From d75e737de182d11d0293212d6fd0fc2c6732af54 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 17 Mar 2023 02:03:30 +0000 Subject: Allow overriding vectorOfN's returned type --- lib/collections.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/collections.hpp b/lib/collections.hpp index 16870be..2b83500 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -92,11 +92,11 @@ operator+(const std::vector & in, Vn && vn) return out; } -template typename Direction = std::plus> +template typename Direction = std::plus, typename T = unsigned int> [[nodiscard]] static auto -vectorOfN(std::integral auto N, unsigned int start = {}, unsigned int step = 1) +vectorOfN(std::integral auto N, T start = {}, T step = 1) { - std::vector v; + std::vector v; v.resize(N); std::generate_n(v.begin(), N, [&start, step, adj = Direction {}]() { return std::exchange(start, adj(start, step)); -- cgit v1.2.3 From c915f1bcd46144578fad464e52b5a6013dc98de8 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 17 Mar 2023 02:05:42 +0000 Subject: Fix texture packer return value so positions match inputs --- assetFactory/texturePacker.cpp | 15 +++++++++------ assetFactory/texturePacker.h | 5 +++-- test/test-assetFactory.cpp | 5 +++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp index 31c9a0e..68a6010 100644 --- a/assetFactory/texturePacker.cpp +++ b/assetFactory/texturePacker.cpp @@ -1,14 +1,16 @@ #include "texturePacker.h" +#include "collections.hpp" #include #include #include #include #include -TexturePacker::TexturePacker(std::vector in) : inputImages {std::move(in)} +TexturePacker::TexturePacker(std::span in) : + inputImages {std::move(in)}, sortedIndexes {vectorOfN(inputImages.size(), size_t {})} { - std::sort(inputImages.rbegin(), inputImages.rend(), [](const auto & a, const auto & b) { - return area(a) < area(b); + std::sort(sortedIndexes.rbegin(), sortedIndexes.rend(), [this](const auto a, const auto b) { + return area(inputImages[a]) < area(inputImages[b]); }); } @@ -24,15 +26,16 @@ TexturePacker::pack(Size size) const using Spaces = std::set; Spaces spaces {{{}, size}}; - Positions result; - for (const auto & image : inputImages) { + Positions result(inputImages.size()); + for (const auto & idx : sortedIndexes) { + const auto & image = inputImages[idx]; if (const auto spaceItr = std::find_if(spaces.begin(), spaces.end(), [image](const Space & s) { return image.x <= s.size.x && image.y <= s.size.y; }); spaceItr != spaces.end()) { auto space = *spaceItr; - result.push_back(space.position); + result[idx] = space.position; spaces.erase(spaceItr); if (space.size.x > image.x) { spaces.emplace(Position {space.position.x + image.x, space.position.y}, diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h index 8e2061b..7e00f1a 100644 --- a/assetFactory/texturePacker.h +++ b/assetFactory/texturePacker.h @@ -23,7 +23,7 @@ public: using Positions = std::vector; using Result = std::pair; - TexturePacker(std::vector); + TexturePacker(std::span); Result pack(Size) const; Result pack() const; @@ -32,5 +32,6 @@ public: static decltype(Size::x) area(const Size & size); private: - std::vector inputImages; + std::span inputImages; + std::vector sortedIndexes; }; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index ad62a93..d71fa9b 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -129,14 +129,15 @@ BOOST_AUTO_TEST_CASE(parseX11RGB) BOOST_AUTO_TEST_CASE(texturePacker) { - TexturePacker tp {{ + std::vector input { {10, 10}, {10, 10}, {10, 10}, {100, 10}, {10, 200}, {5, 5}, - }}; + }; + TexturePacker tp {input}; BOOST_CHECK_EQUAL(TexturePacker::Size(128, 256), tp.minSize()); const auto result = tp.pack(); } -- cgit v1.2.3 From f412b04cc367168b1a6f921dfa1171a17e1353fc Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 18 Mar 2023 02:12:21 +0000 Subject: Use indices instead of triangulation --- assetFactory/factoryMesh.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index c47e80e..59b390f 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -10,24 +10,31 @@ FactoryMesh::createMesh() const for (const auto & use : uses) { use->createMesh(mesh, 1); } - mesh.garbage_collection(); - mesh.triangulate(); mesh.update_face_normals(); mesh.update_vertex_normals(); std::vector vertices; + std::vector indices; 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)) { + auto vrange = mesh.fv_range(face); + const unsigned int start = static_cast(vertices.size()); + for (const auto & vertex : vrange) { const auto textureUV = mesh.texcoord2D(vertex); vertices.emplace_back(mesh.point(vertex), textureUV, smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) : mesh.property(mesh.face_normals_pph(), face), colour); } + const auto vcount = std::distance(vrange.begin(), vrange.end()); + for (unsigned int i = 2; i < vcount; i++) { + indices.push_back(start); + indices.push_back(start + i - 1); + indices.push_back(start + i); + } } - return std::make_shared(vertices, vectorOfN(vertices.size())); + return std::make_shared(vertices, indices); } bool -- cgit v1.2.3 From 51f0f60bd24a3d7ed77cf713f846bc49c2f1aed5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 18 Mar 2023 02:44:00 +0000 Subject: Use halfedge for texture coordinates More unique than vertex as it is also per face, allowing for a different texture on adjacent faces --- assetFactory/factoryMesh.cpp | 9 +++++---- assetFactory/modelFactoryMesh.h | 3 ++- assetFactory/style.cpp | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 59b390f..6cca388 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -18,16 +18,17 @@ FactoryMesh::createMesh() const for (const auto & face : mesh.faces()) { const auto smooth = mesh.property(mesh.smoothFaceProperty, face); const auto colour = mesh.color(face); - auto vrange = mesh.fv_range(face); + auto hrange = mesh.fh_range(face); const unsigned int start = static_cast(vertices.size()); - for (const auto & vertex : vrange) { - const auto textureUV = mesh.texcoord2D(vertex); + for (const auto & heh : hrange) { + const auto & vertex = mesh.to_vertex_handle(heh); + const auto textureUV = mesh.texcoord2D(heh); vertices.emplace_back(mesh.point(vertex), textureUV, smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) : mesh.property(mesh.face_normals_pph(), face), colour); } - const auto vcount = std::distance(vrange.begin(), vrange.end()); + const auto vcount = std::distance(hrange.begin(), hrange.end()); for (unsigned int i = 2; i < vcount; i++) { indices.push_back(start); indices.push_back(start + i - 1); diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index 493ce1b..a862dc1 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -35,7 +35,8 @@ namespace OpenMesh { struct ModelFactoryTraits : public OpenMesh::DefaultTraits { FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status | OpenMesh::Attributes::Color); EdgeAttributes(OpenMesh::Attributes::Status); - VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status | OpenMesh::Attributes::TexCoord2D); + VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + HalfedgeAttributes(OpenMesh::Attributes::TexCoord2D); using Point = glm::vec3; using Normal = glm::vec3; using Color = glm::vec4; diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 8aecc71..d1d4adc 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -30,8 +30,8 @@ Style::applyStyle( auto coords = mf->getTextureCoords(texture); auto coord = coords.begin(); // Wild assumption that face is a quad and the texture should apply linearly - for (const auto & vh : mesh.fv_range(face)) { - mesh.set_texcoord2D(vh, *coord++); + for (const auto & heh : mesh.fh_range(face)) { + mesh.set_texcoord2D(heh, *coord++); } } } -- cgit v1.2.3 From e1c0ba6226225c026b35e67ec27647baaa64b2a8 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:08:26 +0000 Subject: Make perf tests depend on their functional equivalent --- test/Jamfile.jam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Jamfile.jam b/test/Jamfile.jam index e38a80e..bf1c408 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -55,8 +55,8 @@ run test-enumDetails.cpp ; run test-render.cpp : : : test ; run test-glContextBhvr.cpp ; run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.xml *.png ] fixtures/rgb.txt ] : test ; -run perf-assetFactory.cpp : : : benchmark test ; -run perf-persistence.cpp : : : benchmark test ; +run perf-assetFactory.cpp : : : benchmark test test-assetFactory ; +run perf-persistence.cpp : : : benchmark test test-persistence ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; -- cgit v1.2.3 From 60ca9a1cdec3685e1b8496bd9c9dc7c9486157e7 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:28:56 +0000 Subject: Add aggregrate constructor to TexturePacker::Area as required --- assetFactory/texturePacker.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h index 7e00f1a..ca0d67a 100644 --- a/assetFactory/texturePacker.h +++ b/assetFactory/texturePacker.h @@ -10,6 +10,10 @@ public: using Size = glm::uvec2; struct Area { +#ifndef __cpp_aggregate_paren_init + constexpr Area(Position p, Size s) : position {std::move(p)}, size {std::move(s)} { } +#endif + Position position; Size size; bool -- cgit v1.2.3 From 997e03433fd41ad2ba1dc4242ff5a90f55ce694d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:30:02 +0000 Subject: Make Vertex aggregrate constructor conditional on requirement --- gfx/models/vertex.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gfx/models/vertex.hpp b/gfx/models/vertex.hpp index 325aab1..28dc8f3 100644 --- a/gfx/models/vertex.hpp +++ b/gfx/models/vertex.hpp @@ -4,10 +4,12 @@ class Vertex { public: +#ifndef __cpp_aggregate_paren_init 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)} { } +#endif glm::vec3 pos; glm::vec2 texCoord; -- cgit v1.2.3 From c3617a13b6e4d9fb50d32478c68fae94be1b2cba Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:31:17 +0000 Subject: Add defaulted Vertex equality operator --- gfx/models/vertex.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gfx/models/vertex.hpp b/gfx/models/vertex.hpp index 28dc8f3..64ec3d0 100644 --- a/gfx/models/vertex.hpp +++ b/gfx/models/vertex.hpp @@ -11,6 +11,8 @@ public: } #endif + bool operator==(const Vertex &) const = default; + glm::vec3 pos; glm::vec2 texCoord; glm::vec3 normal; -- cgit v1.2.3 From 40cc01f79c6308b2e4b64fab73cb27d4221a648e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:32:47 +0000 Subject: Dedupe vertices during asset factory mesh build This calls Vertex::== far too many times, but it's not (yet) enough to be a problem --- assetFactory/factoryMesh.cpp | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 6cca388..8869efd 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -16,23 +16,31 @@ FactoryMesh::createMesh() const std::vector vertices; std::vector indices; for (const auto & face : mesh.faces()) { - const auto smooth = mesh.property(mesh.smoothFaceProperty, face); - const auto colour = mesh.color(face); - auto hrange = mesh.fh_range(face); - const unsigned int start = static_cast(vertices.size()); - for (const auto & heh : hrange) { + const auto & smooth = mesh.property(mesh.smoothFaceProperty, face); + const auto & colour = mesh.color(face); + + std::vector faceIndices; + for (const auto & heh : mesh.fh_range(face)) { const auto & vertex = mesh.to_vertex_handle(heh); - const auto textureUV = mesh.texcoord2D(heh); - vertices.emplace_back(mesh.point(vertex), textureUV, - smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) - : mesh.property(mesh.face_normals_pph(), face), - colour); + const auto & textureUV = mesh.texcoord2D(heh); + const auto & point = mesh.point(vertex); + const auto & normal = smooth ? mesh.property(mesh.vertex_normals_pph(), vertex) + : mesh.property(mesh.face_normals_pph(), face); + Vertex outVertex {point, textureUV, normal, colour}; + if (const auto existingItr = std::find(vertices.rbegin(), vertices.rend(), outVertex); + existingItr != vertices.rend()) { + faceIndices.push_back(static_cast(std::distance(existingItr, vertices.rend()) - 1)); + } + else { + faceIndices.push_back(static_cast(vertices.size())); + vertices.emplace_back(outVertex); + } } - const auto vcount = std::distance(hrange.begin(), hrange.end()); - for (unsigned int i = 2; i < vcount; i++) { - indices.push_back(start); - indices.push_back(start + i - 1); - indices.push_back(start + i); + + for (unsigned int i = 2; i < faceIndices.size(); i++) { + indices.push_back(faceIndices[0]); + indices.push_back(faceIndices[i - 1]); + indices.push_back(faceIndices[i]); } } return std::make_shared(vertices, indices); -- cgit v1.2.3 From d862385c8956b9cc1002323feddf0b65a777a1c1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:52:22 +0000 Subject: Use OpenMesh built-in to calculate face centre --- assetFactory/faceController.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 1ec1467..2fb0412 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -33,8 +33,7 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const return std::move(out); }); const auto vertexCount = points.size(); - const auto centre - = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast(vertexCount); + const auto centre = mesh.calc_face_centroid(cf.second); if (type == "extrude") { Shape::CreatedFaces newFaces; // mutate points -- cgit v1.2.3 From 187783a322faed9c830493cc0346376e9809c9b5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 19 Mar 2023 23:59:49 +0000 Subject: Remove (as yet) unused members from Camera --- gfx/gl/camera.cpp | 4 ++-- gfx/gl/camera.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/gl/camera.cpp b/gfx/gl/camera.cpp index b596c43..5b7269e 100644 --- a/gfx/gl/camera.cpp +++ b/gfx/gl/camera.cpp @@ -6,8 +6,8 @@ #include Camera::Camera(glm::vec3 pos, float fov, float aspect, float zNear, float zFar) : - position {pos}, forward {::north}, up {::up}, fov {fov}, aspect {aspect}, near {zNear}, far {zFar}, - projection {glm::perspective(fov, aspect, zNear, zFar)}, + position {pos}, forward {::north}, up {::up}, near {zNear}, far {zFar}, projection {glm::perspective( + fov, aspect, zNear, zFar)}, viewProjection {projection * glm::lookAt(position, position + forward, up)}, inverseViewProjection { glm::inverse(viewProjection)} { diff --git a/gfx/gl/camera.h b/gfx/gl/camera.h index 9685a7d..b5611f8 100644 --- a/gfx/gl/camera.h +++ b/gfx/gl/camera.h @@ -72,7 +72,7 @@ private: glm::vec3 forward; glm::vec3 up; - float fov, aspect, near, far; + float near, far; glm::mat4 projection; glm::mat4 viewProjection, inverseViewProjection; }; -- cgit v1.2.3 From 5f0fbcef6dbc1421ea2b538c7f839b2f37c6e36e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 00:11:43 +0000 Subject: Fix some name shadowing in FaceController --- assetFactory/faceController.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 2fb0412..1e563b2 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -52,9 +52,9 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const const auto next = (idx + 1) % vertexCount; const auto newFace = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]}); - auto & name = mesh.property(mesh.nameFaceProperty, newFace); - name = getAdjacentFaceName(ofrange, newFace); - newFaces.emplace(name, newFace); + auto & newFaceName = mesh.property(mesh.nameFaceProperty, newFace); + newFaceName = getAdjacentFaceName(ofrange, newFace); + newFaces.emplace(newFaceName, newFace); } newFaces.emplace(name, mesh.add_face(vertices)); if (smooth) { @@ -63,8 +63,8 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const } } applyStyle(mesh, parents + this, newFaces); - for (const auto & [name, faceController] : faceControllers) { - faceController->apply(mesh, parents + this, name, newFaces); + for (const auto & [subFaceName, faceController] : faceControllers) { + faceController->apply(mesh, parents + this, subFaceName, newFaces); } faces.merge(std::move(newFaces)); } -- cgit v1.2.3 From 3687243d2412b0923d2a81a0c13e25ed7f45a893 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 01:07:57 +0000 Subject: Fix left/right swap of cuboid face names --- assetFactory/cuboid.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assetFactory/cuboid.cpp b/assetFactory/cuboid.cpp index f200258..a8ddcd9 100644 --- a/assetFactory/cuboid.cpp +++ b/assetFactory/cuboid.cpp @@ -21,8 +21,8 @@ Cuboid::createMesh(ModelFactoryMesh & mesh, float) const return { mesh.add_namedFace("top", vhs[4], vhs[5], vhs[6], vhs[7]), mesh.add_namedFace("bottom", vhs[0], vhs[1], vhs[2], vhs[3]), - mesh.add_namedFace("left", vhs[0], vhs[7], vhs[6], vhs[1]), - mesh.add_namedFace("right", vhs[2], vhs[5], vhs[4], vhs[3]), + mesh.add_namedFace("right", vhs[0], vhs[7], vhs[6], vhs[1]), + mesh.add_namedFace("left", 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]), }; -- cgit v1.2.3 From b5db39e7de50cff669920ab1f279df223d257a51 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 00:26:16 +0000 Subject: Support applying a single face controller to multiple faces --- assetFactory/faceController.cpp | 13 ++++++++++++- assetFactory/faceController.h | 5 ++++- res/brush47.xml | 12 ++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 1e563b2..6493300 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -4,7 +4,18 @@ #include "modelFactoryMesh.h" void -FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, +FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & names, + Shape::CreatedFaces & faces) const +{ + std::stringstream nameStream {names}; + std::for_each(std::istream_iterator(nameStream), std::istream_iterator {}, + [&](const auto & name) { + applySingle(mesh, parents, name, faces); + }); +} + +void +FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const { const auto getAdjacentFaceName = [&mesh](const auto & ofrange, OpenMesh::FaceHandle nf) -> std::string { diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 10a226a..890aafa 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -12,7 +12,7 @@ class FaceController : public Mutation, public Style, public Persistence::Persis public: using FaceControllers = std::map>; - void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, + void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & names, Shape::CreatedFaces & faces) const; std::string id; @@ -28,4 +28,7 @@ private: { return {}; }; + + void applySingle(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, + Shape::CreatedFaces & faces) const; }; diff --git a/res/brush47.xml b/res/brush47.xml index c345c82..f74cb29 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -2,8 +2,7 @@ - - + @@ -66,15 +65,12 @@ - - + - - + - - + -- cgit v1.2.3 From fe1913b3f3742077763d7a6fc9fcac219900a4ae Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 18:55:44 +0000 Subject: Slightly tidier FaceController? It's still a mess --- assetFactory/faceController.cpp | 126 +++++++++++++++++++++------------------- assetFactory/faceController.h | 3 + 2 files changed, 68 insertions(+), 61 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 6493300..bafbee2 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -14,82 +14,86 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const }); } +std::string +FaceController::getAdjacentFaceName( + const ModelFactoryMesh & mesh, const std::span ofrange, OpenMesh::FaceHandle nf) +{ + 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 {}; +}; + void FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const { - const auto getAdjacentFaceName = [&mesh](const auto & ofrange, OpenMesh::FaceHandle nf) -> std::string { - const auto nfrange = mesh.ff_range(nf); - if (const auto target = std::find_first_of(ofrange.begin(), ofrange.end(), nfrange.begin(), nfrange.end()); - target != ofrange.end()) { - return mesh.property(mesh.nameFaceProperty, *target); - }; - return {}; - }; - const auto controlledFaces {materializeRange(faces.equal_range(name))}; if (controlledFaces.empty()) { throw std::runtime_error("Named face(s) do not exist: " + name); } if (!type.empty()) { - const auto mutation = getMatrix(); faces.erase(name); - for (const auto & cf : controlledFaces) { - // get points - const auto baseVertices {materializeRange(mesh.fv_range(cf.second))}; - auto points = std::accumulate(baseVertices.begin(), baseVertices.end(), std::vector {}, - [&mesh](auto && out, auto && v) { - out.push_back(mesh.point(v)); - return std::move(out); - }); - const auto vertexCount = points.size(); - const auto centre = mesh.calc_face_centroid(cf.second); - 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); - }); - // create new faces - const auto ofrange = materializeRange(mesh.ff_range(cf.second)); - mesh.delete_face(cf.second); - for (size_t idx {}; idx < vertexCount; ++idx) { - const auto next = (idx + 1) % vertexCount; - const auto newFace - = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]}); - auto & newFaceName = mesh.property(mesh.nameFaceProperty, newFace); - newFaceName = getAdjacentFaceName(ofrange, newFace); - newFaces.emplace(newFaceName, newFace); - } - newFaces.emplace(name, mesh.add_face(vertices)); - if (smooth) { - for (const auto & [name, face] : newFaces) { - mesh.property(mesh.smoothFaceProperty, face) = true; - } - } - applyStyle(mesh, parents + this, newFaces); - for (const auto & [subFaceName, faceController] : faceControllers) { - faceController->apply(mesh, parents + this, subFaceName, newFaces); - } - faces.merge(std::move(newFaces)); - } - else { - mesh.property(mesh.smoothFaceProperty, cf.second) = smooth; - applyStyle(mesh, parents + this, cf.second); + } + for (const auto & [faceName, faceHandle] : controlledFaces) { + if (type == "extrude") { + auto newFaces = extrude(mesh, faceName, faceHandle); + applyStyle(mesh, parents + this, newFaces); + for (const auto & [subFaceName, faceController] : faceControllers) { + faceController->apply(mesh, parents + this, subFaceName, newFaces); } + faces.merge(std::move(newFaces)); } - } - else { - for (const auto & cf : controlledFaces) { - applyStyle(mesh, parents + this, cf.second); + else { + mesh.property(mesh.smoothFaceProperty, faceHandle) = smooth; + applyStyle(mesh, parents + this, faceHandle); } } + for (const auto & [faceName, faceHandle] : controlledFaces) { + mesh.property(mesh.smoothFaceProperty, faceHandle) = smooth; + applyStyle(mesh, parents + this, faceHandle); + } +} + +Shape::CreatedFaces +FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle faceHandle) const +{ + // get points + const auto baseVertices {materializeRange(mesh.fv_range(faceHandle))}; + 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 = mesh.calc_face_centroid(faceHandle); + Shape::CreatedFaces newFaces; + // mutate points + std::for_each(points.begin(), points.end(), [mutation = getMatrix(), ¢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); + }); + // create new faces + const auto ofrange = materializeRange(mesh.ff_range(faceHandle)); + mesh.delete_face(faceHandle); + for (size_t idx {}; idx < vertexCount; ++idx) { + const auto next = (idx + 1) % vertexCount; + const auto newFace = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]}); + auto & newFaceName = mesh.property(mesh.nameFaceProperty, newFace); + newFaceName = getAdjacentFaceName(mesh, ofrange, newFace); + newFaces.emplace(newFaceName, newFace); + mesh.property(mesh.smoothFaceProperty, newFace) = smooth; + } + mesh.property(mesh.smoothFaceProperty, newFaces.emplace(faceName, mesh.add_face(vertices))->second) = smooth; + + return newFaces; } bool diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 890aafa..962a516 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -31,4 +31,7 @@ private: void applySingle(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const; + static std::string getAdjacentFaceName(const ModelFactoryMesh & mesh, + const std::span ofrange, OpenMesh::FaceHandle nf); + Shape::CreatedFaces extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle) const; }; -- cgit v1.2.3 From 5bfeeb70c6a7ed6ccda0576dc39050eb4240434f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 19:32:26 +0000 Subject: Lazy initialisation of SelectionPtr in persistValue Always needed for write phase, but only need on read if their's a name match. --- lib/persistence.cpp | 22 ++++++++++++++-------- lib/persistence.h | 13 ++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 06e40c7..e22d74d 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -48,10 +48,15 @@ namespace Persistence { PersistenceSelect::PersistenceSelect(const std::string & n) : name {n} { } - PersistenceStore::NameAction - PersistenceSelect::setName(const std::string_view key, const Selection &) + PersistenceStore::NameActionSelection + PersistenceSelect::setName(const std::string_view key, SelectionFactory && factory) { - return (key == name) ? NameAction::Push : NameAction::Ignore; + if (key == name) { + return {NameAction::Push, factory()}; + } + else { + return {NameAction::Ignore, nullptr}; + } } void @@ -61,10 +66,11 @@ namespace Persistence { PersistenceWrite::PersistenceWrite(const Writer & o, bool sh) : out {o}, shared {sh} { } - PersistenceStore::NameAction - PersistenceWrite::setName(const std::string_view key, const Selection & s) + PersistenceStore::NameActionSelection + PersistenceWrite::setName(const std::string_view key, SelectionFactory && factory) { - if (s.needsWrite()) { + auto s = factory(); + if (s->needsWrite()) { if (!first) { out.nextValue(); } @@ -72,9 +78,9 @@ namespace Persistence { first = false; } out.pushKey(key); - return NameAction::HandleAndContinue; + return {NameAction::HandleAndContinue, std::move(s)}; } - return NameAction::Ignore; + return {NameAction::Ignore, nullptr}; } void diff --git a/lib/persistence.h b/lib/persistence.h index 1cb3af0..d38d4b1 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -149,6 +149,7 @@ namespace Persistence { struct Persistable; struct PersistenceStore { + using SelectionFactory = std::function; PersistenceStore() = default; virtual ~PersistenceStore() = default; DEFAULT_MOVE_NO_COPY(PersistenceStore); @@ -156,12 +157,14 @@ namespace Persistence { template [[nodiscard]] inline bool persistType(const T * const, const std::type_info & ti); enum class NameAction { Push, HandleAndContinue, Ignore }; + using NameActionSelection = std::pair; template [[nodiscard]] inline bool persistValue(const std::string_view key, T & value) { - auto s = std::make_unique(value); - const auto act {setName(key, *s)}; + auto [act, s] = setName(key, [&value]() { + return std::make_unique(value); + }); if (act != NameAction::Ignore) { sel = std::move(s); if (act == NameAction::HandleAndContinue) { @@ -171,7 +174,7 @@ namespace Persistence { return (act != NameAction::Push); } - virtual NameAction setName(const std::string_view key, const Selection &) = 0; + [[nodiscard]] virtual NameActionSelection setName(const std::string_view key, SelectionFactory &&) = 0; virtual void selHandler() {}; virtual void setType(const std::string_view, const Persistable *) = 0; @@ -181,7 +184,7 @@ namespace Persistence { struct PersistenceSelect : public PersistenceStore { explicit PersistenceSelect(const std::string & n); - NameAction setName(const std::string_view key, const Selection &) override; + NameActionSelection setName(const std::string_view key, SelectionFactory &&) override; void setType(const std::string_view, const Persistable *) override; @@ -191,7 +194,7 @@ namespace Persistence { struct PersistenceWrite : public PersistenceStore { explicit PersistenceWrite(const Writer & o, bool sh); - NameAction setName(const std::string_view key, const Selection &) override; + NameActionSelection setName(const std::string_view key, SelectionFactory &&) override; void selHandler() override; -- cgit v1.2.3 From 3f2822035b3f1c67e0d6ce410bf43e84b0846037 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 23:22:04 +0000 Subject: Add persistence support for std::optional<>s --- lib/persistence.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/persistence.h b/lib/persistence.h index d38d4b1..cc2e4e5 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,10 @@ namespace Persistence { } }; + template struct SelectionT> : public SelectionT { + explicit SelectionT(std::optional & value) : SelectionT {value.emplace()} { } + }; + struct Persistable; struct PersistenceStore { using SelectionFactory = std::function; -- cgit v1.2.3 From 2508112e2853e1a6c012b19c7232aa09f98d3969 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 20 Mar 2023 23:30:21 +0000 Subject: Move smooth property into Style and make it optional This allows it to cascade down as faces are created and also be overridden as required --- assetFactory/faceController.cpp | 8 ++------ assetFactory/faceController.h | 1 - assetFactory/style.cpp | 6 +++++- assetFactory/style.h | 1 + 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index bafbee2..2367f8d 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -48,12 +48,10 @@ FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, faces.merge(std::move(newFaces)); } else { - mesh.property(mesh.smoothFaceProperty, faceHandle) = smooth; applyStyle(mesh, parents + this, faceHandle); } } for (const auto & [faceName, faceHandle] : controlledFaces) { - mesh.property(mesh.smoothFaceProperty, faceHandle) = smooth; applyStyle(mesh, parents + this, faceHandle); } } @@ -89,9 +87,8 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O auto & newFaceName = mesh.property(mesh.nameFaceProperty, newFace); newFaceName = getAdjacentFaceName(mesh, ofrange, newFace); newFaces.emplace(newFaceName, newFace); - mesh.property(mesh.smoothFaceProperty, newFace) = smooth; } - mesh.property(mesh.smoothFaceProperty, newFaces.emplace(faceName, mesh.add_face(vertices))->second) = smooth; + newFaces.emplace(faceName, mesh.add_face(vertices)); return newFaces; } @@ -99,7 +96,6 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O bool FaceController::persist(Persistence::PersistenceStore & store) { - return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && STORE_MEMBER(smooth) - && Mutation::persist(store) + return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && Mutation::persist(store) && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember); } diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 962a516..851292a 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -17,7 +17,6 @@ public: std::string id; std::string type; - bool smooth {false}; FaceControllers faceControllers; private: diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index d1d4adc..12346a6 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -19,6 +19,9 @@ void Style::applyStyle( ModelFactoryMesh & mesh, const ModelFactoryMesh::FaceHandle & face, EffectiveColour effectiveColour) const { + if (smooth.has_value()) { + mesh.property(mesh.smoothFaceProperty, face) = smooth.value(); + } if (texture.empty()) { if (effectiveColour.has_value()) { mesh.set_color(face, effectiveColour->get()); @@ -60,5 +63,6 @@ Style::persist(Persistence::PersistenceStore & store) } }; - return STORE_HELPER(colour, ColourParser) && STORE_MEMBER(texture) && STORE_MEMBER(textureRotation); + return STORE_HELPER(colour, ColourParser) && STORE_MEMBER(smooth) && STORE_MEMBER(texture) + && STORE_MEMBER(textureRotation); } diff --git a/assetFactory/style.h b/assetFactory/style.h index f3bc707..d931f98 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -31,6 +31,7 @@ public: static EffectiveColour getColour(const StyleStack & parents); ColourAlpha colour {}; + std::optional smooth; std::string texture; std::string textureRotation; // Multiples of 90deg, no int/enum support -- cgit v1.2.3 From 37e39b6c00834f4711576c7f77cb0b7b5d558956 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 21 Mar 2023 00:18:25 +0000 Subject: Apply styles in first loop over controlled faces --- assetFactory/faceController.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 2367f8d..25bf833 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -47,11 +47,6 @@ FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, } faces.merge(std::move(newFaces)); } - else { - applyStyle(mesh, parents + this, faceHandle); - } - } - for (const auto & [faceName, faceHandle] : controlledFaces) { applyStyle(mesh, parents + this, faceHandle); } } -- cgit v1.2.3 From b244b896503065198bc736bbff52d6ff79c24986 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Apr 2023 23:47:26 +0100 Subject: Add helper for iterating over a range defined by a pair of iterators --- lib/collections.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/collections.hpp b/lib/collections.hpp index 2b83500..59cec6f 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -117,3 +117,18 @@ materializeRange(const std::pair & in) { return Rtn(in.first, in.second); } + +template struct pair_range { + constexpr auto & + begin() const noexcept + { + return pair.first; + } + constexpr auto & + end() const noexcept + { + return pair.second; + } + const std::pair & pair; +}; +template pair_range(std::pair) -> pair_range; -- cgit v1.2.3 From b76ad768ceddc2777044d0f7128d292332a75353 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Apr 2023 23:49:36 +0100 Subject: Add timeouts to asset factory tests Yes, I made some infinite loops a few times --- test/test-assetFactory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index d71fa9b..54168aa 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -68,7 +68,7 @@ private: }; BOOST_FIXTURE_TEST_SUITE(m, FactoryFixture); -BOOST_AUTO_TEST_CASE(brush47xml) +BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) { auto mf = AssetFactory::loadXML(RESDIR "/brush47.xml"); BOOST_REQUIRE(mf); @@ -118,7 +118,7 @@ BOOST_DATA_TEST_CASE(normalizeColourName, BOOST_CHECK_EQUAL(in, exp); } -BOOST_AUTO_TEST_CASE(parseX11RGB) +BOOST_AUTO_TEST_CASE(parseX11RGB, *boost::unit_test::timeout(5)) { const auto parsedColours = AssetFactory::parseX11RGB(FIXTURESDIR "rgb.txt"); BOOST_REQUIRE_EQUAL(parsedColours.size(), 20); @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(parseX11RGB) BOOST_CHECK_CLOSE_VEC(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(0.79, 0.88, 1)); } -BOOST_AUTO_TEST_CASE(texturePacker) +BOOST_AUTO_TEST_CASE(texturePacker, *boost::unit_test::timeout(5)) { std::vector input { {10, 10}, -- cgit v1.2.3 From 2bcbb86db4061e32005adea8806e4ac552691bcf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Apr 2023 23:55:09 +0100 Subject: Extend face controller to support splitting a face along a plane Individual parts of the splits faces can then be styled separately --- assetFactory/faceController.cpp | 83 +++++++++++++++++++++++++++++++++++++---- assetFactory/faceController.h | 18 +++++++++ res/brush47.xml | 6 ++- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 25bf833..28812d8 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -2,6 +2,7 @@ #include "collections.hpp" #include "maths.h" #include "modelFactoryMesh.h" +#include void FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & names, @@ -30,24 +31,29 @@ void FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const { - const auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (controlledFaces.empty()) { - throw std::runtime_error("Named face(s) do not exist: " + name); - } + auto controlledFaces {materializeRange(faces.equal_range(name))}; - if (!type.empty()) { + if (!type.empty() || !splits.empty()) { faces.erase(name); } - for (const auto & [faceName, faceHandle] : controlledFaces) { + for (auto & [faceName, faceHandle] : controlledFaces) { + Shape::CreatedFaces newFaces; + for (const auto & [newFaceSuffix, splitDef] : splits) { + newFaces.merge(split(mesh, name + newFaceSuffix, faceHandle, *splitDef)); + } if (type == "extrude") { - auto newFaces = extrude(mesh, faceName, faceHandle); + newFaces.merge(extrude(mesh, name, faceHandle)); + } + if (!newFaces.empty()) { applyStyle(mesh, parents + this, newFaces); for (const auto & [subFaceName, faceController] : faceControllers) { faceController->apply(mesh, parents + this, subFaceName, newFaces); } faces.merge(std::move(newFaces)); } - applyStyle(mesh, parents + this, faceHandle); + else { + applyStyle(mesh, parents + this, faceHandle); + } } } @@ -88,9 +94,70 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O return newFaces; } +enum class PlaneRelation { Above, Below, On }; +Shape::CreatedFaces +FaceController::split( + ModelFactoryMesh & mesh, const std::string & name, OpenMesh::FaceHandle & fh, const Split & split) const +{ + // Map face vertex handles to their relationship to the split plane + const auto vertices = materializeRange(mesh.fv_range(fh)); + auto vertexRelations = vertices * [&split, &mesh](OpenMesh::VertexHandle vh) { + const auto d = glm::dot(split.normal, mesh.point(vh) - split.origin); + return std::make_pair(vh, d < 0.f ? PlaneRelation::Below : d > 0.f ? PlaneRelation::Above : PlaneRelation::On); + }; + // Insert new vertices where half edges intersect the split plane + for (size_t curIdx = 0; curIdx < vertexRelations.size(); ++curIdx) { + const size_t nextIdx = (curIdx + 1) % vertexRelations.size(); + const auto ¤t = vertexRelations[curIdx], next = vertexRelations[nextIdx]; + if ((current.second == PlaneRelation::Above && next.second == PlaneRelation::Below) + || (current.second == PlaneRelation::Below && next.second == PlaneRelation::Above)) { + const auto origin = mesh.point(current.first), dir = glm::normalize(mesh.point(next.first) - origin); + + float dist {}; + glm::intersectRayPlane(origin, dir, split.origin, split.normal, dist); + const auto newv = mesh.add_vertex(origin + (dir * dist)); + auto where = vertexRelations.begin(); + ++curIdx; + std::advance(where, curIdx); + vertexRelations.emplace(where, newv, PlaneRelation::On); + } + } + // Create vertex vectors + std::array, 2> out; + auto filterVertices = [&vertexRelations](auto & out, auto notRelation) { + for (const auto & vhr : vertexRelations) { + if (vhr.second != notRelation) { + out.emplace_back(vhr.first); + } + } + }; + filterVertices(out.front(), PlaneRelation::Above); + filterVertices(out.back(), PlaneRelation::Below); + + if (out.back().size() > 2) { + Shape::CreatedFaces newFaces; + const auto oldName = mesh.property(mesh.nameFaceProperty, fh); + mesh.delete_face(fh); + const auto newf1 = newFaces.insert(mesh.add_namedFace(oldName, out.front()))->second; + const auto newf2 = newFaces.insert(mesh.add_namedFace(name, out.back()))->second; + mesh.copy_property(mesh.smoothFaceProperty, fh, newf1); + mesh.copy_property(mesh.smoothFaceProperty, fh, newf2); + fh = newf1; + return newFaces; + } + return {}; +} + bool FaceController::persist(Persistence::PersistenceStore & store) { return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && Mutation::persist(store) + && STORE_NAME_HELPER("split", splits, Persistence::MapByMember) && STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember); } + +bool +FaceController::Split::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(origin) && STORE_MEMBER(normal); +} diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 851292a..91860f4 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -10,7 +10,22 @@ class FaceController : public Mutation, public Style, public Persistence::Persistable { public: + class Split : public Persistable { + public: + std::string id; + glm::vec3 origin, normal; + + private: + friend Persistence::SelectionPtrBase>; + bool persist(Persistence::PersistenceStore & store) override; + std::string + getId() const override + { + return {}; + }; + }; using FaceControllers = std::map>; + using Splits = std::map>; void apply(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & names, Shape::CreatedFaces & faces) const; @@ -18,6 +33,7 @@ public: std::string id; std::string type; FaceControllers faceControllers; + Splits splits; private: friend Persistence::SelectionPtrBase>; @@ -33,4 +49,6 @@ private: static std::string getAdjacentFaceName(const ModelFactoryMesh & mesh, const std::span ofrange, OpenMesh::FaceHandle nf); Shape::CreatedFaces extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle) const; + Shape::CreatedFaces split( + ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle &, const Split &) const; }; diff --git a/res/brush47.xml b/res/brush47.xml index f74cb29..20c43c8 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -70,7 +70,11 @@ - + + + + + -- cgit v1.2.3 From 7f9e28e4e91ac757680fef56e771122cde14c9db Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Apr 2023 11:42:43 +0100 Subject: Remember the name of the adjacent face of each halfedge of each named face Used when extruding to remember what the new face name will be, even after the adjacent face has been removed/replaced/split/etc --- assetFactory/modelFactoryMesh.cpp | 16 ++++++++++++++++ assetFactory/modelFactoryMesh.h | 8 ++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp index 806ac3b..e640502 100644 --- a/assetFactory/modelFactoryMesh.cpp +++ b/assetFactory/modelFactoryMesh.cpp @@ -4,4 +4,20 @@ ModelFactoryMesh::ModelFactoryMesh() { add_property(smoothFaceProperty); add_property(nameFaceProperty); + add_property(nameAdjFaceProperty); +} + +void +ModelFactoryMesh::configNamedFace(const std::string & name, OpenMesh::FaceHandle handle) +{ + property(nameFaceProperty, handle) = name; + const auto halfEdges = fh_range(handle); + for (const auto & he : halfEdges) { + if (auto ofh = opposite_face_handle(he); ofh.is_valid()) { + property(nameAdjFaceProperty, he) = property(nameFaceProperty, ofh); + } + if (auto oheh = opposite_halfedge_handle(he); oheh.is_valid()) { + property(nameAdjFaceProperty, oheh) = name; + } + } } diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index a862dc1..8ac2edd 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -48,13 +48,17 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT smoothFaceProperty; OpenMesh::FPropHandleT nameFaceProperty; + OpenMesh::HPropHandleT nameAdjFaceProperty; template std::pair add_namedFace(std::string name, Vs &&... vs) { const auto handle = add_face(std::forward(vs)...); - property(nameFaceProperty, handle) = name; - return std::make_pair(name, handle); + configNamedFace(name, handle); + return {std::move(name), handle}; } + +private: + void configNamedFace(const std::string & name, OpenMesh::FaceHandle); }; -- cgit v1.2.3 From 5ffac39b0edfe04c081191b399cadcd69ad3ccd2 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Apr 2023 11:46:51 +0100 Subject: Generate extrusion face names from halfedge adjacent face names --- assetFactory/faceController.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 28812d8..92cc8d2 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -69,7 +69,6 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O }); const auto vertexCount = points.size(); const auto centre = mesh.calc_face_centroid(faceHandle); - Shape::CreatedFaces newFaces; // mutate points std::for_each(points.begin(), points.end(), [mutation = getMatrix(), ¢re](auto && p) { p = centre + ((p - centre) % mutation); @@ -79,17 +78,22 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O std::transform(points.begin(), points.end(), std::back_inserter(vertices), [&mesh](auto && p) { return mesh.add_vertex(p); }); + // get new faces names + std::vector faceNames; + for (size_t idx {}; idx < vertexCount; ++idx) { + const auto next = (idx + 1) % vertexCount; + const auto existingEdge = mesh.find_halfedge(baseVertices[idx], baseVertices[next]); + faceNames.push_back(mesh.property(mesh.nameAdjFaceProperty, existingEdge)); + } // create new faces - const auto ofrange = materializeRange(mesh.ff_range(faceHandle)); mesh.delete_face(faceHandle); + Shape::CreatedFaces newFaces; for (size_t idx {}; idx < vertexCount; ++idx) { const auto next = (idx + 1) % vertexCount; - const auto newFace = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]}); - auto & newFaceName = mesh.property(mesh.nameFaceProperty, newFace); - newFaceName = getAdjacentFaceName(mesh, ofrange, newFace); - newFaces.emplace(newFaceName, newFace); + newFaces.emplace(mesh.add_namedFace( + faceNames[idx], baseVertices[idx], baseVertices[next], vertices[next], vertices[idx])); } - newFaces.emplace(faceName, mesh.add_face(vertices)); + newFaces.emplace(mesh.add_namedFace(faceName, vertices)); return newFaces; } -- cgit v1.2.3 From 144f9b57295f7fee9306ee1a4480c165a834e6ca Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Apr 2023 11:48:17 +0100 Subject: Remove no longer required getAdjacentFaceName --- assetFactory/faceController.cpp | 12 ------------ assetFactory/faceController.h | 2 -- 2 files changed, 14 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 92cc8d2..7e4b0e1 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -15,18 +15,6 @@ FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const }); } -std::string -FaceController::getAdjacentFaceName( - const ModelFactoryMesh & mesh, const std::span ofrange, OpenMesh::FaceHandle nf) -{ - 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 {}; -}; - void FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 91860f4..8d30dc6 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -46,8 +46,6 @@ private: void applySingle(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name, Shape::CreatedFaces & faces) const; - static std::string getAdjacentFaceName(const ModelFactoryMesh & mesh, - const std::span ofrange, OpenMesh::FaceHandle nf); Shape::CreatedFaces extrude(ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle) const; Shape::CreatedFaces split( ModelFactoryMesh & mesh, const std::string & faceName, OpenMesh::FaceHandle &, const Split &) const; -- cgit v1.2.3 From 26219bd0e3a1a0da74282509357f0ffcbcbafd7e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Apr 2023 11:50:40 +0100 Subject: Update Brush47 with yellow cab parts and window texture Note: side window distorted still. --- res/brush47.xml | 13 ++++++++++++- res/rail/cabWindowFront.png | Bin 0 -> 630 bytes 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 res/rail/cabWindowFront.png diff --git a/res/brush47.xml b/res/brush47.xml index 20c43c8..31518c8 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -60,14 +60,25 @@ + + + + + + - + + + + + + diff --git a/res/rail/cabWindowFront.png b/res/rail/cabWindowFront.png new file mode 100644 index 0000000..3803f21 Binary files /dev/null and b/res/rail/cabWindowFront.png differ -- cgit v1.2.3 From fe5a06851823ac6da841a513cf59140c63ff69f3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Apr 2023 14:08:25 +0100 Subject: Simplify extruding --- assetFactory/faceController.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 7e4b0e1..7992fba 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -50,23 +50,13 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O { // get points const auto baseVertices {materializeRange(mesh.fv_range(faceHandle))}; - 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 = mesh.calc_face_centroid(faceHandle); - // mutate points - std::for_each(points.begin(), points.end(), [mutation = getMatrix(), ¢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); - }); + const auto vertices + = baseVertices * [&mesh, mutation = getMatrix(), centre = mesh.calc_face_centroid(faceHandle)](auto && v) { + return mesh.add_vertex(centre + ((mesh.point(v) - centre) % mutation)); + }; // get new faces names + const auto vertexCount = baseVertices.size(); std::vector faceNames; for (size_t idx {}; idx < vertexCount; ++idx) { const auto next = (idx + 1) % vertexCount; -- cgit v1.2.3 From b222c21715384efc2eaa53d3ba295fb34da5b599 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Apr 2023 12:24:55 +0100 Subject: Start to factor out geometric place from face controller split --- assetFactory/faceController.cpp | 16 ++++++++-------- assetFactory/faceController.h | 4 ++-- lib/geometricPlane.cpp | 9 +++++++++ lib/geometricPlane.h | 12 ++++++++++++ 4 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 lib/geometricPlane.cpp create mode 100644 lib/geometricPlane.h diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 7992fba..37aaa4c 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -76,7 +76,6 @@ FaceController::extrude(ModelFactoryMesh & mesh, const std::string & faceName, O return newFaces; } -enum class PlaneRelation { Above, Below, On }; Shape::CreatedFaces FaceController::split( ModelFactoryMesh & mesh, const std::string & name, OpenMesh::FaceHandle & fh, const Split & split) const @@ -84,15 +83,16 @@ FaceController::split( // Map face vertex handles to their relationship to the split plane const auto vertices = materializeRange(mesh.fv_range(fh)); auto vertexRelations = vertices * [&split, &mesh](OpenMesh::VertexHandle vh) { - const auto d = glm::dot(split.normal, mesh.point(vh) - split.origin); - return std::make_pair(vh, d < 0.f ? PlaneRelation::Below : d > 0.f ? PlaneRelation::Above : PlaneRelation::On); + return std::make_pair(vh, split.getRelation(mesh.point(vh))); }; // Insert new vertices where half edges intersect the split plane for (size_t curIdx = 0; curIdx < vertexRelations.size(); ++curIdx) { const size_t nextIdx = (curIdx + 1) % vertexRelations.size(); const auto ¤t = vertexRelations[curIdx], next = vertexRelations[nextIdx]; - if ((current.second == PlaneRelation::Above && next.second == PlaneRelation::Below) - || (current.second == PlaneRelation::Below && next.second == PlaneRelation::Above)) { + if ((current.second == GeometricPlane::PlaneRelation::Above + && next.second == GeometricPlane::PlaneRelation::Below) + || (current.second == GeometricPlane::PlaneRelation::Below + && next.second == GeometricPlane::PlaneRelation::Above)) { const auto origin = mesh.point(current.first), dir = glm::normalize(mesh.point(next.first) - origin); float dist {}; @@ -101,7 +101,7 @@ FaceController::split( auto where = vertexRelations.begin(); ++curIdx; std::advance(where, curIdx); - vertexRelations.emplace(where, newv, PlaneRelation::On); + vertexRelations.emplace(where, newv, GeometricPlane::PlaneRelation::On); } } // Create vertex vectors @@ -113,8 +113,8 @@ FaceController::split( } } }; - filterVertices(out.front(), PlaneRelation::Above); - filterVertices(out.back(), PlaneRelation::Below); + filterVertices(out.front(), GeometricPlane::PlaneRelation::Above); + filterVertices(out.back(), GeometricPlane::PlaneRelation::Below); if (out.back().size() > 2) { Shape::CreatedFaces newFaces; diff --git a/assetFactory/faceController.h b/assetFactory/faceController.h index 8d30dc6..0376241 100644 --- a/assetFactory/faceController.h +++ b/assetFactory/faceController.h @@ -1,5 +1,6 @@ #pragma once +#include "geometricPlane.h" #include "modelFactoryMesh_fwd.h" #include "mutation.h" #include "persistence.h" @@ -10,10 +11,9 @@ class FaceController : public Mutation, public Style, public Persistence::Persistable { public: - class Split : public Persistable { + class Split : public Persistable, public GeometricPlane { public: std::string id; - glm::vec3 origin, normal; private: friend Persistence::SelectionPtrBase>; diff --git a/lib/geometricPlane.cpp b/lib/geometricPlane.cpp new file mode 100644 index 0000000..cb52997 --- /dev/null +++ b/lib/geometricPlane.cpp @@ -0,0 +1,9 @@ +#include "geometricPlane.h" +#include + +GeometricPlane::PlaneRelation +GeometricPlane::getRelation(glm::vec3 p) const +{ + const auto d = glm::dot(normal, p - origin); + return d < 0.f ? PlaneRelation::Below : d > 0.f ? PlaneRelation::Above : PlaneRelation::On; +} diff --git a/lib/geometricPlane.h b/lib/geometricPlane.h new file mode 100644 index 0000000..96816f2 --- /dev/null +++ b/lib/geometricPlane.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class GeometricPlane { +public: + enum class PlaneRelation { Above, Below, On }; + + glm::vec3 origin, normal; + + PlaneRelation getRelation(glm::vec3 point) const; +}; -- cgit v1.2.3 From d8f61abae79a3154d5e6dda98713295d1b850e95 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Apr 2023 12:42:09 +0100 Subject: Add helper to create a Ray from two points --- lib/ray.cpp | 6 ++++++ lib/ray.hpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/ray.cpp b/lib/ray.cpp index acbb807..f9e3311 100644 --- a/lib/ray.cpp +++ b/lib/ray.cpp @@ -1,6 +1,12 @@ #include "ray.hpp" #include +Ray +Ray::fromPoints(glm::vec3 start, glm::vec3 p) +{ + return {start, glm::normalize(p - start)}; +} + float Ray::distanceToLine(const glm::vec3 & p1, const glm::vec3 & e1) const { diff --git a/lib/ray.hpp b/lib/ray.hpp index 8bef1c8..9bf47af 100644 --- a/lib/ray.hpp +++ b/lib/ray.hpp @@ -7,6 +7,8 @@ class Ray { public: Ray(glm::vec3 start, glm::vec3 direction) : start {start}, direction {direction} { } + static Ray fromPoints(glm::vec3, glm::vec3); + glm::vec3 start; glm::vec3 direction; -- cgit v1.2.3 From 560bfce98146895cfd8429150792d49a18e24d98 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Apr 2023 13:15:46 +0100 Subject: Add lots of split required stuff to geometric plane --- lib/geometricPlane.cpp | 19 +++++++++++++++++++ lib/geometricPlane.h | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/geometricPlane.cpp b/lib/geometricPlane.cpp index cb52997..71216c1 100644 --- a/lib/geometricPlane.cpp +++ b/lib/geometricPlane.cpp @@ -1,5 +1,7 @@ #include "geometricPlane.h" +#include "ray.hpp" #include +#include GeometricPlane::PlaneRelation GeometricPlane::getRelation(glm::vec3 p) const @@ -7,3 +9,20 @@ GeometricPlane::getRelation(glm::vec3 p) const const auto d = glm::dot(normal, p - origin); return d < 0.f ? PlaneRelation::Below : d > 0.f ? PlaneRelation::Above : PlaneRelation::On; } + +bool +GeometricPlane::isIntersect(PlaneRelation a, PlaneRelation b) +{ + return ((a == PlaneRelation::Above && b == PlaneRelation::Below) + || (a == PlaneRelation::Below && b == PlaneRelation::Above)); +} + +std::optional +GeometricPlane::getRayIntersectPosition(const Ray & ray) const +{ + float dist {}; + if (!glm::intersectRayPlane(ray.start, ray.direction, origin, normal, dist)) { + return {}; + } + return DistAndPosition {dist, ray.start + (ray.direction * dist)}; +} diff --git a/lib/geometricPlane.h b/lib/geometricPlane.h index 96816f2..dc8df50 100644 --- a/lib/geometricPlane.h +++ b/lib/geometricPlane.h @@ -1,12 +1,22 @@ #pragma once #include +#include + +class Ray; class GeometricPlane { public: + struct DistAndPosition { + float dist; + glm::vec3 position; + }; enum class PlaneRelation { Above, Below, On }; glm::vec3 origin, normal; PlaneRelation getRelation(glm::vec3 point) const; + std::optional getRayIntersectPosition(const Ray &) const; + + static bool isIntersect(PlaneRelation a, PlaneRelation b); }; -- cgit v1.2.3 From a74ee2960bd8c930b91dbc897c27d134f5f2c2f0 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 9 Apr 2023 15:51:47 +0100 Subject: Move remaining split/plane functions to use library --- assetFactory/faceController.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp index 37aaa4c..b305f1c 100644 --- a/assetFactory/faceController.cpp +++ b/assetFactory/faceController.cpp @@ -2,7 +2,7 @@ #include "collections.hpp" #include "maths.h" #include "modelFactoryMesh.h" -#include +#include "ray.hpp" void FaceController::apply(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & names, @@ -89,15 +89,11 @@ FaceController::split( for (size_t curIdx = 0; curIdx < vertexRelations.size(); ++curIdx) { const size_t nextIdx = (curIdx + 1) % vertexRelations.size(); const auto ¤t = vertexRelations[curIdx], next = vertexRelations[nextIdx]; - if ((current.second == GeometricPlane::PlaneRelation::Above - && next.second == GeometricPlane::PlaneRelation::Below) - || (current.second == GeometricPlane::PlaneRelation::Below - && next.second == GeometricPlane::PlaneRelation::Above)) { - const auto origin = mesh.point(current.first), dir = glm::normalize(mesh.point(next.first) - origin); - - float dist {}; - glm::intersectRayPlane(origin, dir, split.origin, split.normal, dist); - const auto newv = mesh.add_vertex(origin + (dir * dist)); + if (GeometricPlane::isIntersect(current.second, next.second)) { + const auto ray = Ray::fromPoints(mesh.point(current.first), mesh.point(next.first)); + const auto intersect = split.getRayIntersectPosition(ray); + assert(intersect); + const auto newv = mesh.add_vertex(intersect->position); auto where = vertexRelations.begin(); ++curIdx; std::advance(where, curIdx); -- cgit v1.2.3