summaryrefslogtreecommitdiff
path: root/assetFactory
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-04-09 23:29:00 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2023-04-09 23:29:00 +0100
commitb6b3944e9507f337f77a72ea44b5c0ccba7a2c01 (patch)
treea487df54853522f973ed987006e787117804b9eb /assetFactory
parentSwap messy glmvec wrapper for OpenMesh Point/Normal with real glm::vec and a ... (diff)
parentMove remaining split/plane functions to use library (diff)
downloadilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.tar.bz2
ilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.tar.xz
ilt-b6b3944e9507f337f77a72ea44b5c0ccba7a2c01.zip
Merge branch 'model-factory-textures'
Diffstat (limited to 'assetFactory')
-rw-r--r--assetFactory/asset.cpp10
-rw-r--r--assetFactory/asset.h6
-rw-r--r--assetFactory/assetFactory.cpp64
-rw-r--r--assetFactory/assetFactory.h13
-rw-r--r--assetFactory/cuboid.cpp4
-rw-r--r--assetFactory/faceController.cpp186
-rw-r--r--assetFactory/faceController.h25
-rw-r--r--assetFactory/factoryMesh.cpp40
-rw-r--r--assetFactory/modelFactoryMesh.cpp16
-rw-r--r--assetFactory/modelFactoryMesh.h11
-rw-r--r--assetFactory/style.cpp52
-rw-r--r--assetFactory/style.h7
-rw-r--r--assetFactory/textureFragment.cpp7
-rw-r--r--assetFactory/textureFragment.h14
-rw-r--r--assetFactory/texturePacker.cpp80
-rw-r--r--assetFactory/texturePacker.h41
16 files changed, 473 insertions, 103 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<const AssetFactory>("assetFactory")) {
+ return mf->getTexture();
+ }
+ return nullptr;
+}
+
Asset::MeshConstruct::MeshConstruct(Mesh::Ptr & m) :
Persistence::SelectionPtrBase<FactoryMesh::Ptr> {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 <stdTypeDefs.hpp>
+class Texture;
+
class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> {
public:
+ using TexturePtr = std::shared_ptr<Texture>;
+
std::string id;
std::string name;
protected:
+ TexturePtr getTexture() const;
+
struct MeshConstruct : public Persistence::SelectionPtrBase<FactoryMesh::Ptr> {
using Persistence::SelectionPtrBase<FactoryMesh::Ptr>::setValue;
diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp
index f5fc2b3..ed1af58 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 <filesystem.h>
+#include "texturePacker.h"
+#include <stb/stb_image.h>
AssetFactory::AssetFactory() :
shapes {
@@ -75,11 +80,68 @@ 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<std::unique_ptr<Image>> images;
+ std::transform(
+ textureFragments.begin(), textureFragments.end(), std::back_inserter(images), [](const auto & tf) {
+ return std::make_unique<Image>(Resource::mapPath(tf.second->path), STBI_rgb_alpha);
+ });
+ // * layout images
+ std::vector<TexturePacker::Image> 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
+ texture = std::make_shared<Texture>(outSize.x, outSize.y, TextureOptions {.wrap = GL_CLAMP_TO_EDGE});
+ std::transform(textureFragments.begin(), textureFragments.end(),
+ std::inserter(textureFragmentPositions, textureFragmentPositions.end()),
+ [position = layout.begin(), image = images.begin(), size = imageSizes.begin(),
+ outSize = glm::vec2 {outSize}](const auto & tf) mutable {
+ const auto positionFraction = glm::vec4 {*position, *position + *size} / outSize.xyxy();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(position->x), static_cast<GLint>(position->y),
+ static_cast<GLint>(size->x), static_cast<GLint>(size->y), GL_RGBA, GL_UNSIGNED_BYTE,
+ image->get()->data.data());
+ position++;
+ image++;
+ size++;
+ return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction};
+ });
+ }
+}
+
bool
AssetFactory::persist(Persistence::PersistenceStore & store)
{
using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>;
using MapAssets = Persistence::MapByMember<Assets>;
+ using MapTextureFragments = Persistence::MapByMember<TextureFragments>;
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..52692c4 100644
--- a/assetFactory/assetFactory.h
+++ b/assetFactory/assetFactory.h
@@ -3,23 +3,31 @@
#include "asset.h"
#include "persistence.h"
#include "shape.h"
+#include "textureFragment.h"
#include <filesystem>
+class Texture;
+
class AssetFactory : public Persistence::Persistable {
public:
using Shapes = std::map<std::string, Shape::Ptr, std::less<>>;
using Assets = std::map<std::string, Asset::Ptr, std::less<>>;
+ using TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>;
using Colour = glm::vec3;
using ColourAlpha = glm::vec4;
using Colours = std::map<std::string, Colour, std::less<>>;
+ using TextureFragmentCoords = std::array<glm::vec2, 4>;
AssetFactory();
[[nodiscard]] static std::shared_ptr<AssetFactory> 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;
Colours colours;
+ TextureFragments textureFragments;
static Colours parseX11RGB(const char * rgbtxtpath);
static void normalizeColourName(std::string &);
@@ -27,4 +35,9 @@ public:
private:
friend Persistence::SelectionPtrBase<std::shared_ptr<AssetFactory>>;
bool persist(Persistence::PersistenceStore & store) override;
+
+ void createTexutre() const;
+
+ mutable Asset::TexturePtr texture;
+ mutable std::map<std::string_view, glm::vec4, std::less<>> textureFragmentPositions;
};
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]),
};
diff --git a/assetFactory/faceController.cpp b/assetFactory/faceController.cpp
index 1ec1467..b305f1c 100644
--- a/assetFactory/faceController.cpp
+++ b/assetFactory/faceController.cpp
@@ -2,90 +2,140 @@
#include "collections.hpp"
#include "maths.h"
#include "modelFactoryMesh.h"
+#include "ray.hpp"
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
{
- 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 {};
- };
+ std::stringstream nameStream {names};
+ std::for_each(std::istream_iterator<std::string>(nameStream), std::istream_iterator<std::string> {},
+ [&](const auto & name) {
+ applySingle(mesh, parents, name, faces);
+ });
+}
- const auto controlledFaces {materializeRange(faces.equal_range(name))};
- if (controlledFaces.empty()) {
- throw std::runtime_error("Named face(s) do not exist: " + name);
- }
+void
+FaceController::applySingle(ModelFactoryMesh & mesh, const StyleStack & parents, const std::string & name,
+ Shape::CreatedFaces & faces) const
+{
+ auto controlledFaces {materializeRange(faces.equal_range(name))};
- if (!type.empty()) {
- const auto mutation = getMatrix();
+ if (!type.empty() || !splits.empty()) {
faces.erase(name);
- for (const auto & cf : controlledFaces) {
- // get points
- const auto baseVertices {materializeRange(mesh.fv_range(cf.second))};
- auto points = std::accumulate(baseVertices.begin(), baseVertices.end(), std::vector<glm::vec3> {},
- [&mesh](auto && out, auto && v) {
- out.push_back(mesh.point(v));
- return std::move(out);
- });
- const auto vertexCount = points.size();
- const auto centre
- = std::accumulate(points.begin(), points.end(), glm::vec3 {}) / static_cast<float>(vertexCount);
- if (type == "extrude") {
- Shape::CreatedFaces newFaces;
- // mutate points
- std::for_each(points.begin(), points.end(), [&mutation, &centre](auto && p) {
- p = centre + ((p - centre) % mutation);
- });
- // create new vertices
- std::vector<OpenMesh::VertexHandle> vertices;
- std::transform(points.begin(), points.end(), std::back_inserter(vertices), [&mesh](auto && p) {
- return mesh.add_vertex(p);
- });
- // create new faces
- const auto ofrange = materializeRange(mesh.ff_range(cf.second));
- mesh.delete_face(cf.second);
- for (size_t idx {}; idx < vertexCount; ++idx) {
- const auto next = (idx + 1) % vertexCount;
- const auto newFace
- = mesh.add_face({baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]});
- auto & name = mesh.property(mesh.nameFaceProperty, newFace);
- name = getAdjacentFaceName(ofrange, newFace);
- newFaces.emplace(name, newFace);
- }
- newFaces.emplace(name, mesh.add_face(vertices));
- if (smooth) {
- for (const auto & [name, face] : newFaces) {
- mesh.property(mesh.smoothFaceProperty, face) = true;
- }
- }
- applyStyle(mesh, parents + this, newFaces);
- for (const auto & [name, faceController] : faceControllers) {
- faceController->apply(mesh, parents + this, name, newFaces);
- }
- faces.merge(std::move(newFaces));
- }
- else {
- mesh.property(mesh.smoothFaceProperty, cf.second) = smooth;
- applyStyle(mesh, parents + this, cf.second);
+ }
+ 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") {
+ 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));
+ }
+ else {
+ 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))};
+ // create new vertices
+ 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<std::string> 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
+ mesh.delete_face(faceHandle);
+ Shape::CreatedFaces newFaces;
+ for (size_t idx {}; idx < vertexCount; ++idx) {
+ const auto next = (idx + 1) % vertexCount;
+ newFaces.emplace(mesh.add_namedFace(
+ faceNames[idx], baseVertices[idx], baseVertices[next], vertices[next], vertices[idx]));
+ }
+ newFaces.emplace(mesh.add_namedFace(faceName, vertices));
+
+ return newFaces;
+}
+
+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) {
+ 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 &current = vertexRelations[curIdx], next = vertexRelations[nextIdx];
+ 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);
+ vertexRelations.emplace(where, newv, GeometricPlane::PlaneRelation::On);
}
}
- else {
- for (const auto & cf : controlledFaces) {
- applyStyle(mesh, parents + this, cf.second);
+ // Create vertex vectors
+ std::array<std::vector<OpenMesh::VertexHandle>, 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(), GeometricPlane::PlaneRelation::Above);
+ filterVertices(out.back(), GeometricPlane::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) && STORE_MEMBER(smooth)
- && Mutation::persist(store)
+ return STORE_TYPE && STORE_MEMBER(id) && Style::persist(store) && STORE_MEMBER(type) && Mutation::persist(store)
+ && STORE_NAME_HELPER("split", splits, Persistence::MapByMember<Splits>)
&& STORE_NAME_HELPER("face", faceControllers, Persistence::MapByMember<FaceControllers>);
}
+
+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 10a226a..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,15 +11,29 @@
class FaceController : public Mutation, public Style, public Persistence::Persistable {
public:
+ class Split : public Persistable, public GeometricPlane {
+ public:
+ std::string id;
+
+ private:
+ friend Persistence::SelectionPtrBase<std::unique_ptr<Split>>;
+ bool persist(Persistence::PersistenceStore & store) override;
+ std::string
+ getId() const override
+ {
+ return {};
+ };
+ };
using FaceControllers = std::map<std::string, std::unique_ptr<FaceController>>;
+ using Splits = std::map<std::string, std::unique_ptr<Split>>;
- 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;
std::string type;
- bool smooth {false};
FaceControllers faceControllers;
+ Splits splits;
private:
friend Persistence::SelectionPtrBase<std::unique_ptr<FaceController>>;
@@ -28,4 +43,10 @@ private:
{
return {};
};
+
+ void applySingle(ModelFactoryMesh & mesh, const Style::StyleStack & parents, const std::string & name,
+ Shape::CreatedFaces & faces) const;
+ 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/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp
index 1665b90..8869efd 100644
--- a/assetFactory/factoryMesh.cpp
+++ b/assetFactory/factoryMesh.cpp
@@ -2,34 +2,48 @@
#include "collections.hpp"
#include "gfx/models/vertex.hpp"
#include "modelFactoryMesh.h"
-#include <glm/ext/matrix_transform.hpp>
Mesh::Ptr
FactoryMesh::createMesh() const
{
- constexpr glm::vec2 NullUV {};
-
ModelFactoryMesh mesh;
for (const auto & use : uses) {
use->createMesh(mesh, 1);
}
- mesh.garbage_collection();
- mesh.triangulate();
mesh.update_face_normals();
mesh.update_vertex_normals();
std::vector<Vertex> vertices;
+ std::vector<unsigned int> 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)) {
- vertices.emplace_back(mesh.point(vertex), NullUV,
- smooth ? mesh.property(mesh.vertex_normals_pph(), vertex)
- : mesh.property(mesh.face_normals_pph(), face),
- colour);
+ const auto & smooth = mesh.property(mesh.smoothFaceProperty, face);
+ const auto & colour = mesh.color(face);
+
+ std::vector<unsigned int> faceIndices;
+ for (const auto & heh : mesh.fh_range(face)) {
+ const auto & vertex = mesh.to_vertex_handle(heh);
+ 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<unsigned int>(std::distance(existingItr, vertices.rend()) - 1));
+ }
+ else {
+ faceIndices.push_back(static_cast<unsigned int>(vertices.size()));
+ vertices.emplace_back(outVertex);
+ }
+ }
+
+ 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<Mesh>(vertices, vectorOfN(vertices.size()));
+ return std::make_shared<Mesh>(vertices, indices);
}
bool
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 149ff1a..8ac2edd 100644
--- a/assetFactory/modelFactoryMesh.h
+++ b/assetFactory/modelFactoryMesh.h
@@ -4,6 +4,7 @@
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Mesh/Traits.hh>
#include <glm/geometric.hpp>
+#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
@@ -35,9 +36,11 @@ 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);
+ HalfedgeAttributes(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<ModelFactoryTraits> {
@@ -45,13 +48,17 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTra
OpenMesh::FPropHandleT<bool> smoothFaceProperty;
OpenMesh::FPropHandleT<std::string> nameFaceProperty;
+ OpenMesh::HPropHandleT<std::string> nameAdjFaceProperty;
template<typename... Vs>
std::pair<std::string, OpenMesh::FaceHandle>
add_namedFace(std::string name, Vs &&... vs)
{
const auto handle = add_face(std::forward<Vs>(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);
};
diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp
index fc5c34e..12346a6 100644
--- a/assetFactory/style.cpp
+++ b/assetFactory/style.cpp
@@ -4,27 +4,48 @@
void
Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const
{
- if (const auto effectiveColour = getProperty(parents, &Style::colour,
- [](auto && style) {
- return style->colour.a > 0;
- });
- effectiveColour.has_value()) {
- for (const auto & face : faces) {
- mesh.set_color(face.second, effectiveColour->get());
- }
+ 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 = getProperty(parents, &Style::colour,
- [](auto && style) {
- return style->colour.a > 0;
- });
- effectiveColour.has_value()) {
- mesh.set_color(face, effectiveColour->get());
+ applyStyle(mesh, face, getColour(parents));
+}
+
+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());
+ }
}
+ else {
+ mesh.set_color(face, {});
+ if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("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 & heh : mesh.fh_range(face)) {
+ mesh.set_texcoord2D(heh, *coord++);
+ }
+ }
+ }
+}
+
+Style::EffectiveColour
+Style::getColour(const StyleStack & parents)
+{
+ return getProperty(parents, &Style::colour, [](auto && style) {
+ return style->colour.a > 0;
+ });
}
bool
@@ -42,5 +63,6 @@ Style::persist(Persistence::PersistenceStore & store)
}
};
- return STORE_HELPER(colour, ColourParser);
+ 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 e8fd012..d931f98 100644
--- a/assetFactory/style.h
+++ b/assetFactory/style.h
@@ -12,6 +12,7 @@ public:
using StyleStack = std::vector<const Style *>;
using Colour = glm::vec3;
using ColourAlpha = glm::vec4;
+ using EffectiveColour = std::optional<std::reference_wrapper<const ColourAlpha>>;
void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const;
void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const;
@@ -27,8 +28,14 @@ public:
return {};
}
+ static EffectiveColour getColour(const StyleStack & parents);
+
ColourAlpha colour {};
+ std::optional<bool> smooth;
+ std::string texture;
+ std::string textureRotation; // Multiples of 90deg, no int/enum support
protected:
bool persist(Persistence::PersistenceStore & store);
+ void applyStyle(ModelFactoryMesh &, const ModelFactoryMesh::FaceHandle &, EffectiveColour) const;
};
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<TextureFragment> {
+public:
+ std::string id;
+ std::string path;
+
+private:
+ friend Persistence::SelectionPtrBase<Ptr>;
+ bool persist(Persistence::PersistenceStore & store) override;
+};
diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp
new file mode 100644
index 0000000..68a6010
--- /dev/null
+++ b/assetFactory/texturePacker.cpp
@@ -0,0 +1,80 @@
+#include "texturePacker.h"
+#include "collections.hpp"
+#include <algorithm>
+#include <cstdio>
+#include <numeric>
+#include <ostream>
+#include <set>
+
+TexturePacker::TexturePacker(std::span<const Image> in) :
+ inputImages {std::move(in)}, sortedIndexes {vectorOfN(inputImages.size(), size_t {})}
+{
+ std::sort(sortedIndexes.rbegin(), sortedIndexes.rend(), [this](const auto a, const auto b) {
+ return area(inputImages[a]) < area(inputImages[b]);
+ });
+}
+
+TexturePacker::Result
+TexturePacker::pack() const
+{
+ return pack(minSize());
+}
+
+TexturePacker::Result
+TexturePacker::pack(Size size) const
+{
+ using Spaces = std::set<Space>;
+ Spaces spaces {{{}, size}};
+
+ 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[idx] = 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..ca0d67a
--- /dev/null
+++ b/assetFactory/texturePacker.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <glm/vec2.hpp>
+#include <span>
+#include <vector>
+
+class TexturePacker {
+public:
+ using Position = glm::uvec2;
+ 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
+ operator<(const Area & other) const
+ {
+ return area(size) < area(other.size);
+ }
+ };
+ using Image = Size;
+ using Space = Area;
+ using Positions = std::vector<Position>;
+ using Result = std::pair<Positions, Size>;
+
+ TexturePacker(std::span<const Image>);
+
+ Result pack(Size) const;
+ Result pack() const;
+
+ Size minSize() const;
+ static decltype(Size::x) area(const Size & size);
+
+private:
+ std::span<const Image> inputImages;
+ std::vector<size_t> sortedIndexes;
+};