diff options
62 files changed, 1144 insertions, 871 deletions
diff --git a/Jamroot.jam b/Jamroot.jam index e2075cf..35b48ea 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -1,18 +1,17 @@ using gcc ; using pkg-config ; import pkg-config ; -import type : register ; -import type : type ; -import generators : register-standard ; import testing ; import lex ; import sequence ; +import glsl ; pkg-config.import sdl2 ; pkg-config.import glew ; pkg-config.import freetype2 ; pkg-config.import glib-2.0 ; pkg-config.import mxml ; +pkg-config.import assimp ; lib pthread ; lib OpenMeshCore ; @@ -23,7 +22,6 @@ project : requirements <variant>profile:<lto>on <variant>coverage:<coverage>on <toolset>tidy:<enable>all - <toolset>tidy:<exclude>bin/link-static/gfx/models/obj.cpp <toolset>tidy:<exclude>bin/link-static/lib/jsonParse.cpp <toolset>tidy:<checkxx>boost-* <toolset>tidy:<checkxx>bugprone-* @@ -49,26 +47,6 @@ project : requirements <toolset>tidy:<define>TIDY ; -type.register GL_VERTEX_SHADER : vs ; -type.register GL_GEOMETRY_SHADER : gs ; -type.register GL_FRAGMENT_SHADER : fs ; - -generators.register-standard embed.glsl : GL_VERTEX_SHADER : CPP(vs-%) H(vs-%) ; -generators.register-standard embed.glsl : GL_GEOMETRY_SHADER : CPP(gs-%) H(gs-%) ; -generators.register-standard embed.glsl : GL_FRAGMENT_SHADER : CPP(fs-%) H(fs-%) ; - -actions embed.glsl -{ - m4 -DNAME=$(2:B) -DTYPE=$(2:S) > $(1[2]) lib/embed-glsl.h.m4 - m4 -DSOURCE=$(2) -DNAME=$(2:B) -DTYPE=$(2:S) -DGLTYPE=$(OPTIONS) > $(1[1]) lib/embed-glsl.cpp.m4 -} -rule embed.glsl ( targets * : sources * : properties * ) -{ - DEPENDS $(targets) : lib/embed-glsl.h.m4 lib/embed-glsl.cpp.m4 ; - OPTIONS on $(targets) = [ type.type $(sources) ] ; -} -IMPORT $(__name__) : embed.glsl : : embed.glsl ; - exe iliketrains : application/main.cpp : @@ -115,6 +93,7 @@ lib ilt : <library>freetype2 <library>glib-2.0 <library>mxml + <library>assimp <library>pthread <library>OpenMeshCore : : diff --git a/application/main.cpp b/application/main.cpp index 1f91dab..aea3d2e 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -1,4 +1,5 @@ #include <array> +#include <assetFactory/assetFactory.h> #include <chrono> #include <collection.hpp> #include <game/activities/go.h> @@ -11,6 +12,8 @@ #include <game/objective.h> #include <game/objectives/goto.h> #include <game/orders.h> +#include <game/scenary/foliage.h> +#include <game/scenary/plant.h> #include <game/terrain.h> #include <game/vehicles/railVehicle.h> #include <game/vehicles/railVehicleClass.h> @@ -43,6 +46,7 @@ public: world.create<Terrain>(geoData); { + const auto assets = AssetFactory::loadAll("res"); auto rl = world.create<RailLinks>(); const glm::vec3 j {-1120, -1100, 3}, k {-1100, -1000, 15}, l {-1000, -800, 20}, m {-900, -600, 30}, n {-600, -500, 32}, o {-500, -800, 30}, p {-600, -900, 25}, q {-1025, -1175, 10}, @@ -66,13 +70,16 @@ public: rl->addLinksBetween(t, u); rl->addLinksBetween(u, m); const std::shared_ptr<Train> train = world.create<Train>(l3); - auto b47 = std::make_shared<RailVehicleClass>("brush47"); + auto b47 = std::dynamic_pointer_cast<RailVehicleClass>(assets.at("brush-47")); for (int N = 0; N < 6; N++) { train->create<RailVehicle>(b47); } train->orders.removeAll(); train->orders.create<GoTo>(&train->orders, l3->ends[1], l3->length, rl->findNodeAt({-1100, -450, 15})); train->currentActivity = train->orders.current()->createActivity(); + + auto foliage = std::dynamic_pointer_cast<Foliage>(assets.at("Tree-01-1")); + world.create<Plant>(foliage, Location {{-1100, -1100, 0}}); } auto t_start = std::chrono::high_resolution_clock::now(); diff --git a/assetFactory/asset.h b/assetFactory/asset.h index 30f40cd..dba4974 100644 --- a/assetFactory/asset.h +++ b/assetFactory/asset.h @@ -4,11 +4,11 @@ #include "persistence.h" #include <stdTypeDefs.hpp> -class Texture; +class TextureAtlas; class Asset : public Persistence::Persistable, public StdTypeDefs<Asset> { public: - using TexturePtr = std::shared_ptr<Texture>; + using TexturePtr = std::shared_ptr<TextureAtlas>; std::string id; std::string name; diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index ed1af58..af0cd54 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -11,7 +11,7 @@ #include "resource.h" #include "saxParse-persistence.h" #include "texturePacker.h" -#include <stb/stb_image.h> +#include <numeric> AssetFactory::AssetFactory() : shapes { @@ -30,6 +30,18 @@ AssetFactory::loadXML(const std::filesystem::path & filename) return Persistence::SAXParsePersistence {}.loadState<std::shared_ptr<AssetFactory>>(file); } +AssetFactory::Assets +AssetFactory::loadAll(const std::filesystem::path & root) +{ + return std::accumulate(std::filesystem::recursive_directory_iterator {root}, + std::filesystem::recursive_directory_iterator {}, Assets {}, [](auto && out, auto && path) { + if (path.path().extension() == ".xml") { + out.merge(loadXML(path)->assets); + } + return std::move(out); + }); +} + AssetFactory::Colours AssetFactory::parseX11RGB(const char * path) { @@ -81,17 +93,11 @@ 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 +GLuint +AssetFactory::getMaterialIndex(std::string_view id) const { createTexutre(); - const auto & fragmentUV = textureFragmentPositions.at(id); - return { - fragmentUV.xy(), - fragmentUV.zy(), - fragmentUV.zw(), - fragmentUV.xw(), - }; + return textureFragmentPositions.at(id); } Asset::TexturePtr @@ -104,33 +110,31 @@ AssetFactory::getTexture() const void AssetFactory::createTexutre() const { - if (!textureFragments.empty() && (!texture || textureFragmentPositions.empty())) { - // * load images - std::vector<std::unique_ptr<Image>> images; + if (!textureFragments.empty() && !texture) { + // * layout images + std::map<const TextureFragment *, std::unique_ptr<const 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); + textureFragments.begin(), textureFragments.end(), std::inserter(images, images.end()), [](auto && tf) { + return decltype(images)::value_type {tf.second.get(), tf.second->image->get()}; }); - // * 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}; + std::transform(images.begin(), images.end(), std::back_inserter(imageSizes), [](const auto & i) { + return TexturePacker::Image {i.second->width, i.second->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(), + texture = std::make_shared<TextureAtlas>(outSize.x, outSize.y, layout.size()); + std::transform(images.begin(), images.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 = layout.begin(), size = imageSizes.begin(), this](const auto & i) mutable { + const auto m = texture->add(*position, *size, i.second->data.data(), + { + .wrapU = i.first->mapmodeU, + .wrapV = i.first->mapmodeV, + }); position++; - image++; size++; - return decltype(textureFragmentPositions)::value_type {tf.first, positionFraction}; + return decltype(textureFragmentPositions)::value_type {i.first->id, m}; }); } } @@ -141,7 +145,8 @@ AssetFactory::persist(Persistence::PersistenceStore & store) using MapObjects = Persistence::MapByMember<Shapes, std::shared_ptr<Object>>; using MapAssets = Persistence::MapByMember<Assets>; using MapTextureFragments = Persistence::MapByMember<TextureFragments>; + using MapAssImp = Persistence::MapByMember<AssImps, std::shared_ptr<AssImp>, &AssImp::path>; return STORE_TYPE && STORE_NAME_HELPER("object", shapes, MapObjects) && STORE_NAME_HELPER("textureFragment", textureFragments, MapTextureFragments) - && STORE_NAME_HELPER("asset", assets, MapAssets); + && STORE_NAME_HELPER("assimp", assimps, MapAssImp) && STORE_NAME_HELPER("asset", assets, MapAssets); } diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 52692c4..e449ce2 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -1,6 +1,7 @@ #pragma once #include "asset.h" +#include "assimp.h" #include "persistence.h" #include "shape.h" #include "textureFragment.h" @@ -12,6 +13,7 @@ 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 AssImps = std::map<std::string, AssImp::Ptr, std::less<>>; using TextureFragments = std::map<std::string, TextureFragment::Ptr, std::less<>>; using Colour = glm::vec3; using ColourAlpha = glm::vec4; @@ -20,12 +22,14 @@ public: AssetFactory(); [[nodiscard]] static std::shared_ptr<AssetFactory> loadXML(const std::filesystem::path &); + [[nodiscard]] static Assets loadAll(const std::filesystem::path &); [[nodiscard]] ColourAlpha parseColour(std::string_view) const; - [[nodiscard]] TextureFragmentCoords getTextureCoords(std::string_view) const; + [[nodiscard]] GLuint getMaterialIndex(std::string_view) const; [[nodiscard]] Asset::TexturePtr getTexture() const; Shapes shapes; Assets assets; + AssImps assimps; Colours colours; TextureFragments textureFragments; @@ -39,5 +43,5 @@ private: void createTexutre() const; mutable Asset::TexturePtr texture; - mutable std::map<std::string_view, glm::vec4, std::less<>> textureFragmentPositions; + mutable std::map<std::string_view, GLuint, std::less<>> textureFragmentPositions; }; diff --git a/assetFactory/assimp.cpp b/assetFactory/assimp.cpp new file mode 100644 index 0000000..840c5a9 --- /dev/null +++ b/assetFactory/assimp.cpp @@ -0,0 +1,130 @@ +#include "assimp.h" +#include "assetFactory.h" +#include "collections.hpp" +#include "ptr.hpp" +#include "resource.h" +#include <assimp/cimport.h> +#include <assimp/postprocess.h> +#include <assimp/scene.h> +#include <future> +#include <stb/stb_image.h> + +template<typename T> +glm::vec<3, T> +operator*(const aiVector3t<T> & v) +{ + return {v.x, v.y, v.z}; +} + +template<typename T> +glm::vec<2, T> +operator!(const aiVector3t<T> & v) +{ + return {v.x, v.y}; +} + +#define AIRANGE(parent, member) \ + std::span \ + { \ + (parent)->m##member, (parent)->mNum##member \ + } + +using SceneCPtr = std::shared_ptr<const aiScene>; +class AssImpNode : public Shape { +public: + AssImpNode(SceneCPtr scene, const aiNode * node) : scene(std::move(scene)), node(node) { } + + SceneCPtr scene; + const aiNode * node; + + CreatedFaces + createMesh(ModelFactoryMesh & mesh, float) const + { + CreatedFaces faces; + addMesh(faces, mesh, node); + return faces; + } + + void + addMesh(CreatedFaces & faces, ModelFactoryMesh & mesh, const aiNode * node) const + { + for (const auto & m : AIRANGE(node, Meshes)) { + addMesh(faces, mesh, scene->mMeshes[m]); + } + for (const auto & n : AIRANGE(node, Children)) { + addMesh(faces, mesh, n); + } + } + + void + addMesh(CreatedFaces & faces, ModelFactoryMesh & mesh, const aiMesh * amesh) const + { + const auto vhs = AIRANGE(amesh, Vertices) * [&mesh](auto && v) { + return mesh.add_vertex(*v); + }; + const auto & m = scene->mMaterials[amesh->mMaterialIndex]->GetName(); + + GLuint material {}; + if (auto mf = Persistence::ParseBase::getShared<AssetFactory>("assetFactory")) { + material = mf->getMaterialIndex(m.C_Str()); + } + + for (const auto & f : AIRANGE(amesh, Faces)) { + const auto fvhs = AIRANGE(&f, Indices) * [&vhs](auto && i) { + return vhs[i]; + }; + const auto fh = faces.emplace(mesh.add_namedFace(amesh->mName.C_Str(), fvhs))->second; + if (amesh->HasTextureCoords(0)) { + for (auto idx = f.mIndices; const auto fheh : mesh.fh_range(fh)) { + const auto ouv = !amesh->mTextureCoords[0][*idx++]; + mesh.set_texcoord2D(fheh, ouv); + mesh.property(mesh.materialFaceProperty, fh) = material; + } + } + } + } +}; + +static_assert(TextureOptions::MapMode::Repeat == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Wrap)); +static_assert(TextureOptions::MapMode::Clamp == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Clamp)); +static_assert(TextureOptions::MapMode::Decal == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Decal)); +static_assert(TextureOptions::MapMode::Mirror == static_cast<TextureOptions::MapMode>(aiTextureMapMode_Mirror)); + +void +AssImp::postLoad() +{ + SceneCPtr scene { + aiImportFile(Resource::mapPath(path).c_str(), aiProcess_RemoveRedundantMaterials), &aiReleaseImport}; + if (!scene) { + throw std::runtime_error("Failed to load asset library: " + path); + } + if (auto mf = Persistence::ParseBase::getShared<AssetFactory>("assetFactory")) { + const auto root = AIRANGE(scene->mRootNode, Children); + std::transform( + root.begin(), root.end(), std::inserter(mf->shapes, mf->shapes.end()), [&scene](const aiNode * m) { + return AssetFactory::Shapes::value_type {m->mName.C_Str(), std::make_shared<AssImpNode>(scene, m)}; + }); + const auto materials = AIRANGE(scene, Materials); + std::transform(materials.begin(), materials.end(), + std::inserter(mf->textureFragments, mf->textureFragments.end()), [&scene](const aiMaterial * m) { + aiString path; + m->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); + auto texture = std::make_shared<TextureFragment>(); + texture->id = m->GetName().C_Str(); + texture->path = path.C_Str(); + m->Get(AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0), texture->mapmodeU); + m->Get(AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0), texture->mapmodeV); + texture->image = Worker::addWork([t = scene->GetEmbeddedTexture(path.C_Str())]() { + return std::make_unique<Image>( + std::span {reinterpret_cast<unsigned char *>(t->pcData), t->mWidth}, STBI_rgb_alpha); + }); + return AssetFactory::TextureFragments::value_type {texture->id, texture}; + }); + } +} + +bool +AssImp::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_MEMBER(path); +} diff --git a/assetFactory/assimp.h b/assetFactory/assimp.h new file mode 100644 index 0000000..55b551d --- /dev/null +++ b/assetFactory/assimp.h @@ -0,0 +1,14 @@ +#pragma once + +#include "persistence.h" + +class AssImp : public Persistence::Persistable { +public: + using Ptr = std::shared_ptr<AssImp>; + + void postLoad() override; + + bool persist(Persistence::PersistenceStore & store) override; + + std::string path; +}; diff --git a/assetFactory/factoryMesh.cpp b/assetFactory/factoryMesh.cpp index 8869efd..f7bc7c8 100644 --- a/assetFactory/factoryMesh.cpp +++ b/assetFactory/factoryMesh.cpp @@ -6,43 +6,46 @@ Mesh::Ptr FactoryMesh::createMesh() const { - ModelFactoryMesh mesh; + std::vector<Vertex> vertices; + std::vector<unsigned int> indices; + for (const auto & use : uses) { + ModelFactoryMesh mesh; use->createMesh(mesh, 1); - } - 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); + mesh.update_face_normals(); + mesh.update_vertex_normals(); + for (const auto & face : mesh.faces()) { + const auto & smooth = mesh.property(mesh.smoothFaceProperty, face); + const auto & colour = mesh.color(face); + const auto & material = mesh.property(mesh.materialFaceProperty, 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); + 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, material}; + 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]); + 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, indices); } diff --git a/assetFactory/modelFactoryMesh.cpp b/assetFactory/modelFactoryMesh.cpp index e640502..3d4b5f3 100644 --- a/assetFactory/modelFactoryMesh.cpp +++ b/assetFactory/modelFactoryMesh.cpp @@ -3,6 +3,7 @@ ModelFactoryMesh::ModelFactoryMesh() { add_property(smoothFaceProperty); + add_property(materialFaceProperty); add_property(nameFaceProperty); add_property(nameAdjFaceProperty); } diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index 8ac2edd..4eac7ee 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -1,6 +1,7 @@ #pragma once #include "modelFactoryMesh_fwd.h" +#include <GL/glew.h> #include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh> #include <OpenMesh/Core/Mesh/Traits.hh> #include <glm/geometric.hpp> @@ -47,6 +48,7 @@ struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT<ModelFactoryTra ModelFactoryMesh(); OpenMesh::FPropHandleT<bool> smoothFaceProperty; + OpenMesh::FPropHandleT<GLuint> materialFaceProperty; OpenMesh::FPropHandleT<std::string> nameFaceProperty; OpenMesh::HPropHandleT<std::string> nameAdjFaceProperty; diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 12346a6..ea67fc2 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -30,7 +30,14 @@ Style::applyStyle( else { mesh.set_color(face, {}); if (auto mf = Persistence::ParseBase::getShared<const AssetFactory>("assetFactory")) { - auto coords = mf->getTextureCoords(texture); + const auto material = mf->getMaterialIndex(texture); + mesh.property(mesh.materialFaceProperty, face) = material; + static constexpr std::array<ModelFactoryTraits::TexCoord2D, 4> coords {{ + {0.f, 0.f}, + {1.f, 0.f}, + {1.f, 1.f}, + {0.f, 1.f}, + }}; 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)) { diff --git a/assetFactory/textureFragment.cpp b/assetFactory/textureFragment.cpp index 72107a5..0a4ec1d 100644 --- a/assetFactory/textureFragment.cpp +++ b/assetFactory/textureFragment.cpp @@ -1,7 +1,17 @@ #include "textureFragment.h" +#include "resource.h" +#include <stb/stb_image.h> bool TextureFragment::persist(Persistence::PersistenceStore & store) { return STORE_TYPE && STORE_MEMBER(id) && STORE_MEMBER(path); } + +void +TextureFragment::postLoad() +{ + image = Worker::addWork([this]() { + return std::make_unique<Image>(Resource::mapPath(path), STBI_rgb_alpha); + }); +} diff --git a/assetFactory/textureFragment.h b/assetFactory/textureFragment.h index 52f2591..e6f07f3 100644 --- a/assetFactory/textureFragment.h +++ b/assetFactory/textureFragment.h @@ -1,14 +1,20 @@ #pragma once +#include "gfx/image.h" +#include "gfx/models/texture.h" #include "persistence.h" #include "stdTypeDefs.hpp" +#include "worker.h" class TextureFragment : public Persistence::Persistable, public StdTypeDefs<TextureFragment> { public: std::string id; std::string path; + TextureOptions::MapMode mapmodeU, mapmodeV; + Worker::WorkPtrT<std::unique_ptr<Image>> image; private: friend Persistence::SelectionPtrBase<Ptr>; bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; }; diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp index 68a6010..fcc935f 100644 --- a/assetFactory/texturePacker.cpp +++ b/assetFactory/texturePacker.cpp @@ -1,7 +1,9 @@ #include "texturePacker.h" #include "collections.hpp" +#include <GL/glew.h> #include <algorithm> #include <cstdio> +#include <glm/common.hpp> #include <numeric> #include <ostream> #include <set> @@ -12,6 +14,9 @@ TexturePacker::TexturePacker(std::span<const Image> in) : std::sort(sortedIndexes.rbegin(), sortedIndexes.rend(), [this](const auto a, const auto b) { return area(inputImages[a]) < area(inputImages[b]); }); + int mts; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mts); + maxTextureSize = static_cast<unsigned int>(mts); } TexturePacker::Result @@ -23,6 +28,9 @@ TexturePacker::pack() const TexturePacker::Result TexturePacker::pack(Size size) const { + if (size.x > maxTextureSize || size.y > maxTextureSize) { + return {}; + } using Spaces = std::set<Space>; Spaces spaces {{{}, size}}; @@ -47,14 +55,27 @@ TexturePacker::pack(Size size) const } } else { - if (size.x < size.y) { - return pack({size.x * 2, size.y}); + const auto x = pack({size.x * 2, size.y}), y = pack({size.x, size.y * 2}); + if (!x.first.empty() && (y.first.empty() || area(x.second) < area(y.second))) { + return x; } - else { - return pack({size.x, size.y * 2}); + else if (!y.first.empty()) { + return y; } + return {}; } } + if (GLEW_ARB_texture_non_power_of_two) { + // Crop the size back to minimum size + size = std::transform_reduce( + result.begin(), result.end(), inputImages.begin(), Size {}, + [](auto && max, auto && limit) { + return glm::max(max, limit); + }, + [](auto && pos, auto && size) { + return pos + size; + }); + } return {result, size}; } diff --git a/assetFactory/texturePacker.h b/assetFactory/texturePacker.h index ca0d67a..a1b270b 100644 --- a/assetFactory/texturePacker.h +++ b/assetFactory/texturePacker.h @@ -38,4 +38,5 @@ public: private: std::span<const Image> inputImages; std::vector<size_t> sortedIndexes; + unsigned int maxTextureSize; }; diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp new file mode 100644 index 0000000..d39d500 --- /dev/null +++ b/game/scenary/foliage.cpp @@ -0,0 +1,33 @@ +#include "foliage.h" +#include "gfx/gl/sceneShader.h" +#include "gfx/gl/shadowMapper.h" +#include "gfx/models/texture.h" + +bool +Foliage::persist(Persistence::PersistenceStore & store) +{ + return STORE_TYPE && STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store); +} + +void +Foliage::postLoad() +{ + texture = getTexture(); +} + +void +Foliage::render(const SceneShader & shader, const Location & loc) const +{ + shader.basic.use(loc); + if (texture) { + texture->bind(); + } + bodyMesh->Draw(); +} + +void +Foliage::shadows(const ShadowMapper & mapper, const Location & loc) const +{ + mapper.dynamicPoint.use(loc); + bodyMesh->Draw(); +} diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h new file mode 100644 index 0000000..b85aab2 --- /dev/null +++ b/game/scenary/foliage.h @@ -0,0 +1,22 @@ +#pragma once + +#include "assetFactory/asset.h" + +class SceneShader; +class ShadowMapper; +class Location; +class Texture; + +class Foliage : public Asset, public StdTypeDefs<Foliage> { + Mesh::Ptr bodyMesh; + std::shared_ptr<Texture> texture; + +public: + void render(const SceneShader &, const Location &) const; + void shadows(const ShadowMapper &, const Location &) const; + +protected: + friend Persistence::SelectionPtrBase<std::shared_ptr<Foliage>>; + bool persist(Persistence::PersistenceStore & store) override; + void postLoad() override; +}; diff --git a/game/scenary/plant.cpp b/game/scenary/plant.cpp new file mode 100644 index 0000000..2b01bee --- /dev/null +++ b/game/scenary/plant.cpp @@ -0,0 +1,13 @@ +#include "plant.h" + +void +Plant::render(const SceneShader & shader) const +{ + type->render(shader, position); +} + +void +Plant::shadows(const ShadowMapper & mapper) const +{ + type->shadows(mapper, position); +} diff --git a/game/scenary/plant.h b/game/scenary/plant.h new file mode 100644 index 0000000..55acca1 --- /dev/null +++ b/game/scenary/plant.h @@ -0,0 +1,22 @@ +#pragma once + +#include "foliage.h" +#include "game/worldobject.h" +#include "gfx/renderable.h" +#include "location.hpp" + +class Plant : public Renderable, public WorldObject { + std::shared_ptr<const Foliage> type; + Location position; + + void render(const SceneShader & shader) const override; + void shadows(const ShadowMapper & shadowMapper) const override; + + void + tick(TickDuration) override + { + } + +public: + Plant(std::shared_ptr<const Foliage> type, Location position) : type(std::move(type)), position(position) { } +}; diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 4e9263c..b7a3b1e 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -2,7 +2,6 @@ #include "gfx/gl/sceneShader.h" #include "gfx/gl/shadowMapper.h" #include "gfx/models/mesh.h" -#include "gfx/models/obj.h" #include "gfx/models/texture.h" #include <algorithm> #include <array> @@ -22,25 +21,6 @@ #include <utility> #include <vector> -RailVehicleClass::RailVehicleClass(const std::string & name) : - RailVehicleClass {std::make_unique<ObjParser>(Resource::mapPath(name + ".obj")), - Texture::cachedTexture.get(Resource::mapPath(name + ".png"))} -{ -} - -RailVehicleClass::RailVehicleClass(std::unique_ptr<ObjParser> o, std::shared_ptr<Texture> t) : - texture {std::move(t)}, maxSpeed(95._mph) -{ - wheelBase = bogieOffset(*o); - length = objectLength(*o); - const auto m = o->createMeshes(); - bodyMesh = m.at("Body"); - bogies[0] = m.at("Bogie1"); - bogies[1] = m.at("Bogie2"); -} - -RailVehicleClass::RailVehicleClass() { } - bool RailVehicleClass::persist(Persistence::PersistenceStore & store) { @@ -80,36 +60,3 @@ RailVehicleClass::shadows( bogies[b]->Draw(); } } - -float -RailVehicleClass::bogieOffset(ObjParser & o) -{ - float wheelBase {0}; - // offset bogie positions so they can be set directly - for (auto & object : o.objects) { // bogie object index - if (!object.first.starts_with("Bogie")) { - continue; - } - std::set<std::pair<float, std::size_t>> vertexIds; - for (const auto & face : object.second) { - for (const auto & faceElement : face) { - vertexIds.emplace(o.vertices[faceElement.x - 1].y, faceElement.x - 1); - } - } - const auto offset = (vertexIds.begin()->first + vertexIds.rbegin()->first) / 2; - for (const auto & v : vertexIds) { - o.vertices[v.second].y -= offset; - } - wheelBase += std::abs(offset); - } - return wheelBase; -} - -float -RailVehicleClass::objectLength(ObjParser & o) -{ - const auto mme = std::minmax_element(o.vertices.begin(), o.vertices.end(), [](const auto & v1, const auto & v2) { - return v1.y < v2.y; - }); - return mme.second->y - mme.first->y; -} diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index 61ec4ec..1a52e2b 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -9,14 +9,10 @@ class SceneShader; class ShadowMapper; class Texture; -class ObjParser; class Location; class RailVehicleClass : public Asset { public: - explicit RailVehicleClass(const std::string & name); - RailVehicleClass(); - void render(const SceneShader &, const Location &, const std::array<Location, 2> &) const; void shadows(const ShadowMapper &, const Location &, const std::array<Location, 2> &) const; @@ -31,10 +27,5 @@ 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>); - static float bogieOffset(ObjParser & o); - static float objectLength(ObjParser & o); }; using RailVehicleClassPtr = std::shared_ptr<RailVehicleClass>; diff --git a/gfx/gl/shaders/basicShader.fs b/gfx/gl/shaders/basicShader.fs index 24b2791..57eaba2 100644 --- a/gfx/gl/shaders/basicShader.fs +++ b/gfx/gl/shaders/basicShader.fs @@ -1,22 +1,54 @@ #version 330 core +#extension GL_ARB_shading_language_420pack : enable -in vec3 FragPos; -in vec2 TexCoords; -in vec3 Normal; -in vec4 Colour; +include(`materialInterface.glsl') +include(`geometryOut.glsl') -out vec4 gPosition; -out vec4 gNormal; -out vec4 gAlbedoSpec; +layout(binding = 0) uniform sampler2D texture0; +layout(binding = 1) uniform usampler2DRect material; -uniform sampler2D texture0; +float map(uint mapmode, float value) +{ + switch (mapmode) { + case 0u: // Repeat + return fract(value); + case 1u: // Clamp to edge + return clamp(0.0, 1.0, value); + case 2u: // Mirror + discard; + case 3u: // Decal + if (value != clamp(0.0, 1.0, value)) { + discard; + } + } + return 0; +} + +vec2 map(uint mapmodeU, uint mapmodeV, vec2 value) +{ + return vec2(map(mapmodeU, value.x), map(mapmodeV, value.y)); +} + +vec4 getTextureColour(uint midx, vec2 uv) +{ + if (midx > 0u) { + const vec2 tSize = textureSize(texture0, 0); + const vec4 sPosSize = texture(material, uvec2(0, midx - 1u)); + const uvec4 sMode = texture(material, uvec2(1, midx - 1u)); + const uint mapmodeU = sMode.x & 0xFu; + const uint mapmodeV = (sMode.x & 0xF0u) >> 1; + uv = (sPosSize.xy + sPosSize.zw * map(mapmodeU, mapmodeV, uv)) / tSize; + } + return texture(texture0, uv); +} void main() { - vec4 textureColour = texture(texture0, TexCoords); - float clear = round(mix(textureColour.a, 1, Colour.a)); - gPosition = vec4(FragPos, clear); - gNormal = vec4(Normal, clear); + vec4 textureColour = getTextureColour(Material, TexCoords); + float opaque = step(0.5, mix(textureColour.a, 1, Colour.a)); + gPosition = vec4(FragPos, opaque); + gNormal = vec4(Normal, opaque); + gl_FragDepth = mix(1.0, gl_FragCoord.z, opaque); gAlbedoSpec = mix(textureColour, vec4(Colour.rgb, 1), Colour.a); } diff --git a/gfx/gl/shaders/basicShader.vs b/gfx/gl/shaders/basicShader.vs index ff9a401..e1701ed 100644 --- a/gfx/gl/shaders/basicShader.vs +++ b/gfx/gl/shaders/basicShader.vs @@ -1,14 +1,7 @@ #version 330 core -in vec3 position; -in vec2 texCoord; -in vec3 normal; -in vec4 colour; - -out vec3 FragPos; -out vec2 TexCoords; -out vec3 Normal; -out vec4 Colour; +include(`meshIn.glsl') +include(`materialInterface.glsl') uniform mat4 viewProjection; uniform mat4 model; @@ -22,6 +15,7 @@ main() TexCoords = texCoord; Normal = (model * vec4(normal, 0.0)).xyz; Colour = colour; + Material = material; gl_Position = viewProjection * worldPos; } diff --git a/gfx/gl/shaders/geometryOut.glsl b/gfx/gl/shaders/geometryOut.glsl new file mode 100644 index 0000000..dc5f8e8 --- /dev/null +++ b/gfx/gl/shaders/geometryOut.glsl @@ -0,0 +1,3 @@ +layout(location = 0) out vec4 gPosition; +layout(location = 1) out vec4 gNormal; +layout(location = 2) out vec4 gAlbedoSpec; diff --git a/gfx/gl/shaders/landmassShader.fs b/gfx/gl/shaders/landmassShader.fs index 7c08e8e..f3cc3e8 100644 --- a/gfx/gl/shaders/landmassShader.fs +++ b/gfx/gl/shaders/landmassShader.fs @@ -1,12 +1,7 @@ #version 330 core -in vec3 FragPos; -in vec2 TexCoords; -in vec3 Normal; - -layout(location = 0) out vec4 gPosition; -layout(location = 1) out vec4 gNormal; -layout(location = 2) out vec4 gAlbedoSpec; +include(`materialInterface.glsl') +include(`geometryOut.glsl') uniform sampler2D texture0; diff --git a/gfx/gl/shaders/landmassShader.vs b/gfx/gl/shaders/landmassShader.vs index 30c4ef4..0cc8153 100644 --- a/gfx/gl/shaders/landmassShader.vs +++ b/gfx/gl/shaders/landmassShader.vs @@ -1,14 +1,7 @@ #version 330 core -in vec3 position; -in vec2 texCoord; -in vec3 normal; -in vec4 colour; - -out vec3 FragPos; -out vec2 TexCoords; -out vec3 Normal; -out vec4 Colour; +include(`meshIn.glsl') +include(`materialInterface.glsl') uniform mat4 viewProjection; @@ -19,6 +12,7 @@ main() TexCoords = texCoord; Normal = normal; Colour = colour; + Material = material; gl_Position = viewProjection * vec4(position, 1.0); } diff --git a/gfx/gl/shaders/lightingShader.vs b/gfx/gl/shaders/lightingShader.vs index 2271d2e..e07cd0a 100644 --- a/gfx/gl/shaders/lightingShader.vs +++ b/gfx/gl/shaders/lightingShader.vs @@ -1,6 +1,6 @@ #version 330 core -in vec4 position; +in ivec4 position; out vec2 TexCoords; diff --git a/gfx/gl/shaders/materialInterface.glsl b/gfx/gl/shaders/materialInterface.glsl new file mode 100644 index 0000000..5735cf9 --- /dev/null +++ b/gfx/gl/shaders/materialInterface.glsl @@ -0,0 +1,7 @@ +ifelse(TYPE, .fs, in, out) vec3 FragPos; +ifelse(TYPE, .fs, in, out) vec2 TexCoords; +ifelse(TYPE, .fs, in, out) vec3 Normal; +ifelse(TYPE, .fs, in, out) vec4 Colour; +flat +ifelse(TYPE, .fs, in, out) +uint Material; diff --git a/gfx/gl/shaders/meshIn.glsl b/gfx/gl/shaders/meshIn.glsl new file mode 100644 index 0000000..dd84a10 --- /dev/null +++ b/gfx/gl/shaders/meshIn.glsl @@ -0,0 +1,5 @@ +layout(location = 0) in vec3 position; +layout(location = 1) in vec2 texCoord; +layout(location = 2) in vec3 normal; +layout(location = 3) in vec4 colour; +layout(location = 4) in uint material; diff --git a/gfx/gl/shaders/shadowDynamicPoint.vs b/gfx/gl/shaders/shadowDynamicPoint.vs index 6ea352f..531d8de 100644 --- a/gfx/gl/shaders/shadowDynamicPoint.vs +++ b/gfx/gl/shaders/shadowDynamicPoint.vs @@ -1,6 +1,6 @@ #version 330 core -in vec3 position; +include(`meshIn.glsl') uniform mat4 viewProjection; uniform mat4 model; diff --git a/gfx/gl/shaders/shadowFixedPoint.vs b/gfx/gl/shaders/shadowFixedPoint.vs index 246890c..c9fa19b 100644 --- a/gfx/gl/shaders/shadowFixedPoint.vs +++ b/gfx/gl/shaders/shadowFixedPoint.vs @@ -1,6 +1,6 @@ #version 330 core -layout(location = 0) in vec3 position; +include(`meshIn.glsl') uniform mat4 viewProjection; diff --git a/gfx/gl/shaders/waterShader.fs b/gfx/gl/shaders/waterShader.fs index d242566..5936da4 100644 --- a/gfx/gl/shaders/waterShader.fs +++ b/gfx/gl/shaders/waterShader.fs @@ -1,13 +1,8 @@ #version 330 core #extension GL_ARB_shading_language_420pack : enable -in vec3 FragPos; -in vec2 TexCoords; -in vec3 Normal; - -out vec4 gPosition; -out vec4 gNormal; -out vec4 gAlbedoSpec; +include(`materialInterface.glsl') +include(`geometryOut.glsl') uniform sampler2D texture0; uniform vec3 waves; diff --git a/gfx/gl/shaders/waterShader.vs b/gfx/gl/shaders/waterShader.vs index fe9206f..a21b49f 100644 --- a/gfx/gl/shaders/waterShader.vs +++ b/gfx/gl/shaders/waterShader.vs @@ -1,12 +1,7 @@ #version 330 core -in vec3 position; -in vec2 texCoord; -in vec3 normal; - -out vec3 FragPos; -out vec2 TexCoords; -out vec3 Normal; +include(`meshIn.glsl') +include(`materialInterface.glsl') uniform mat4 viewProjection; uniform vec3 waves; diff --git a/gfx/gl/vertexArrayObject.hpp b/gfx/gl/vertexArrayObject.hpp index c0273c0..5b9fc60 100644 --- a/gfx/gl/vertexArrayObject.hpp +++ b/gfx/gl/vertexArrayObject.hpp @@ -61,7 +61,7 @@ private: { glEnableVertexAttribArray(vertexArrayId); using traits = gl_traits<T>; - glVertexAttribPointer(vertexArrayId, traits::size, traits::type, GL_FALSE, sizeof(Vertex), ptr); + traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(Vertex), ptr); } template<auto Vertex::*attrib> diff --git a/gfx/image.cpp b/gfx/image.cpp index e3e3b01..fb86cb6 100644 --- a/gfx/image.cpp +++ b/gfx/image.cpp @@ -19,6 +19,22 @@ Image::Image(const char * fileName, int flags) : width {}, height {}, numCompone data = {bytes, static_cast<size_t>(width * height * numComponents)}; } +Image::Image(std::span<unsigned char> buffer, int flags) +{ + stbi_set_flip_vertically_on_load(1); + int w, h, nc; + unsigned char * bytes = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &w, &h, &nc, flags); + width = static_cast<unsigned int>(w); + height = static_cast<unsigned int>(h); + numComponents = static_cast<unsigned int>(nc); + + if (!bytes) { + throw std::runtime_error {"Unable to load image from memory buffer "}; + } + + data = {bytes, static_cast<size_t>(width * height * numComponents)}; +} + Image::~Image() { stbi_image_free(data.data()); diff --git a/gfx/image.h b/gfx/image.h index 47437ca..97c2731 100644 --- a/gfx/image.h +++ b/gfx/image.h @@ -8,6 +8,7 @@ class Image { public: Image(const char * fileName, int flags); Image(const std::string & fileName, int flags) : Image(fileName.c_str(), flags) { } + Image(std::span<unsigned char> data, int flags); ~Image(); NO_COPY(Image); diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index 3db1ad5..2719211 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -7,7 +7,8 @@ Mesh::Mesh(const std::span<const Vertex> vertices, const std::span<const unsigned int> indices, GLenum m) :
m_numIndices {static_cast<GLsizei>(indices.size())}, mode {m}
{
- VertexArrayObject<Vertex>::configure<&Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour>(
+ VertexArrayObject<Vertex>::configure<&Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour,
+ &Vertex::material>(
m_vertexArrayObject, m_vertexArrayBuffers[0], m_vertexArrayBuffers[1], vertices, indices);
}
diff --git a/gfx/models/obj.h b/gfx/models/obj.h deleted file mode 100644 index e28f7de..0000000 --- a/gfx/models/obj.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#ifndef yyFlexLexer -# define yyFlexLexer objbaseFlexLexer -# include <FlexLexer.h> -#endif -#include <filesystem> -#include <fstream> -#include <glm/glm.hpp> -#include <map> -#include <memory> -#include <vector> - -class Mesh; -class Vertex; - -class ObjParser : yyFlexLexer { -public: - explicit ObjParser(const std::filesystem::path & fileName); - explicit ObjParser(std::unique_ptr<std::istream> in); - - int yylex() override; - - std::vector<glm::vec4> vertices; - std::vector<glm::vec3> texCoords; - std::vector<glm::vec3> normals; - using FaceElement = glm::vec<3, unsigned int>; - using Face = std::vector<FaceElement>; - using Faces = std::vector<Face>; - using Object = std::pair<std::string, Faces>; - std::vector<Object> objects; - glm::length_t axis {0}; - - using NamedMeshesData = std::map<std::string, std::pair<std::vector<Vertex>, std::vector<unsigned int>>>; - [[nodiscard]] NamedMeshesData createMeshData() const; - using NamedMeshes = std::map<std::string, std::shared_ptr<const Mesh>>; - [[nodiscard]] NamedMeshes createMeshes() const; -}; diff --git a/gfx/models/obj.impl.cpp b/gfx/models/obj.impl.cpp deleted file mode 100644 index 205abc8..0000000 --- a/gfx/models/obj.impl.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "obj.h" -#include <algorithm> -#include <cassert> -#include <filesystem> -#include <fstream> -#include <gfx/models/mesh.h> // IWYU pragma: keep -#include <gfx/models/vertex.hpp> -#include <glm/glm.hpp> -#include <iterator> -#include <map> -#include <memory> -#include <utility> -#include <vector> - -ObjParser::ObjParser(const std::filesystem::path & fileName) : ObjParser {std::make_unique<std::ifstream>(fileName)} { } - -ObjParser::ObjParser(std::unique_ptr<std::istream> in) : yyFlexLexer(in.get()) -{ - assert(in); - ObjParser::yylex(); - assert(in->good()); - constexpr const glm::mat4 obj2ilt { - -1, 0, 0, 0, // x - 0, 0, 1, 0, // y - 0, 1, 0, 0, // z - 0, 0, 0, 0, // w - }; - std::for_each(vertices.begin(), vertices.end(), [&obj2ilt](auto & v) { - v = v * obj2ilt; - }); - std::for_each(normals.begin(), normals.end(), [&obj2ilt](auto & v) { - v = glm::vec4 {v, 0} * obj2ilt; - }); -} - -ObjParser::NamedMeshes -ObjParser::createMeshes() const -{ - NamedMeshes out; - const auto data {createMeshData()}; - std::transform(data.begin(), data.end(), std::inserter(out, out.end()), [](auto && obj) { - return std::make_pair(obj.first, std::make_shared<Mesh>(obj.second.first, obj.second.second)); - }); - return out; -} - -ObjParser::NamedMeshesData -ObjParser::createMeshData() const -{ - NamedMeshesData out; - std::transform(objects.begin(), objects.end(), std::inserter(out, out.end()), [this](auto && obj) { - std::vector<Vertex> overtices; - std::vector<ObjParser::FaceElement> vertexOrder; - std::vector<unsigned int> indices; - for (const auto & face : obj.second) { - for (auto idx = 2U; idx < face.size(); idx += 1) { - auto f = [&](auto idx) { - const auto & fe {face[idx]}; - if (const auto existing = std::find(vertexOrder.begin(), vertexOrder.end(), fe); - existing != vertexOrder.end()) { - indices.push_back(static_cast<unsigned int>(std::distance(vertexOrder.begin(), existing))); - } - else { - indices.push_back(static_cast<unsigned int>(overtices.size())); - overtices.emplace_back(vertices[fe.x - 1], texCoords[fe.y - 1], -normals[fe.z - 1]); - vertexOrder.emplace_back(fe); - } - }; - f(0U); - f(idx); - f(idx - 1); - } - } - return std::make_pair(obj.first, std::make_pair(overtices, indices)); - }); - return out; -} diff --git a/gfx/models/obj.ll b/gfx/models/obj.ll deleted file mode 100644 index 84884fe..0000000 --- a/gfx/models/obj.ll +++ /dev/null @@ -1,116 +0,0 @@ -%option batch -%option c++ -%option noyywrap -%option 8bit -%option stack -%option yyclass="ObjParser" -%option prefix="objbase" - -%{ -#include <gfx/models/obj.h> -#include <glm/glm.hpp> -#include <memory> -#include <string> -#include <vector> -class objbaseFlexLexer; -%} - -comment #.* -float -?[0-9.]+ -index -?[0-9]+ -linestring [^\r\n]* -truthy (1|on) -falsey (0|off) - -%x FACE -%x MTLLIB -%x OBJECT -%x SMOOTH -%x USEMTL -%x VERTEX -%x NORMAL -%x TEXCOORD - -%% - -<INITIAL>{comment} { - // fprintf(stderr, "COMMENT %s\n", YYText()); -} -<INITIAL>"f " { - BEGIN(FACE); - objects.back().second.emplace_back(); - objects.back().second.back().emplace_back(); - axis = 0; -} -<INITIAL>"mtllib " { - BEGIN(MTLLIB); -} -<INITIAL>"o " { - BEGIN(OBJECT); -} -<INITIAL>"s " { - BEGIN(SMOOTH); -} -<INITIAL>"usemtl " { - BEGIN(USEMTL); -} -<INITIAL>"v " { - BEGIN(VERTEX); - vertices.emplace_back(0, 0, 0, 1); - axis = 0; -} -<INITIAL>"vn " { - BEGIN(NORMAL); - normals.emplace_back(0, 0, 0); - axis = 0; -} -<INITIAL>"vt " { - BEGIN(TEXCOORD); - texCoords.emplace_back(0, 1, 1); - axis = 0; -} -<USEMTL>{linestring} { - // fprintf(stderr, "USEMTL <%s>\n", YYText()); -} -<MTLLIB>{linestring} { - // fprintf(stderr, "MTLLIB <%s>\n", YYText()); -} -<OBJECT>{linestring} { - objects.emplace_back(YYText(), Faces{}); -} -<SMOOTH>{truthy} { - // fprintf(stderr, "Set smooth\n"); -} -<SMOOTH>{falsey} { - // fprintf(stderr, "Set flat\n"); -} -<VERTEX>{float} { - vertices.back()[axis++] = std::stof(YYText()); -} -<NORMAL>{float} { - normals.back()[axis++] = std::stof(YYText()); -} -<TEXCOORD>{float} { - texCoords.back()[axis++] = std::stof(YYText()); -} -<FACE>{index} { - objects.back().second.back().back()[axis] = std::stoi(YYText()); -} -<FACE>\/ { - axis++; -} -<FACE>[ \t] { - objects.back().second.back().emplace_back(); - axis = 0; -} - -<*>[ \t] { -} -<*>[\r\n\f] { - BEGIN(INITIAL); -} - -%% - -// Make iwyu think unistd.h is required -[[maybe_unused]]static auto x=getpid; diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp index efc76e1..e014f80 100644 --- a/gfx/models/texture.cpp +++ b/gfx/models/texture.cpp @@ -1,5 +1,6 @@ #include "texture.h"
#include "glArrays.h"
+#include "tga.h"
#include <GL/glew.h>
#include <cache.h>
#include <fcntl.h>
@@ -12,6 +13,21 @@ Cache<Texture, std::filesystem::path> Texture::cachedTexture;
+GLint
+TextureOptions::glMapMode(TextureOptions::MapMode mm)
+{
+ switch (mm) {
+ case MapMode::Repeat:
+ return GL_REPEAT;
+ case MapMode::Clamp:
+ return GL_CLAMP_TO_EDGE;
+ case MapMode::Mirror:
+ return GL_MIRRORED_REPEAT;
+ default:
+ throw std::domain_error("Invalid MapMode value");
+ }
+}
+
Texture::Texture(const std::filesystem::path & fileName, TextureOptions to) :
Texture {Image {Resource::mapPath(fileName).c_str(), STBI_rgb_alpha}, to}
{
@@ -29,8 +45,8 @@ Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOption glBindTexture(type, m_texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexParameteri(type, GL_TEXTURE_WRAP_S, to.wrap);
- glTexParameteri(type, GL_TEXTURE_WRAP_T, to.wrap);
+ glTexParameteri(type, GL_TEXTURE_WRAP_S, TextureOptions::glMapMode(to.wrapU));
+ glTexParameteri(type, GL_TEXTURE_WRAP_T, TextureOptions::glMapMode(to.wrapV));
glTexParameteri(type, GL_TEXTURE_MIN_FILTER, to.minFilter);
glTexParameteri(type, GL_TEXTURE_MAG_FILTER, to.magFilter);
@@ -44,44 +60,91 @@ Texture::bind(GLenum unit) const glBindTexture(type, m_texture);
}
-void
-Texture::save(const glTexture & texture, GLenum format, GLenum type, const glm::ivec2 & size, unsigned short channels,
- const char * path, short tgaFormat)
+glm::ivec2
+Texture::getSize(const glTexture & texture)
{
- using TGAHead = std::array<short, 9>;
+ glm::ivec2 size;
+ glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_WIDTH, &size.x);
+ glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_HEIGHT, &size.y);
+ return size;
+}
+void
+Texture::save(
+ const glTexture & texture, GLenum format, GLenum type, uint8_t channels, const char * path, uint8_t tgaFormat)
+{
+ const auto size = getSize(texture);
const size_t dataSize = (static_cast<size_t>(size.x * size.y * channels));
const size_t fileSize = dataSize + sizeof(TGAHead);
filesystem::fh out {path, O_RDWR | O_CREAT, 0660};
out.truncate(fileSize);
auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED);
- *tga.get<TGAHead>() = {0, tgaFormat, 0, 0, 0, 0, static_cast<short>(size.x), static_cast<short>(size.y),
- static_cast<short>(8 * channels)};
+ *tga.get<TGAHead>() = {
+ .format = tgaFormat,
+ .size = size,
+ .pixelDepth = static_cast<uint8_t>(8 * channels),
+ };
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
glGetTextureImage(texture, 0, format, type, static_cast<GLsizei>(dataSize), tga.get<TGAHead>() + 1);
tga.msync(MS_ASYNC);
}
void
-Texture::save(const glm::ivec2 & size, const char * path) const
+Texture::save(const char * path) const
{
- save(m_texture, GL_BGR, GL_UNSIGNED_BYTE, size, 3, path, 2);
+ save(m_texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
}
void
-Texture::save(const glTexture & texture, const glm::ivec2 & size, const char * path)
+Texture::save(const glTexture & texture, const char * path)
{
- save(texture, GL_BGR, GL_UNSIGNED_BYTE, size, 3, path, 2);
+ save(texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
}
void
-Texture::saveDepth(const glTexture & texture, const glm::ivec2 & size, const char * path)
+Texture::saveDepth(const glTexture & texture, const char * path)
{
- save(texture, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, size, 1, path, 3);
+ save(texture, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 1, path, 3);
}
void
-Texture::saveNormal(const glTexture & texture, const glm::ivec2 & size, const char * path)
+Texture::saveNormal(const glTexture & texture, const char * path)
+{
+ save(texture, GL_BGR, GL_BYTE, 3, path, 2);
+}
+
+TextureAtlas::TextureAtlas(GLsizei width, GLsizei height, GLuint count) : Texture(width, height, nullptr, {})
+{
+ glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);
+
+ glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA16UI, 2, static_cast<GLsizei>(count), 0, GL_RGBA_INTEGER,
+ GL_UNSIGNED_BYTE, nullptr);
+}
+
+void
+TextureAtlas::bind(GLenum unit) const
+{
+ Texture::bind(unit);
+ glActiveTexture(unit + 1);
+ glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);
+}
+
+GLuint
+TextureAtlas::add(glm::ivec2 position, glm::ivec2 size, void * data, TextureOptions to)
{
- save(texture, GL_BGR, GL_BYTE, size, 3, path, 2);
+ glTextureSubImage2D(m_texture, 0, position.x, position.y, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ struct Material {
+ glm::vec<2, uint16_t> position, size;
+ TextureOptions::MapMode wrapU;
+ TextureOptions::MapMode wrapV;
+ } material {position, size, to.wrapU, to.wrapV};
+ static_assert(sizeof(Material) <= 32);
+ glTextureSubImage2D(m_atlas, 0, 0, static_cast<GLsizei>(used), 2, 1, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, &material);
+ return ++used;
}
diff --git a/gfx/models/texture.h b/gfx/models/texture.h index ffc9a4a..86e76a0 100644 --- a/gfx/models/texture.h +++ b/gfx/models/texture.h @@ -9,13 +9,23 @@ class Image;
struct TextureOptions {
- GLint wrap {GL_REPEAT};
+ enum class MapMode {
+ Repeat,
+ Clamp,
+ Mirror,
+ Decal,
+ };
+ MapMode wrapU {MapMode::Repeat}, wrapV {MapMode::Repeat};
GLint minFilter {GL_LINEAR}, magFilter {GL_LINEAR};
GLenum type {GL_TEXTURE_2D};
+ static GLint glMapMode(MapMode);
};
class Texture {
public:
+ virtual ~Texture() = default;
+ DEFAULT_MOVE_NO_COPY(Texture);
+
explicit Texture(const std::filesystem::path & fileName, TextureOptions = {});
explicit Texture(const Image & image, TextureOptions = {});
explicit Texture(GLsizei width, GLsizei height, TextureOptions = {});
@@ -23,17 +33,29 @@ public: static Cache<Texture, std::filesystem::path> cachedTexture;
- void bind(GLenum unit = GL_TEXTURE0) const;
+ virtual 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);
+ void save(const char * path) const;
+ static void save(const glTexture &, const char * path);
+ static void saveDepth(const glTexture &, const char * path);
+ static void saveNormal(const glTexture &, const char * path);
-private:
- static void save(const glTexture &, GLenum, GLenum, const glm::ivec2 & size, unsigned short channels,
- const char * path, short tgaFormat);
+protected:
+ static void save(const glTexture &, GLenum, GLenum, uint8_t channels, const char * path, uint8_t tgaFormat);
+ static glm::ivec2 getSize(const glTexture &);
glTexture m_texture;
GLenum type;
};
+
+class TextureAtlas : public Texture {
+public:
+ TextureAtlas(GLsizei width, GLsizei height, GLuint count);
+
+ void bind(GLenum unit = GL_TEXTURE0) const override;
+ GLuint add(glm::ivec2 position, glm::ivec2 size, void * data, TextureOptions = {});
+
+private:
+ glTexture m_atlas;
+ GLuint used {};
+};
diff --git a/gfx/models/tga.h b/gfx/models/tga.h new file mode 100644 index 0000000..1f400ef --- /dev/null +++ b/gfx/models/tga.h @@ -0,0 +1,14 @@ +#pragma once + +#include <cstdint> +#include <glm/vec2.hpp> + +struct TGAHead { + uint8_t idLength {}, colorMapType {}, format {}; + uint16_t __attribute__((packed)) colorMapFirst {}, colorMapLength {}; + uint8_t colorMapEntrySize {}; + glm::vec<2, uint16_t> origin {}, size {}; + uint8_t pixelDepth {}; + uint8_t descriptor {}; +}; +static_assert(sizeof(TGAHead) == 18); diff --git a/gfx/models/vertex.hpp b/gfx/models/vertex.hpp index 64ec3d0..181e7e7 100644 --- a/gfx/models/vertex.hpp +++ b/gfx/models/vertex.hpp @@ -1,12 +1,14 @@ #pragma once +#include <GL/glew.h> #include <glm/glm.hpp> 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)} + constexpr Vertex(glm::vec3 pos, glm::vec2 texCoord, glm::vec3 normal, glm::vec4 colour = {}, GLuint material = 0) : + pos {std::move(pos)}, texCoord {std::move(texCoord)}, normal {std::move(normal)}, colour {std::move(colour)}, + material {material} { } #endif @@ -16,5 +18,6 @@ public: glm::vec3 pos; glm::vec2 texCoord; glm::vec3 normal; - glm::vec4 colour; + glm::vec4 colour {}; + GLuint material {}; }; diff --git a/glsl.jam b/glsl.jam new file mode 100644 index 0000000..87dee61 --- /dev/null +++ b/glsl.jam @@ -0,0 +1,36 @@ +import type : register ; +import type : type ; +import generators : register-standard ; +import scanner : register ; + +type.register GL_VERTEX_SHADER : vs ; +type.register GL_GEOMETRY_SHADER : gs ; +type.register GL_FRAGMENT_SHADER : fs ; + +generators.register-standard glsl.embed : GL_VERTEX_SHADER : CPP(vs-%) H(vs-%) ; +generators.register-standard glsl.embed : GL_GEOMETRY_SHADER : CPP(gs-%) H(gs-%) ; +generators.register-standard glsl.embed : GL_FRAGMENT_SHADER : CPP(fs-%) H(fs-%) ; + +class m4-scanner : common-scanner { + rule pattern ( ) { + return "s?include\\(`([^']*)'\\)" ; + } +} + +scanner.register m4-scanner : include ; + +type.set-scanner GL_VERTEX_SHADER : m4-scanner ; +type.set-scanner GL_GEOMETRY_SHADER : m4-scanner ; +type.set-scanner GL_FRAGMENT_SHADER : m4-scanner ; + +actions glsl.embed +{ + m4 -I$(2:D) -DNAME=$(2:B) -DTYPE=$(2:S) > $(1[2]) lib/embed-glsl.h.m4 + m4 -I$(2:D) -DSOURCE=$(2) -DNAME=$(2:B) -DTYPE=$(2:S) -DGLTYPE=$(OPTIONS) > $(1[1]) lib/embed-glsl.cpp.m4 +} + +rule glsl.embed ( targets * : sources * : properties * ) +{ + DEPENDS $(targets) : lib/embed-glsl.h.m4 lib/embed-glsl.cpp.m4 ; + OPTIONS on $(targets) = [ type.type $(sources) ] ; +} diff --git a/lib/collections.hpp b/lib/collections.hpp index 59cec6f..8f5a43d 100644 --- a/lib/collections.hpp +++ b/lib/collections.hpp @@ -65,11 +65,36 @@ operator*=(IterableCollection auto & in, auto && f) } template<template<typename...> typename C, typename... T> + requires IterableCollection<C<T...>> [[nodiscard]] constexpr auto operator*(const C<T...> & in, auto && f) { - C<decltype(f(in[0]))> out; + C<decltype(f(*in.begin()))> out; + if constexpr (requires { out.reserve(in.size()); }) { + out.reserve(in.size()); + } + + std::transform(in.begin(), in.end(), std::inserter(out, out.end()), f); + return out; +} + +template<typename T, std::size_t N> +[[nodiscard]] constexpr auto +operator*(const std::span<T, N> in, auto && f) +{ + std::array<decltype(f(std::declval<T>())), N> out; + + std::transform(in.begin(), in.end(), out.begin(), f); + return out; +} + +template<typename T> +[[nodiscard]] constexpr auto +operator*(const std::span<T, std::dynamic_extent> in, auto && f) +{ + std::vector<decltype(f(std::declval<T>()))> out; + out.reserve(in.size()); std::transform(in.begin(), in.end(), std::inserter(out, out.end()), f); return out; } diff --git a/lib/gl_traits.hpp b/lib/gl_traits.hpp index d140de9..e2e689d 100644 --- a/lib/gl_traits.hpp +++ b/lib/gl_traits.hpp @@ -6,37 +6,50 @@ #include <glm/fwd.hpp> template<typename T> struct gl_traits; -template<> struct gl_traits<glm::f32> { - static constexpr GLenum type {GL_FLOAT}; +struct gl_traits_base { static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::f64> { +struct gl_traits_float : public gl_traits_base { + static constexpr auto vertexAttribFunc { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer); + }}; +}; +struct gl_traits_longfloat : public gl_traits_base { + static constexpr auto vertexAttribFunc { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + glVertexAttribLPointer(index, size, type, stride, pointer); + }}; +}; +struct gl_traits_integer : public gl_traits_base { + static constexpr auto vertexAttribFunc { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + glVertexAttribIPointer(index, size, type, stride, pointer); + }}; +}; +template<> struct gl_traits<glm::f32> : public gl_traits_float { + static constexpr GLenum type {GL_FLOAT}; +}; +template<> struct gl_traits<glm::f64> : public gl_traits_longfloat { static constexpr GLenum type {GL_DOUBLE}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::int8> { +template<> struct gl_traits<glm::int8> : public gl_traits_integer { static constexpr GLenum type {GL_BYTE}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::int16> { +template<> struct gl_traits<glm::int16> : public gl_traits_integer { static constexpr GLenum type {GL_SHORT}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::int32> { +template<> struct gl_traits<glm::int32> : public gl_traits_integer { static constexpr GLenum type {GL_INT}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::uint8> { +template<> struct gl_traits<glm::uint8> : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_BYTE}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::uint16> { +template<> struct gl_traits<glm::uint16> : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_SHORT}; - static constexpr GLint size {1}; }; -template<> struct gl_traits<glm::uint32> { +template<> struct gl_traits<glm::uint32> : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_INT}; - static constexpr GLint size {1}; }; template<typename T, std::size_t S> struct gl_traits<std::array<T, S>> : public gl_traits<T> { diff --git a/lib/work.h b/lib/work.h deleted file mode 100644 index c780e13..0000000 --- a/lib/work.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include <special_members.hpp> - -class Work { -public: - virtual ~Work() = default; - NO_COPY(Work); - NO_MOVE(Work); - - virtual void doWork() = 0; -}; diff --git a/lib/worker.cpp b/lib/worker.cpp index fd255c7..45fb6df 100644 --- a/lib/worker.cpp +++ b/lib/worker.cpp @@ -1,27 +1,24 @@ #include "worker.h" -#if __cpp_lib_semaphore -# include "work.h" -# include <algorithm> -# include <iterator> -# include <mutex> +#include <algorithm> +#include <iterator> +#include <mutex> + +Worker Worker::instance; Worker::Worker() : todoLen {0} { std::generate_n(std::back_inserter(threads), std::thread::hardware_concurrency(), [this]() { - return std::thread {&Worker::worker, this}; + return std::jthread {&Worker::worker, this}; }); } Worker::~Worker() { todoLen.release(std::thread::hardware_concurrency()); - std::for_each(threads.begin(), threads.end(), [](auto & th) { - th.join(); - }); } void -Worker::addWork(WorkPtr j) +Worker::addWorkPtr(WorkPtr j) { std::lock_guard<std::mutex> lck {todoMutex}; todoLen.release(); @@ -45,4 +42,24 @@ Worker::worker() j->doWork(); } } -#endif + +void +Worker::assist() +{ + auto job = [this]() { + using namespace std::chrono_literals; + if (todoLen.try_acquire_for(100us)) { + if (std::lock_guard<std::mutex> lck {todoMutex}; todo.size()) { + WorkPtr x = std::move(todo.front()); + if (x) { + todo.pop_front(); + } + return x; + } + } + return WorkPtr {}; + }; + if (auto j = job()) { + j->doWork(); + } +} diff --git a/lib/worker.h b/lib/worker.h index 7cd06f9..d9a5a6f 100644 --- a/lib/worker.h +++ b/lib/worker.h @@ -1,60 +1,122 @@ #pragma once +#include <deque> +#include <functional> +#include <future> +#include <memory> +#include <mutex> +#include <semaphore> +#include <special_members.hpp> +#include <thread> #include <utility> -class Work; - -#if __cpp_lib_semaphore -# include <deque> -# include <memory> -# include <mutex> -# include <semaphore> -# include <special_members.hpp> -# include <thread> -# include <vector> +#include <vector> class Worker { public: + class WorkItem { + protected: + WorkItem(Worker * worker) : worker {worker} { } + virtual ~WorkItem() = default; + NO_MOVE(WorkItem); + NO_COPY(WorkItem); + + void + assist() const + { + worker->assist(); + } + Worker * worker; + + public: + virtual void doWork() = 0; + }; + + template<typename T> class WorkItemT : public WorkItem { + public: + T + get() + { + using namespace std::chrono_literals; + while (future.wait_for(0s) == std::future_status::timeout) { + assist(); + } + return future.get(); + } + + protected: + WorkItemT(Worker * worker) : WorkItem {worker} { } + + std::promise<T> promise; + std::future<T> future {promise.get_future()}; + friend Worker; + }; + + template<typename... Params> + static auto + addWork(Params &&... params) + { + return instance.addWorkImpl(std::forward<Params>(params)...); + } + template<typename T> using WorkPtrT = std::shared_ptr<WorkItemT<T>>; + +private: + template<typename T, typename... Params> class WorkItemTImpl : public WorkItemT<T> { + public: + WorkItemTImpl(Worker * worker, Params &&... params) : + WorkItemT<T> {worker}, params {std::forward<Params>(params)...} + { + } + + private: + void + doWork() override + { + try { + if constexpr (std::is_void_v<T>) { + std::apply(std::invoke<Params &...>, params); + WorkItemT<T>::promise.set_value(); + } + else { + WorkItemT<T>::promise.set_value(std::apply(std::invoke<Params &...>, params)); + } + } + catch (...) { + WorkItemT<T>::promise.set_exception(std::current_exception()); + } + } + + std::tuple<Params...> params; + }; + Worker(); ~Worker(); NO_COPY(Worker); NO_MOVE(Worker); - using WorkPtr = std::unique_ptr<Work>; + using WorkPtr = std::shared_ptr<WorkItem>; - template<typename T, typename... Params> - void - addWork(Params &&... params) - requires std::is_base_of_v<Work, T> + template<typename... Params> + auto + addWorkImpl(Params &&... params) { - addWork(std::make_unique<T>(std::forward<Params>(params)...)); + using T = decltype(std::invoke(std::forward<Params>(params)...)); + auto work = std::make_shared<WorkItemTImpl<T, Params...>>(this, std::forward<Params>(params)...); + addWorkPtr(work); + return work; } - void addWork(WorkPtr w); - -private: + void addWorkPtr(WorkPtr w); void worker(); + void assist(); - using Threads = std::vector<std::thread>; + using Threads = std::vector<std::jthread>; using ToDo = std::deque<WorkPtr>; - Threads threads; ToDo todo; std::counting_semaphore<16> todoLen; std::mutex todoMutex; -}; - -#else + Threads threads; -class Worker { -public: - template<typename T, typename... Params> - void - addWork(Params &&... params) - requires std::is_base_of_v<Work, T> - { - T(std::forward<Params>(params)...).doWork(); - } + static Worker instance; }; - -#endif diff --git a/res/assetTemplate.m4 b/res/assetTemplate.m4 new file mode 100644 index 0000000..8750c42 --- /dev/null +++ b/res/assetTemplate.m4 @@ -0,0 +1,5 @@ + <asset p.typeid="TYPE" id="ID" name="ID"> + <bodyMesh id="body"> + <use type="ID"/> + </bodyMesh> + </asset> diff --git a/res/brush47.mtl b/res/brush47.mtl deleted file mode 100644 index a4ba512..0000000 --- a/res/brush47.mtl +++ /dev/null @@ -1,13 +0,0 @@ -# Blender MTL File: 'brush47.blend' -# Material Count: 1 - -newmtl Brush47 -Ns 225.000000 -Ka 1.000000 1.000000 1.000000 -Kd 0.800000 0.800000 0.800000 -Ks 0.500000 0.500000 0.500000 -Ke 0.000000 0.000000 0.000000 -Ni 1.450000 -d 1.000000 -illum 2 -map_Kd brush47.png diff --git a/res/brush47.obj b/res/brush47.obj deleted file mode 100644 index 8fcb2b1..0000000 --- a/res/brush47.obj +++ /dev/null @@ -1,260 +0,0 @@ -# Blender v2.91.0 OBJ File: 'brush47.blend' -# www.blender.org -mtllib brush47.mtl -o Body -v -1.345000 1.200000 -9.690000 -v -1.345000 1.200000 9.690000 -v 1.345000 1.200000 -9.690000 -v 1.345000 1.200000 9.690000 -v -0.991447 3.900000 -8.536446 -v -1.345000 3.546447 -9.440000 -v -0.991447 3.900000 8.536446 -v -1.345000 3.546447 9.440000 -v 0.991447 3.900000 -8.536446 -v 1.345000 3.546447 -9.440000 -v 0.991447 3.900000 8.536446 -v 1.345000 3.546447 9.440000 -v -1.345000 2.723224 9.690000 -v 1.345000 2.723224 9.690000 -v 1.345000 2.723224 -9.690000 -v -1.345000 2.723224 -9.690000 -v 1.340000 0.500000 9.600000 -v -1.340000 0.500000 9.600000 -v -1.340000 1.200000 9.690000 -v -1.340000 0.500000 -9.600000 -v 1.340000 0.500000 -9.600000 -v -1.300000 0.200000 -2.250000 -v -1.300000 1.200000 -2.250000 -v -1.300000 0.200000 2.250000 -v -1.300000 1.200000 2.250000 -v 1.300000 0.200000 -2.250000 -v 1.300000 1.200000 -2.250000 -v 1.300000 0.200000 2.250000 -v 1.300000 1.200000 2.250000 -v 1.345000 1.200000 8.236500 -v 1.345000 1.200000 -8.236500 -v -1.345000 1.200000 -8.236500 -v -1.345000 1.200000 8.236500 -vt 0.025174 0.662500 -vt 0.025174 0.831250 -vt 0.037326 0.925000 -vt 0.957465 0.927083 -vt 0.971354 0.833333 -vt 0.971354 0.662500 -vt 0.900391 0.662500 -vt 0.096137 0.662500 -vt 0.025174 0.662500 -vt 0.025174 0.831250 -vt 0.037326 0.925000 -vt 0.957465 0.927083 -vt 0.971354 0.833333 -vt 0.971354 0.662500 -vt 0.900391 0.662500 -vt 0.096137 0.662500 -vt 0.605035 0.456250 -vt 0.605035 0.581250 -vt 0.359375 0.581250 -vt 0.359375 0.456250 -vt 0.605035 0.581250 -vt 0.605035 0.456250 -vt 0.359375 0.581250 -vt 0.359375 0.456250 -vt 0.026042 0.593750 -vt 0.971354 0.593750 -vt 0.026042 0.593750 -vt 0.971354 0.593750 -vt 0.088542 0.089583 -vt 0.088542 0.206250 -vt 0.918403 0.206250 -vt 0.918403 0.089583 -vt 0.926215 0.527083 -vt 0.926215 0.612500 -vt 0.834201 0.612500 -vt 0.834201 0.527083 -vt 0.083333 0.989583 -vt 0.913194 0.989583 -vt 0.037326 0.927083 -vt 0.152778 0.364583 -vt 0.197049 0.364583 -vt 0.204861 0.310417 -vt 0.144965 0.310417 -vt 0.083333 0.989583 -vt 0.913194 0.989583 -vt 0.037326 0.927083 -vt 0.152778 0.364583 -vt 0.197049 0.364583 -vt 0.204861 0.310417 -vt 0.144965 0.310417 -vt 0.834201 0.527083 -vt 0.834201 0.612500 -vt 0.927083 0.612500 -vt 0.927083 0.527083 -vt 0.114583 0.293750 -vt 0.114583 0.414583 -vt 0.010417 0.414583 -vt 0.010417 0.293750 -vt 0.010417 0.293750 -vt 0.010417 0.414583 -vt 0.114583 0.414583 -vt 0.114583 0.293750 -vt 0.043403 0.014583 -vt 0.002604 0.014583 -vt 0.002604 0.081250 -vt 0.043403 0.081250 -vt 0.043403 0.014583 -vt 0.002604 0.014583 -vt 0.002604 0.081250 -vt 0.043403 0.081250 -vn -1.0000 0.0000 0.0000 -vn 1.0000 0.0000 0.0000 -vn 0.0000 0.0000 -1.0000 -vn 0.0000 0.0000 1.0000 -vn -1.0000 0.0071 0.0000 -vn 1.0000 0.0071 -0.0000 -vn -0.3335 -0.9344 -0.1252 -vn 0.3335 -0.9344 -0.1252 -vn 0.3335 -0.9344 0.1252 -vn -0.3335 -0.9344 0.1252 -vn 0.6755 -0.1083 -0.7294 -vn 0.6276 -0.5509 -0.5500 -vn -0.6276 -0.5509 -0.5500 -vn -0.6755 -0.1083 -0.7294 -vn 0.6276 -0.5509 0.5500 -vn -0.6276 -0.5509 0.5500 -vn -0.6755 -0.1083 0.7294 -vn 0.6755 -0.1083 0.7294 -vn 0.8867 0.0030 -0.4622 -vn -0.6937 0.0482 -0.7186 -vn -0.6937 0.0482 0.7186 -vn 0.6937 0.0482 0.7186 -vn -0.6128 0.1051 -0.7832 -vn 0.6145 0.1049 -0.7819 -vn 0.0000 0.1275 -0.9918 -vn 0.6128 0.1051 0.7832 -vn -0.6128 0.1051 0.7832 -usemtl Brush47 -s off -f 4/1/1 14/2/1 12/3/1 10/4/1 15/5/1 3/6/1 31/7/1 30/8/1 -f 1/9/2 16/10/2 6/11/2 8/12/2 13/13/2 2/14/2 33/15/2 32/16/2 -f 22/17/2 23/18/2 25/19/2 24/20/2 -f 24/20/3 25/19/3 29/21/3 28/22/3 -f 28/22/1 29/21/1 27/23/1 26/24/1 -f 26/24/4 27/23/4 23/18/4 22/17/4 -f 4/1/5 30/8/5 17/25/5 -f 31/7/5 3/6/5 21/26/5 -f 1/9/6 32/16/6 20/27/6 -f 33/15/6 2/14/6 18/28/6 -s 1 -f 11/29/7 7/30/8 5/31/9 9/32/10 -f 13/33/11 8/34/12 12/35/13 14/36/14 -f 5/37/9 7/38/8 8/12/12 6/39/15 -f 7/40/8 11/41/7 12/42/13 8/43/12 -f 11/44/7 9/45/10 10/4/16 12/46/13 -f 9/47/10 5/48/9 6/49/15 10/50/16 -f 15/51/17 10/52/16 6/53/15 16/54/18 -f 2/55/19 13/56/11 14/57/14 4/58/20 -f 3/59/21 15/60/17 16/61/18 1/62/22 -f 17/63/23 18/64/24 19/65/25 4/66/20 -f 20/67/26 21/68/27 3/69/21 1/70/22 -o Bogie2 -v -0.717500 0.000000 -2.700000 -v -0.717500 0.000000 -9.300000 -v -0.717500 1.200000 -2.700000 -v 0.717500 1.200000 -2.700000 -v 0.717500 0.000000 -2.700000 -v 0.717500 0.000000 -9.300000 -v 1.017500 1.200000 -2.700000 -v 1.017500 0.000000 -2.700000 -v 1.017500 0.000000 -9.300000 -v -1.017500 0.000000 -9.300000 -v -1.017501 0.000000 -2.700000 -v -1.017501 1.200000 -2.700000 -v -0.717500 0.654015 -9.300000 -v -0.717500 1.200000 -8.236500 -v -0.717500 0.661560 -9.285304 -v 0.717500 1.200000 -8.236500 -v 0.717500 0.654015 -9.300000 -v 1.017500 1.200000 -8.236500 -v 1.017500 0.654015 -9.300000 -v -1.017500 0.654015 -9.300000 -v -1.017500 1.200000 -8.236500 -vt 0.342014 0.591667 -vt 0.342014 0.435417 -vt 0.006076 0.435417 -vt 0.006076 0.520575 -vt 0.006824 0.521557 -vt 0.060208 0.591667 -vt 0.006076 0.435417 -vt 0.342014 0.435417 -vt 0.342014 0.591667 -vt 0.060208 0.591667 -vt 0.006076 0.520575 -vt 0.217014 0.270833 -vt 0.552951 0.270833 -vt 0.552951 0.427083 -vt 0.271146 0.427083 -vt 0.217014 0.355992 -vt 0.552083 0.429167 -vt 0.552083 0.272917 -vt 0.216146 0.272917 -vt 0.216146 0.358075 -vt 0.270278 0.429167 -vn 1.0000 0.0000 -0.0000 -vn -1.0000 0.0000 0.0000 -usemtl Brush47 -s off -f 36/71/28 34/72/28 35/73/28 46/74/28 48/75/28 47/76/28 -f 39/77/29 38/78/29 37/79/29 49/80/29 50/81/29 -f 42/82/29 41/83/29 40/84/29 51/85/29 52/86/29 -f 45/87/28 44/88/28 43/89/28 53/90/28 54/91/28 -o Bogie1 -v 0.717500 0.000000 2.700000 -v 0.717500 1.200000 2.700000 -v 0.717500 0.000000 9.300000 -v -0.717500 0.000000 2.700000 -v -0.717500 1.200000 2.700000 -v -0.717500 0.000000 9.300000 -v 1.017500 0.000000 2.700001 -v 1.017500 1.200000 2.700001 -v 1.017500 0.000000 9.300000 -v -1.017500 0.000000 2.700000 -v -1.017500 1.200000 2.700000 -v -1.017500 0.000000 9.300000 -v 0.717500 0.654015 9.300000 -v 0.717500 1.200000 8.236500 -v 1.017500 0.654015 9.300000 -v 1.017500 1.200000 8.236500 -v -0.717500 1.200000 8.236500 -v -0.717500 0.661372 9.285670 -v -0.717500 0.654015 9.300000 -v -1.017500 1.200000 8.236500 -v -1.017500 0.654015 9.300000 -vt 0.342014 0.435417 -vt 0.006076 0.435417 -vt 0.006076 0.520575 -vt 0.060208 0.591667 -vt 0.342014 0.591667 -vt 0.006076 0.520575 -vt 0.006076 0.435417 -vt 0.342014 0.435417 -vt 0.342014 0.591667 -vt 0.060208 0.591667 -vt 0.006806 0.521533 -vt 0.552083 0.270833 -vt 0.216146 0.270833 -vt 0.216146 0.355992 -vt 0.270278 0.427083 -vt 0.552083 0.427083 -vt 0.215278 0.355992 -vt 0.215278 0.270833 -vt 0.552083 0.270833 -vt 0.552083 0.427083 -vt 0.269549 0.427083 -vn -1.0000 0.0000 -0.0000 -vn 1.0000 -0.0000 0.0000 -usemtl Brush47 -s off -f 55/92/30 57/93/30 67/94/30 68/95/30 56/96/30 -f 73/97/31 60/98/31 58/99/31 59/100/31 71/101/31 72/102/31 -f 61/103/30 63/104/30 69/105/30 70/106/30 62/107/30 -f 75/108/31 66/109/31 64/110/31 65/111/31 74/112/31 diff --git a/res/brush47.png b/res/brush47.png Binary files differdeleted file mode 100644 index 3d435d6..0000000 --- a/res/brush47.png +++ /dev/null diff --git a/res/foliage.xml b/res/foliage.xml new file mode 100644 index 0000000..5345b39 --- /dev/null +++ b/res/foliage.xml @@ -0,0 +1,159 @@ +<?xml version="1.0"?> +<ilt p.id="assetFactory"> + <assimp path="shapespark-low-poly-plants-kit.fbx"/> + <asset p.typeid="Foliage" id="Tree-01-1" name="Tree-01-1"> + <bodyMesh id="body"> + <use type="Tree-01-1"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-01-1" name="Tree-01-1"> + <bodyMesh id="body"> + <use type="Tree-01-1"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-01-2" name="Tree-01-2"> + <bodyMesh id="body"> + <use type="Tree-01-2"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-01-3" name="Tree-01-3"> + <bodyMesh id="body"> + <use type="Tree-01-3"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-01-4" name="Tree-01-4"> + <bodyMesh id="body"> + <use type="Tree-01-4"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-02-1" name="Tree-02-1"> + <bodyMesh id="body"> + <use type="Tree-02-1"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-02-2" name="Tree-02-2"> + <bodyMesh id="body"> + <use type="Tree-02-2"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-02-3" name="Tree-02-3"> + <bodyMesh id="body"> + <use type="Tree-02-3"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-02-4" name="Tree-02-4"> + <bodyMesh id="body"> + <use type="Tree-02-4"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-03-1" name="Tree-03-1"> + <bodyMesh id="body"> + <use type="Tree-03-1"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-03-2" name="Tree-03-2"> + <bodyMesh id="body"> + <use type="Tree-03-2"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-03-3" name="Tree-03-3"> + <bodyMesh id="body"> + <use type="Tree-03-3"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Tree-03-4" name="Tree-03-4"> + <bodyMesh id="body"> + <use type="Tree-03-4"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Hedge-01" name="Hedge-01"> + <bodyMesh id="body"> + <use type="Hedge-01"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Bush-01" name="Bush-01"> + <bodyMesh id="body"> + <use type="Bush-01"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Bush-02" name="Bush-02"> + <bodyMesh id="body"> + <use type="Bush-02"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Bush-03" name="Bush-03"> + <bodyMesh id="body"> + <use type="Bush-03"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Bush-04" name="Bush-04"> + <bodyMesh id="body"> + <use type="Bush-04"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Bush-05" name="Bush-05"> + <bodyMesh id="body"> + <use type="Bush-05"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Clover-01" name="Clover-01"> + <bodyMesh id="body"> + <use type="Clover-01"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Clover-02" name="Clover-02"> + <bodyMesh id="body"> + <use type="Clover-02"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Clover-03" name="Clover-03"> + <bodyMesh id="body"> + <use type="Clover-03"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Clover-04" name="Clover-04"> + <bodyMesh id="body"> + <use type="Clover-04"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Clover-05" name="Clover-05"> + <bodyMesh id="body"> + <use type="Clover-05"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Grass-01" name="Grass-01"> + <bodyMesh id="body"> + <use type="Grass-01"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Grass-02" name="Grass-02"> + <bodyMesh id="body"> + <use type="Grass-02"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Grass-03" name="Grass-03"> + <bodyMesh id="body"> + <use type="Grass-03"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Flowers-02" name="Flowers-02"> + <bodyMesh id="body"> + <use type="Flowers-02"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Flowers-04" name="Flowers-04"> + <bodyMesh id="body"> + <use type="Flowers-04"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Flowers-01" name="Flowers-01"> + <bodyMesh id="body"> + <use type="Flowers-01"/> + </bodyMesh> + </asset> + <asset p.typeid="Foliage" id="Flowers-03" name="Flowers-03"> + <bodyMesh id="body"> + <use type="Flowers-03"/> + </bodyMesh> + </asset> +</ilt> diff --git a/res/shapespark-low-poly-plants-kit.fbx b/res/shapespark-low-poly-plants-kit.fbx Binary files differnew file mode 100644 index 0000000..fe87c0c --- /dev/null +++ b/res/shapespark-low-poly-plants-kit.fbx diff --git a/test/Jamfile.jam b/test/Jamfile.jam index bf1c408..b0eed5e 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -44,7 +44,6 @@ project : requirements lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] ; run test-collection.cpp ; -run test-obj.cpp ; run test-maths.cpp ; run test-lib.cpp ; run test-geo.cpp ; @@ -52,11 +51,12 @@ run test-network.cpp ; 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-render.cpp : -- : test-assetFactory : <library>test ; run test-glContextBhvr.cpp ; -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 ; +run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt ] : <library>test ; +run perf-assetFactory.cpp : -- : test-assetFactory : <library>benchmark <library>test ; +run perf-persistence.cpp : -- : test-persistence : <library>benchmark <library>test ; +run test-worker.cpp ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/perf-assetFactory.cpp b/test/perf-assetFactory.cpp index c90ac52..147e4ba 100644 --- a/test/perf-assetFactory.cpp +++ b/test/perf-assetFactory.cpp @@ -14,6 +14,17 @@ brush47xml_load(benchmark::State & state) } } +static void +foliagexml_load(benchmark::State & state) +{ + TestMainWindow window; + + for (auto _ : state) { + benchmark::DoNotOptimize(AssetFactory::loadXML(RESDIR "/foliage.xml")); + } +} + BENCHMARK(brush47xml_load); +BENCHMARK(foliagexml_load); BENCHMARK_MAIN(); diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 54168aa..3d79213 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -8,6 +8,8 @@ #include "assetFactory/assetFactory.h" #include "assetFactory/object.h" #include "assetFactory/texturePacker.h" +#include "game/scenary/foliage.h" +#include "game/scenary/plant.h" #include "game/vehicles/railVehicle.h" #include "game/vehicles/railVehicleClass.h" #include "gfx/gl/sceneRenderer.h" @@ -30,7 +32,7 @@ public: glDisable(GL_DEBUG_OUTPUT); auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); std::filesystem::create_directories(outpath.parent_path()); - Texture::save(outImage, size, outpath.c_str()); + Texture::save(outImage, outpath.c_str()); } void content(const SceneShader & shader) const override @@ -96,8 +98,30 @@ BOOST_AUTO_TEST_CASE(brush47xml, *boost::unit_test::timeout(5)) render(); } + +BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) +{ + auto mf = AssetFactory::loadXML(RESDIR "/foliage.xml"); + BOOST_REQUIRE(mf); + auto tree_01_1 = mf->assets.at("Tree-01-1"); + BOOST_REQUIRE(tree_01_1); + auto tree_01_1_f = std::dynamic_pointer_cast<Foliage>(tree_01_1); + BOOST_REQUIRE(tree_01_1_f); + + auto plant = std::make_shared<Plant>(tree_01_1_f, Location {{-2, 2, 0}, {}}); + objects.objects.push_back(plant); + + render(5); +} BOOST_AUTO_TEST_SUITE_END(); +BOOST_AUTO_TEST_CASE(loadall) +{ + const auto assets = AssetFactory::loadAll(RESDIR); + BOOST_CHECK(assets.at("brush-47")); + BOOST_CHECK(assets.at("Tree-01-1")); +} + template<typename T> using InOut = std::tuple<T, T>; BOOST_DATA_TEST_CASE(normalizeColourName, boost::unit_test::data::make<InOut<std::string>>({ @@ -157,7 +181,7 @@ BOOST_AUTO_TEST_CASE(texturePacker_many, *boost::unit_test::timeout(5)) BOOST_CHECK_EQUAL(totalSize, TexturePacker::area(result.second)); } -BOOST_AUTO_TEST_CASE(texturePacker_many_random, *boost::unit_test::timeout(5)) +BOOST_AUTO_TEST_CASE(texturePacker_many_random, *boost::unit_test::timeout(15)) { std::vector<TexturePacker::Image> images(2048); std::mt19937 gen(std::random_device {}()); diff --git a/test/test-obj.cpp b/test/test-obj.cpp deleted file mode 100644 index e6e725d..0000000 --- a/test/test-obj.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#define BOOST_TEST_MODULE test_obj - -#include <boost/test/unit_test.hpp> - -#include <gfx/models/obj.h> -#include <gfx/models/vertex.hpp> -#include <glm/glm.hpp> -#include <map> -#include <memory> -#include <utility> -#include <vector> - -BOOST_AUTO_TEST_CASE(objparse) -{ - const ObjParser op {RESDIR "/brush47.obj"}; - BOOST_CHECK_EQUAL(75, op.vertices.size()); - BOOST_CHECK_EQUAL(112, op.texCoords.size()); - BOOST_CHECK_EQUAL(31, op.normals.size()); - BOOST_CHECK_EQUAL(3, op.objects.size()); - const auto & object {op.objects.front()}; - BOOST_CHECK_EQUAL("Body", object.first); - BOOST_CHECK_EQUAL(21, object.second.size()); - BOOST_CHECK_EQUAL(8, object.second[0].size()); - BOOST_CHECK_EQUAL(8, object.second[1].size()); - BOOST_CHECK_EQUAL(4, object.second[12].size()); -} - -BOOST_AUTO_TEST_CASE(create_meshes) -{ - const ObjParser op {RESDIR "/brush47.obj"}; - const auto ms = op.createMeshData(); - BOOST_REQUIRE_EQUAL(3, ms.size()); - BOOST_REQUIRE_EQUAL("Body", ms.begin()->first); - const auto & o = ms.at("Body"); - BOOST_REQUIRE_EQUAL(88, o.first.size()); - const auto & v = o.first.front(); - BOOST_REQUIRE_CLOSE(-1.345, v.pos.x, 1); - BOOST_REQUIRE_EQUAL(138, o.second.size()); -} diff --git a/test/test-render.cpp b/test/test-render.cpp index 7771760..45acab5 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -6,8 +6,10 @@ #include <boost/test/data/test_case.hpp> #include <boost/test/unit_test.hpp> +#include <assetFactory/assetFactory.h> #include <game/geoData.h> #include <game/terrain.h> +#include <game/vehicles/railVehicle.h> #include <game/vehicles/railVehicleClass.h> #include <gfx/gl/sceneRenderer.h> #include <gfx/models/texture.h> @@ -19,18 +21,30 @@ #include <ui/window.h> class TestScene : public SceneProvider { - RailVehicleClass train {"brush47"}; + std::shared_ptr<RailVehicle> train1, train2; + Terrain terrain {[]() { auto gd = std::make_shared<GeoData>(GeoData::Limits {{0, 0}, {100, 100}}); gd->generateRandom(); return gd; }()}; + +public: + TestScene() + { + const auto assetFactory = AssetFactory::loadXML(RESDIR "/brush47.xml"); + const auto brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>(assetFactory->assets.at("brush-47")); + train1 = std::make_shared<RailVehicle>(brush47rvc); + train1->location.pos = {52, 50, 2}; + train2 = std::make_shared<RailVehicle>(brush47rvc); + train2->location.pos = {52, 30, 2}; + } void content(const SceneShader & shader) const override { terrain.render(shader); - train.render(shader, Location {{52, 50, 2}}, {Location {{52, 56, 2}}, Location {{52, 44, 2}}}); - train.render(shader, Location {{52, 30, 2}}, {Location {{52, 36, 2}}, Location {{52, 24, 2}}}); + train1->render(shader); + train2->render(shader); } void lights(const SceneShader &) const override @@ -40,8 +54,8 @@ class TestScene : public SceneProvider { shadows(const ShadowMapper & shadowMapper) const override { terrain.shadows(shadowMapper); - train.shadows(shadowMapper, Location {{52, 50, 2}}, {Location {{52, 56, 2}}, Location {{52, 44, 2}}}); - train.shadows(shadowMapper, Location {{52, 30, 2}}, {Location {{52, 36, 2}}, Location {{52, 24, 2}}}); + train1->shadows(shadowMapper); + train2->shadows(shadowMapper); } }; @@ -73,7 +87,7 @@ BOOST_AUTO_TEST_CASE(basic) const TestScene scene; ss.render(scene); glDisable(GL_DEBUG_OUTPUT); - Texture::save(outImage, size, "/tmp/basic.tga"); + Texture::save(outImage, "/tmp/basic.tga"); } BOOST_AUTO_TEST_CASE(pointlight) @@ -101,7 +115,7 @@ BOOST_AUTO_TEST_CASE(pointlight) const PointLightScene scene; ss.render(scene); glDisable(GL_DEBUG_OUTPUT); - Texture::save(outImage, size, "/tmp/pointlight.tga"); + Texture::save(outImage, "/tmp/pointlight.tga"); } BOOST_AUTO_TEST_CASE(spotlight) @@ -128,7 +142,7 @@ BOOST_AUTO_TEST_CASE(spotlight) const PointLightScene scene; ss.render(scene); glDisable(GL_DEBUG_OUTPUT); - Texture::save(outImage, size, "/tmp/spotlight.tga"); + Texture::save(outImage, "/tmp/spotlight.tga"); } BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/test-worker.cpp b/test/test-worker.cpp new file mode 100644 index 0000000..76b7138 --- /dev/null +++ b/test/test-worker.cpp @@ -0,0 +1,104 @@ +#define BOOST_TEST_MODULE test_worker + +#include "testHelpers.h" +#include <boost/test/unit_test.hpp> +#include <set> +#include <stream_support.hpp> +#include <worker.h> + +uint32_t +workCounter() +{ + static std::atomic_uint32_t n; + usleep(1000); + return n++; +} + +void +workVoid() +{ + usleep(1000); +} + +void +workFail() +{ + usleep(1000); + throw std::runtime_error {"test"}; +} + +BOOST_AUTO_TEST_CASE(basic_slow_counter) +{ + std::vector<Worker::WorkPtrT<uint32_t>> ps; + for (int i {}; i < 30; ++i) { + ps.push_back(Worker::addWork(workCounter)); + } + std::set<uint32_t> out; + std::transform(ps.begin(), ps.end(), std::inserter(out, out.end()), [](auto && p) { + return p->get(); + }); + BOOST_REQUIRE_EQUAL(out.size(), ps.size()); + BOOST_CHECK_EQUAL(*out.begin(), 0); + BOOST_CHECK_EQUAL(*out.rbegin(), ps.size() - 1); +} + +BOOST_AUTO_TEST_CASE(basic_error_handler) +{ + auto workitem = Worker::addWork(workFail); + BOOST_CHECK_THROW(workitem->get(), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(basic_void_work) +{ + auto workitem = Worker::addWork(workVoid); + BOOST_CHECK_NO_THROW(workitem->get()); +} + +BOOST_AUTO_TEST_CASE(lambda_void) +{ + BOOST_CHECK_NO_THROW(Worker::addWork([]() {})->get()); + BOOST_CHECK_NO_THROW(Worker::addWork([](int) {}, 0)->get()); + BOOST_CHECK_NO_THROW(Worker::addWork([](int, int) {}, 0, 0)->get()); +} + +BOOST_AUTO_TEST_CASE(lambda_value) +{ + BOOST_CHECK_EQUAL(1, Worker::addWork([]() { + return 1; + })->get()); + BOOST_CHECK_EQUAL(2, + Worker::addWork( + [](int i) { + return i; + }, + 2) + ->get()); + BOOST_CHECK_EQUAL(3, + Worker::addWork( + [](int i, int j) { + return i + j; + }, + 1, 2) + ->get()); +} + +BOOST_AUTO_TEST_CASE(recursive, *boost::unit_test::timeout(5)) +{ + auto recurse = []() { + std::vector<Worker::WorkPtrT<uint32_t>> ps; + for (int i {}; i < 30; ++i) { + ps.push_back(Worker::addWork(workCounter)); + } + return std::accumulate(ps.begin(), ps.end(), 0U, [](auto && out, auto && p) { + return out += p->get(); + }); + }; + std::vector<Worker::WorkPtrT<uint32_t>> ps; + for (int i {}; i < 30; ++i) { + ps.push_back(Worker::addWork(recurse)); + } + std::set<uint32_t> out; + std::transform(ps.begin(), ps.end(), std::inserter(out, out.end()), [](auto && p) { + return p->get(); + }); +} |