diff options
42 files changed, 731 insertions, 155 deletions
diff --git a/Jamroot.jam b/Jamroot.jam index 27db410..e2075cf 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -90,6 +90,7 @@ lib ilt : <warnings-as-errors>off ] : + <define>GLM_FORCE_SWIZZLE <variant>debug:<warnings>pedantic <variant>debug:<warnings-as-errors>on <variant>debug:<cflags>-Wnon-virtual-dtor 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, ¢re](auto && p) { - p = centre + ((p - centre) % mutation); - }); - // create new vertices - std::vector<OpenMesh::VertexHandle> vertices; - std::transform(points.begin(), points.end(), std::back_inserter(vertices), [&mesh](auto && p) { - return mesh.add_vertex(p); - }); - // 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 ¤t = 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; +}; 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 @@ -50,6 +50,12 @@ RailVehicleClass::persist(Persistence::PersistenceStore & store) } void +RailVehicleClass::postLoad() +{ + texture = getTexture(); +} + +void RailVehicleClass::render( const SceneShader & shader, const Location & location, const std::array<Location, 2> & 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<std::shared_ptr<RailVehicleClass>>; bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; private: RailVehicleClass(std::unique_ptr<ObjParser> obj, std::shared_ptr<Texture>); 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 <ray.hpp> 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;
};
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); } diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index ef6d7e7..efc76e1 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -12,34 +12,36 @@ Cache<Texture, std::filesystem::path> 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<GLsizei>(tex.width), static_cast<GLsizei>(tex.height), tex.data.data()}
+Texture::Texture(const Image & tex, TextureOptions to) :
+ Texture {static_cast<GLsizei>(tex.width), static_cast<GLsizei>(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) : 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, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(type, GL_TEXTURE_WRAP_S, to.wrap);
+ glTexParameteri(type, 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);
- 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
@@ -61,6 +63,12 @@ Texture::save(const glTexture & texture, GLenum format, GLenum type, const glm:: }
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)
{
save(texture, GL_BGR, GL_UNSIGNED_BYTE, size, 3, path, 2);
diff --git a/gfx/models/texture.h b/gfx/models/texture.h index 1aad1e0..ffc9a4a 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -8,16 +8,24 @@ // IWYU pragma: no_forward_declare Cache
class Image;
+struct TextureOptions {
+ GLint wrap {GL_REPEAT};
+ GLint minFilter {GL_LINEAR}, magFilter {GL_LINEAR};
+ GLenum type {GL_TEXTURE_2D};
+};
+
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<Texture, std::filesystem::path> cachedTexture;
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);
@@ -27,4 +35,5 @@ private: const char * path, short tgaFormat);
glTexture m_texture;
+ GLenum type;
};
diff --git a/gfx/models/vertex.hpp b/gfx/models/vertex.hpp index 325aab1..64ec3d0 100644 --- a/gfx/models/vertex.hpp +++ b/gfx/models/vertex.hpp @@ -4,10 +4,14 @@ 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 + + bool operator==(const Vertex &) const = default; glm::vec3 pos; glm::vec2 texCoord; diff --git a/lib/collections.hpp b/lib/collections.hpp index 16870be..59cec6f 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -92,11 +92,11 @@ operator+(const std::vector<T...> & in, Vn && vn) return out; } -template<template<typename> typename Direction = std::plus> +template<template<typename> 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<unsigned int> v; + std::vector<T> v; v.resize(N); std::generate_n(v.begin(), N, [&start, step, adj = Direction {}]() { return std::exchange(start, adj(start, step)); @@ -117,3 +117,18 @@ materializeRange(const std::pair<In, In> & in) { return Rtn(in.first, in.second); } + +template<typename T> struct pair_range { + constexpr auto & + begin() const noexcept + { + return pair.first; + } + constexpr auto & + end() const noexcept + { + return pair.second; + } + const std::pair<T, T> & pair; +}; +template<typename T> pair_range(std::pair<T, T>) -> pair_range<T>; diff --git a/lib/geometricPlane.cpp b/lib/geometricPlane.cpp new file mode 100644 index 0000000..71216c1 --- /dev/null +++ b/lib/geometricPlane.cpp @@ -0,0 +1,28 @@ +#include "geometricPlane.h" +#include "ray.hpp" +#include <glm/geometric.hpp> +#include <glm/gtx/intersect.hpp> + +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; +} + +bool +GeometricPlane::isIntersect(PlaneRelation a, PlaneRelation b) +{ + return ((a == PlaneRelation::Above && b == PlaneRelation::Below) + || (a == PlaneRelation::Below && b == PlaneRelation::Above)); +} + +std::optional<GeometricPlane::DistAndPosition> +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 new file mode 100644 index 0000000..dc8df50 --- /dev/null +++ b/lib/geometricPlane.h @@ -0,0 +1,22 @@ +#pragma once + +#include <glm/vec3.hpp> +#include <optional> + +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<DistAndPosition> getRayIntersectPosition(const Ray &) const; + + static bool isIntersect(PlaneRelation a, PlaneRelation b); +}; diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 8c7c6a4..e22d74d 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -41,12 +41,22 @@ namespace Persistence { return ss.str(); } + void + Persistable::postLoad() + { + } + 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 @@ -56,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(); } @@ -67,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 35d60ca..cc2e4e5 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -6,6 +6,7 @@ #include <iosfwd> #include <map> #include <memory> +#include <optional> #include <span> #include <special_members.hpp> #include <sstream> @@ -147,8 +148,13 @@ namespace Persistence { } }; + template<typename T> struct SelectionT<std::optional<T>> : public SelectionT<T> { + explicit SelectionT(std::optional<T> & value) : SelectionT<T> {value.emplace()} { } + }; + struct Persistable; struct PersistenceStore { + using SelectionFactory = std::function<SelectionPtr()>; PersistenceStore() = default; virtual ~PersistenceStore() = default; DEFAULT_MOVE_NO_COPY(PersistenceStore); @@ -156,12 +162,14 @@ namespace Persistence { template<typename T> [[nodiscard]] inline bool persistType(const T * const, const std::type_info & ti); enum class NameAction { Push, HandleAndContinue, Ignore }; + using NameActionSelection = std::pair<NameAction, SelectionPtr>; template<typename Helper, typename T> [[nodiscard]] inline bool persistValue(const std::string_view key, T & value) { - auto s = std::make_unique<Helper>(value); - const auto act {setName(key, *s)}; + auto [act, s] = setName(key, [&value]() { + return std::make_unique<Helper>(value); + }); if (act != NameAction::Ignore) { sel = std::move(s); if (act == NameAction::HandleAndContinue) { @@ -171,7 +179,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 +189,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 +199,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; @@ -311,8 +319,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<Type>::endObject(stk); } private: @@ -327,8 +336,9 @@ namespace Persistence { void endObject(Persistence::Stack & stk) override { + // TODO test with unique_ptr container.emplace_back(std::move(s)); - stk.pop(); + Persistence::SelectionT<Type>::endObject(stk); } private: @@ -342,6 +352,7 @@ namespace Persistence { DEFAULT_MOVE_COPY(Persistable); virtual bool persist(PersistenceStore & store) = 0; + virtual void postLoad(); [[nodiscard]] virtual std::string getId() const; @@ -484,6 +495,9 @@ namespace Persistence { endObject(Stack & stk) override { make_default_as_needed(this->v); + if (this->v) { + this->v->postLoad(); + } stk.pop(); } 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 <algorithm> +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; 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"; diff --git a/res/brush47.xml b/res/brush47.xml index dbd3327..31518c8 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -2,8 +2,7 @@ <ilt p.id="assetFactory"> <object id="wheel"> <use type="cylinder" position="0,0,0.571" scale="1.142,1.142,0.07" rotation="0,0,1.5708" colour="silver"> - <face id="top" colour="#413b3a"/> - <face id="bottom" colour="#413b3a"/> + <face id="top bottom" colour="#413b3a"/> </use> <use type="cuboid" scale="0.3,0.5,0.3" position="0.2,0,0.421" colour="grey30"/> </object> @@ -59,17 +58,35 @@ </face> </use> </object> + <textureFragment id="roofSideWithVents" path="rail/roofSideWithVents.png"/> + <textureFragment id="roofTopWithVents" path="rail/roofTopWithVents.png"/> + <textureFragment id="cabWindowFront" path="rail/cabWindowFront.png"/> <asset p.typeid="RailVehicleClass" id="brush-47" name="Brush 47" length="19.38" wheelBase="15.70" maxSpeed="42.4688"> <bodyMesh id="body" size="2.69,19.38,3.9"> <use type="cuboid" position="0,0,1.2" scale="2.69,19.38,0.3" colour="goldenrod"> <face id="bottom" colour="#2c3539"/> <face id="top" type="extrude" position="0,0,1.05" colour="#2c4f5a"> - <face id="front" colour="goldenrod"/> - <face id="back" colour="goldenrod"/> + <face id="front back" colour="goldenrod"/> + <face id="right left"> + <split id="frontcorner" origin="0,7.90704,0" normal="0,1,0"/> + <split id="backcorner" origin="0,-7.90704,0" normal="0,-1,0"/> + <face id="rightfrontcorner leftfrontcorner rightbackcorner leftbackcorner" colour="goldenrod"/> + </face> <face id="top" type="extrude" scale="1,0.96,1" position="0,0,0.775"> - <face id="front" colour="#e1eff3"/> - <face id="back" colour="#e1eff3"/> - <face id="top" type="extrude" scale="0.5,0.85,0" smooth="true" position="0,0,0.575" colour="#aeb0b0"/> + <face id="front back" texture="cabWindowFront"/> + <face id="right left"> + <split id="frontcorner" origin="0,7.90704,0" normal="0,1,0"/> + <split id="backcorner" origin="0,-7.90704,0" normal="0,-1,0"/> + <face id="rightfrontcorner leftfrontcorner rightbackcorner leftbackcorner" texture="cabWindowFront"/> + </face> + <face id="top" type="extrude" scale="0.5,0.85,0" smooth="true" position="0,0,0.575" colour="#aeb0b0"> + <face id="top" texture="roofTopWithVents"/> + <face id="right left"> + <split id="frontcorner" origin="0,7.90704,0" normal="0,1,0"/> + <split id="backcorner" origin="0,-7.90704,0" normal="0,-1,0"/> + <face id="right left" texture="roofSideWithVents"/> + </face> + </face> </face> </face> </use> diff --git a/res/rail/cabWindowFront.png b/res/rail/cabWindowFront.png Binary files differnew file mode 100644 index 0000000..3803f21 --- /dev/null +++ b/res/rail/cabWindowFront.png diff --git a/res/rail/roofSideWithVents.png b/res/rail/roofSideWithVents.png Binary files differnew file mode 100644 index 0000000..67ed851 --- /dev/null +++ b/res/rail/roofSideWithVents.png diff --git a/res/rail/roofTopWithVents.png b/res/rail/roofTopWithVents.png Binary files differnew file mode 100644 index 0000000..0f64ced --- /dev/null +++ b/res/rail/roofTopWithVents.png diff --git a/test/Jamfile.jam b/test/Jamfile.jam index e178573..bf1c408 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,14 +49,14 @@ 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 ] ] : <library>test ; +run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob-tree fixtures : *.json ] ] : <library>test ; run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : : : <library>test ; run test-glContextBhvr.cpp ; -run test-assetFactory.cpp : -- : ../res/brush47.xml : <library>test ; -run perf-assetFactory.cpp : : : <library>benchmark <library>test ; -run perf-persistence.cpp : : : <library>benchmark <library>test ; +run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.xml *.png ] fixtures/rgb.txt ] : <library>test ; +run perf-assetFactory.cpp : : : <library>benchmark <library>test <dependency>test-assetFactory ; +run perf-persistence.cpp : : : <library>benchmark <library>test <dependency>test-persistence ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 204ffb3..54168aa 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" @@ -67,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); @@ -117,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); @@ -125,3 +126,46 @@ 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, *boost::unit_test::timeout(5)) +{ + std::vector<TexturePacker::Image> 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(); +} + +BOOST_AUTO_TEST_CASE(texturePacker_many, *boost::unit_test::timeout(5)) +{ + std::vector<TexturePacker::Image> 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<TexturePacker::Image> 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()); +} 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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<std::unique_ptr<TestObject>>(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<AbsObject> aptr; std::vector<std::unique_ptr<TestObject>> vptr; + unsigned int postLoadCalled {}; + bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; }; struct SharedTestObject : public Persistence::Persistable { |