diff options
Diffstat (limited to 'game')
-rw-r--r-- | game/geoData.cpp | 412 | ||||
-rw-r--r-- | game/geoData.h | 131 | ||||
-rw-r--r-- | game/terrain.cpp | 65 |
3 files changed, 365 insertions, 243 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp index ec990ea..7710efe 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -1,215 +1,319 @@ #include "geoData.h" -#include "gfx/image.h" -#include <algorithm> -#include <array> -#include <cmath> -#include <cstddef> +#include <fstream> #include <glm/gtx/intersect.hpp> -#include <initializer_list> -#include <limits> #include <maths.h> -#include <random> -#include <ray.h> -#include <stb/stb_image.h> -#include <stdexcept> -#include <util.h> -GeoData::GeoData(Limits l, float s) : - limit {std::move(l)}, size {(limit.second - limit.first) + 1}, scale {s}, nodes {[this]() { - return (static_cast<std::size_t>(size.x * size.y)); - }()} +GeoData +GeoData::loadFromAsciiGrid(const std::filesystem::path & input) { -} - -void -GeoData::generateRandom() -{ - // We acknowledge this is terrible :) - - // Add hills - std::mt19937 gen(std::random_device {}()); - std::uniform_int_distribution<> rxpos(limit.first.x + 2, limit.second.x - 2), - rypos(limit.first.y + 2, limit.second.y - 2); - std::uniform_int_distribution<> rsize(10, 30); - std::uniform_real_distribution<float> rheight(1000, 3000); - for (int h = 0; h < 500;) { - const glm::ivec2 hpos {rxpos(gen), rypos(gen)}; - const glm::ivec2 hsize {rsize(gen), rsize(gen)}; - if (const auto lim1 = hpos - hsize; lim1.x > limit.first.x && lim1.y > limit.first.y) { - if (const auto lim2 = hpos + hsize; lim2.x < limit.second.x && lim2.y < limit.second.y) { - const auto height = rheight(gen); - const glm::ivec2 hsizesqrd {hsize.x * hsize.x, hsize.y * hsize.y}; - for (auto y = lim1.y; y < lim2.y; y += 1) { - for (auto x = lim1.x; x < lim2.x; x += 1) { - const auto dist {hpos - glm::ivec2 {x, y}}; - const glm::ivec2 distsqrd {dist.x * dist.x, dist.y * dist.y}; - const auto out {ratio(sq(x - hpos.x), sq(hsize.x)) + ratio(sq(y - hpos.y), sq(hsize.y))}; - if (out <= 1.0F) { - auto & node {nodes[at({x, y})]}; - const auto m {1.F / (7.F * out - 8.F) + 1.F}; - node.height += height * m; - } - } - } - h += 1; - } + size_t ncols = 0, nrows = 0, xllcorner = 0, yllcorner = 0, cellsize = 0; + std::map<std::string_view, size_t *> properties { + {"ncols", &ncols}, + {"nrows", &nrows}, + {"xllcorner", &xllcorner}, + {"yllcorner", &yllcorner}, + {"cellsize", &cellsize}, + }; + std::ifstream f {input}; + while (!properties.empty()) { + std::string property; + f >> property; + f >> *properties.at(property); + properties.erase(property); + } + xllcorner *= 1000; + yllcorner *= 1000; + cellsize *= 1000; + std::vector<VertexHandle> vertices; + vertices.reserve(ncols * nrows); + GeoData mesh; + mesh.lowerExtent = {xllcorner, yllcorner, std::numeric_limits<GlobalDistance>::max()}; + mesh.upperExtent = {xllcorner + (cellsize * (ncols - 1)), yllcorner + (cellsize * (nrows - 1)), + std::numeric_limits<GlobalDistance>::min()}; + for (size_t row = 0; row < nrows; ++row) { + for (size_t col = 0; col < ncols; ++col) { + float heightf = 0; + f >> heightf; + const auto height = static_cast<GlobalDistance>(std::round(heightf * 1000.F)); + mesh.upperExtent.z = std::max(mesh.upperExtent.z, height); + mesh.lowerExtent.z = std::min(mesh.lowerExtent.z, height); + vertices.push_back(mesh.add_vertex({xllcorner + (col * cellsize), yllcorner + (row * cellsize), height})); } } -} + if (!f.good()) { + throw std::runtime_error("Couldn't read terrain file"); + } + for (size_t row = 1; row < nrows; ++row) { + for (size_t col = 1; col < ncols; ++col) { + mesh.add_face({ + vertices[ncols * (row - 1) + (col - 1)], + vertices[ncols * (row - 0) + (col - 0)], + vertices[ncols * (row - 0) + (col - 1)], + }); + mesh.add_face({ + vertices[ncols * (row - 1) + (col - 1)], + vertices[ncols * (row - 1) + (col - 0)], + vertices[ncols * (row - 0) + (col - 0)], + }); + } + } + mesh.update_face_normals(); + mesh.update_vertex_normals(); -void -GeoData::loadFromImages(const std::filesystem::path & fileName, float scale_) + return mesh; +}; + +GeoData +GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h) { - const Image map {fileName.c_str(), STBI_grey}; - size = {map.width, map.height}; - limit = {{0, 0}, size - glm::uvec2 {1, 1}}; - const auto points {size.x * size.y}; - scale = scale_; - nodes.resize(points); + GeoData mesh; - std::transform(map.data.data(), map.data.data() + points, nodes.begin(), [](auto d) { - return Node {(d * 0.1F) - 1.5F}; - }); + mesh.lowerExtent = {lower, h}; + mesh.upperExtent = {upper, h}; + + const auto ll = mesh.add_vertex({lower.x, lower.y, h}), lu = mesh.add_vertex({lower.x, upper.y, h}), + ul = mesh.add_vertex({upper.x, lower.y, h}), uu = mesh.add_vertex({upper.x, upper.y, h}); + + mesh.add_face(ll, uu, lu); + mesh.add_face(ll, ul, uu); + + mesh.update_face_normals(); + mesh.update_vertex_normals(); + + return mesh; } -GeoData::Quad -GeoData::quad(Position2D wcoord) const +OpenMesh::FaceHandle +GeoData::findPoint(GlobalPosition2D p) const { - constexpr static const std::array<glm::vec2, 4> corners {{{0, 0}, {0, 1}, {1, 0}, {1, 1}}}; - return transform_array(transform_array(corners, - [coord = (wcoord / scale)](const auto c) { - return glm::vec2 {std::floor(coord.x), std::floor(coord.y)} + c; - }), - [this](const auto c) { - return (c * scale) || nodes[at(c)].height; - }); + return findPoint(p, *faces_begin()); } -glm::vec3 -GeoData::positionAt(const glm::vec2 wcoord) const +GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh) : + PointFace {p, mesh, *mesh->faces_begin()} { - const auto point {quad(wcoord)}; - const glm::vec2 frac = (wcoord - !point.front()) / scale; - auto edge = [&point, &frac](auto offset) { - return point[offset].z + ((point[offset + 2].z - point[offset].z) * frac.x); - }; - const auto heightFloor = edge(0U), heightCeil = edge(1U), - heightMid = heightFloor + ((heightCeil - heightFloor) * frac.y); - - return wcoord || heightMid; } -GeoData::RayTracer::RayTracer(glm::vec2 p0, glm::vec2 p1) : RayTracer {p0, p1, glm::abs(p1)} { } - -GeoData::RayTracer::RayTracer(glm::vec2 p0, glm::vec2 p1, glm::vec2 d) : - RayTracer {p0, d, byAxis(p0, p1, d, 0), byAxis(p0, p1, d, 1)} +GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh, FaceHandle start) : + PointFace {p, mesh->findPoint(p, start)} { } -GeoData::RayTracer::RayTracer( - glm::vec2 p0, glm::vec2 d_, std::pair<float, float> xdata, std::pair<float, float> ydata) : - p {glm::floor(p0)}, - d {d_}, error {xdata.second - ydata.second}, inc {xdata.first, ydata.first} +GeoData::FaceHandle +GeoData::PointFace::face(const GeoData * mesh, FaceHandle start) const { + if (_face.is_valid()) { + assert(mesh->triangleContainsPoint(point, _face)); + return _face; + } + else { + return (_face = mesh->findPoint(point, start)); + } } -std::pair<float, float> -GeoData::RayTracer::byAxis(glm::vec2 p0, glm::vec2 p1, glm::vec2 d, glm::length_t axis) +GeoData::FaceHandle +GeoData::PointFace::face(const GeoData * mesh) const { - using Limits = std::numeric_limits<typename glm::vec2::value_type>; - static_assert(Limits::has_infinity); - if (d[axis] == 0) { - return {0, Limits::infinity()}; + return face(mesh, *mesh->faces_begin()); +} + +namespace { + template<template<typename> typename Op> + [[nodiscard]] constexpr inline auto + pointLineOp(const GlobalPosition2D p, const GlobalPosition2D e1, const GlobalPosition2D e2) + { + return Op {}(int64_t(e2.x - e1.x) * int64_t(p.y - e1.y), int64_t(e2.y - e1.y) * int64_t(p.x - e1.x)); } - else if (p1[axis] > 0) { - return {1, (std::floor(p0[axis]) + 1.F - p0[axis]) * d[1 - axis]}; + + constexpr auto pointLeftOfLine = pointLineOp<std::greater>; + constexpr auto pointLeftOfOrOnLine = pointLineOp<std::greater_equal>; + + static_assert(pointLeftOfLine({1, 2}, {1, 1}, {2, 2})); + static_assert(pointLeftOfLine({2, 1}, {2, 2}, {1, 1})); + static_assert(pointLeftOfLine({2, 2}, {1, 2}, {2, 1})); + static_assert(pointLeftOfLine({1, 1}, {2, 1}, {1, 2})); + static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490000000}, {310050000, 490050000})); + static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310050000, 490050000}, {310000000, 490050000})); + static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490050000}, {310000000, 490000000})); + + [[nodiscard]] constexpr inline bool + linesCross( + const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2) + { + return (pointLeftOfLine(a2, b1, b2) == pointLeftOfLine(a1, b2, b1)) + && (pointLeftOfLine(b1, a1, a2) == pointLeftOfLine(b2, a2, a1)); } - else { - return {-1, (p0[axis] - std::floor(p0[axis])) * d[1 - axis]}; + + static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1})); + static_assert(linesCross({2, 2}, {1, 1}, {1, 2}, {2, 1})); + + [[nodiscard]] constexpr inline bool + linesCrossLtR( + const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2) + { + return pointLeftOfLine(a2, b1, b2) && pointLeftOfLine(a1, b2, b1) && pointLeftOfLine(b1, a1, a2) + && pointLeftOfLine(b2, a2, a1); } + + static_assert(linesCrossLtR({1, 1}, {2, 2}, {1, 2}, {2, 1})); + static_assert(!linesCrossLtR({2, 2}, {1, 1}, {1, 2}, {2, 1})); } -glm::vec2 -GeoData::RayTracer::next() +OpenMesh::FaceHandle +GeoData::findPoint(GlobalPosition2D p, OpenMesh::FaceHandle f) const { - const glm::vec2 cur {p}; - - static constexpr const glm::vec2 m {1, -1}; - const int axis = (error > 0) ? 1 : 0; - p[axis] += inc[axis]; - error += d[1 - axis] * m[axis]; + while (f.is_valid() && !triangleContainsPoint(p, triangle<2>(f))) { + for (auto next = cfh_iter(f); next.is_valid(); ++next) { + f = opposite_face_handle(*next); + if (f.is_valid()) { + const auto e1 = point(to_vertex_handle(*next)); + const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(*next))); + if (pointLeftOfLine(p, e1, e2)) { + break; + } + } + f.reset(); + } + } + return f; +} - return cur; +GlobalPosition3D +GeoData::positionAt(const PointFace & p) const +{ + RelativePosition3D out {}; + const auto t = triangle<3>(p.face(this)); + glm::intersectLineTriangle<RelativePosition3D>({p.point, 0}, up, t[0], t[1], t[2], out); + return {p.point, out[0]}; } -std::optional<glm::vec3> +[[nodiscard]] std::optional<GlobalPosition3D> GeoData::intersectRay(const Ray & ray) const { - if (glm::length(!ray.direction) <= 0) { - return {}; + return intersectRay(ray, findPoint(ray.start)); +} + +[[nodiscard]] std::optional<GlobalPosition3D> +GeoData::intersectRay(const Ray & ray, FaceHandle face) const +{ + std::optional<GlobalPosition3D> out; + walkUntil(PointFace {ray.start, face}, ray.start + (ray.direction * 10000.F), [&out, &ray, this](FaceHandle face) { + BaryPosition bari {}; + float dist {}; + const auto t = triangle<3>(face); + if (glm::intersectRayTriangle<RelativePosition3D::value_type, glm::defaultp>( + ray.start, ray.direction, t[0], t[1], t[2], bari, dist)) { + out = t * bari; + return true; + } + return false; + }); + return out; +} + +void +GeoData::walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const +{ + walkUntil(from, to, [&op](const auto & fh) { + op(fh); + return false; + }); +} + +void +GeoData::walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const +{ + auto f = from.face(this); + if (!f.is_valid()) { + f = opposite_face_handle(findEntry(from.point, to)); } - RayTracer rt {ray.start / scale, ray.direction}; - while (true) { - const auto n {rt.next() * scale}; - try { - const auto point = quad(n); - for (auto offset : {0U, 1U}) { - BaryPosition bary; - float distance; - if (glm::intersectRayTriangle(ray.start, ray.direction, point[offset], point[offset + 1], - point[offset + 2], bary, distance)) { - return point[offset] + ((point[offset + 1] - point[offset]) * bary[0]) - + ((point[offset + 2] - point[offset]) * bary[1]); + FaceHandle previousFace; + while (f.is_valid() && !op(f)) { + for (auto next = cfh_iter(f); next.is_valid(); ++next) { + f = opposite_face_handle(*next); + if (f.is_valid() && f != previousFace) { + const auto e1 = point(to_vertex_handle(*next)); + const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(*next))); + if (linesCrossLtR(from.point, to, e1, e2)) { + previousFace = f; + break; } } - } - catch (std::range_error &) { - const auto rel = n / !ray.direction; - if (rel.x > 0 && rel.y > 0) { - return {}; - } + f.reset(); } } +} - return {}; +void +GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op) const +{ + boundaryWalk(op, findBoundaryStart()); } -unsigned int -GeoData::at(glm::ivec2 coord) const +void +GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op, HalfedgeHandle start) const { - if (coord.x < limit.first.x || coord.x > limit.second.x || coord.y < limit.first.y || coord.y > limit.second.y) { - throw std::range_error {"Coordinates outside GeoData limits"}; - } - const glm::uvec2 offset = coord - limit.first; - return offset.x + (offset.y * size.x); + assert(is_boundary(start)); + boundaryWalkUntil( + [&op](auto heh) { + op(heh); + return false; + }, + start); +} + +void +GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op) const +{ + boundaryWalkUntil(op, findBoundaryStart()); } -unsigned int -GeoData::at(int x, int y) const +void +GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op, HalfedgeHandle start) const { - return at({x, y}); + assert(is_boundary(start)); + if (!op(start)) { + for (auto heh = next_halfedge_handle(start); heh != start; heh = next_halfedge_handle(heh)) { + if (op(heh)) { + break; + } + } + } } -GeoData::Limits -GeoData::getLimit() const +GeoData::HalfedgeHandle +GeoData::findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const { - return limit; + HalfedgeHandle entry; + boundaryWalkUntil([this, from, to, &entry](auto he) { + const auto e1 = point(to_vertex_handle(he)); + const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(he))); + if (linesCrossLtR(from, to, e1, e2)) { + entry = he; + return true; + } + return false; + }); + return entry; } -float -GeoData::getScale() const +bool +GeoData::triangleContainsPoint(const GlobalPosition2D p, const Triangle<2> & t) { - return scale; + return pointLeftOfOrOnLine(p, t[0], t[1]) && pointLeftOfOrOnLine(p, t[1], t[2]) + && pointLeftOfOrOnLine(p, t[2], t[0]); } -glm::uvec2 -GeoData::getSize() const +bool +GeoData::triangleContainsPoint(const GlobalPosition2D p, FaceHandle face) const { - return size; + return triangleContainsPoint(p, triangle<2>(face)); } -std::span<const GeoData::Node> -GeoData::getNodes() const +GeoData::HalfedgeHandle +GeoData::findBoundaryStart() const { - return nodes; + return *std::find_if(halfedges_begin(), halfedges_end(), [this](const auto heh) { + return is_boundary(heh); + }); } diff --git a/game/geoData.h b/game/geoData.h index 3bceb9c..3141dbe 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -1,63 +1,110 @@ #pragma once +#include "collections.h" // IWYU pragma: keep IterableCollection #include "config/types.h" -#include <array> +#include "ray.h" +#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> #include <filesystem> -#include <glm/glm.hpp> +#include <glm/vec2.hpp> #include <optional> -#include <span> -#include <utility> -#include <vector> +#include <thirdparty/openmesh/glmcompat.h> -class Ray; +struct GeoDataTraits : public OpenMesh::DefaultTraits { + FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + EdgeAttributes(OpenMesh::Attributes::Status); + VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + HalfedgeAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); + using Point = GlobalPosition3D; + using Normal = Normal3D; +}; -class GeoData { -public: - struct Node { - float height {-1500.F}; - }; +class GeoData : public OpenMesh::TriMesh_ArrayKernelT<GeoDataTraits> { +private: + GeoData() = default; - using Quad = std::array<Position3D, 4>; +public: + static GeoData loadFromAsciiGrid(const std::filesystem::path &); + static GeoData createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h); - using Limits = std::pair<glm::vec<2, int>, glm::vec<2, int>>; + struct PointFace { + // NOLINTNEXTLINE(hicpp-explicit-conversions) + PointFace(const GlobalPosition2D p) : point {p} { } - explicit GeoData(Limits limit, float scale = 10.F); + PointFace(const GlobalPosition2D p, FaceHandle face) : point {p}, _face {face} { } - void generateRandom(); - void loadFromImages(const std::filesystem::path &, float scale); + PointFace(const GlobalPosition2D p, const GeoData *); + PointFace(const GlobalPosition2D p, const GeoData *, FaceHandle start); - [[nodiscard]] Position3D positionAt(Position2D) const; - [[nodiscard]] std::optional<Position3D> intersectRay(const Ray &) const; + const GlobalPosition2D point; + [[nodiscard]] FaceHandle face(const GeoData *) const; + [[nodiscard]] FaceHandle face(const GeoData *, FaceHandle start) const; - [[nodiscard]] unsigned int at(glm::vec<2, int>) const; - [[nodiscard]] unsigned int at(int x, int y) const; - [[nodiscard]] Quad quad(Position2D) const; + [[nodiscard]] bool + isLocated() const + { + return _face.is_valid(); + } - [[nodiscard]] Limits getLimit() const; - [[nodiscard]] glm::vec<2, unsigned int> getSize() const; - [[nodiscard]] float getScale() const; - [[nodiscard]] std::span<const Node> getNodes() const; + private: + mutable FaceHandle _face {}; + }; - class RayTracer { - public: - RayTracer(Position2D p0, Position2D p1); + template<glm::length_t Dim> struct Triangle : public glm::vec<3, glm::vec<Dim, GlobalDistance>> { + using base = glm::vec<3, glm::vec<Dim, GlobalDistance>>; + using base::base; - Position2D next(); + template<IterableCollection Range> Triangle(const GeoData * m, Range range) + { + assert(std::distance(range.begin(), range.end()) == 3); + std::transform(range.begin(), range.end(), &base::operator[](0), [m](auto vh) { + return m->point(vh); + }); + } - private: - RayTracer(Position2D p0, Position2D p1, Position2D d); - RayTracer(Position2D p0, Position2D d, std::pair<float, float>, std::pair<float, float>); - static std::pair<float, float> byAxis(Position2D p0, Position2D p1, Position2D d, glm::length_t); - - Position2D p; - const Position2D d; - float error; - Position2D inc; + glm::vec<Dim, GlobalDistance> + operator*(BaryPosition bari) const + { + const auto & t {*this}; + return t[0] + GlobalPosition<Dim>(RelativePosition<Dim>(t[1] - t[0]) * bari.x) + + GlobalPosition<Dim>(RelativePosition<Dim>(t[2] - t[1]) * bari.y); + } }; + [[nodiscard]] FaceHandle findPoint(GlobalPosition2D) const; + [[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle start) const; + + [[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const; + [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray &) const; + [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray &, FaceHandle start) const; + + void walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const; + void walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const; + + void boundaryWalk(const std::function<void(HalfedgeHandle)> &) const; + void boundaryWalk(const std::function<void(HalfedgeHandle)> &, HalfedgeHandle start) const; + void boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> &) const; + void boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> &, HalfedgeHandle start) const; + + [[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const; + + [[nodiscard]] auto + getExtents() const + { + return std::tie(lowerExtent, upperExtent); + } + protected: - Limits limit {}; // Base grid limits first(x,y) -> second(x,y) - glm::vec<2, unsigned> size {}; - float scale {1}; - std::vector<Node> nodes; + template<glm::length_t Dim> + [[nodiscard]] Triangle<Dim> + triangle(FaceHandle f) const + { + return {this, fv_range(f)}; + } + + [[nodiscard]] static bool triangleContainsPoint(const GlobalPosition2D, const Triangle<2> &); + [[nodiscard]] bool triangleContainsPoint(const GlobalPosition2D, FaceHandle) const; + [[nodiscard]] HalfedgeHandle findBoundaryStart() const; + +private: + GlobalPosition3D lowerExtent {}, upperExtent {}; }; diff --git a/game/terrain.cpp b/game/terrain.cpp index f3bec1e..5bb8abd 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -2,7 +2,6 @@ #include "game/geoData.h" #include "gfx/models/texture.h" #include <algorithm> -#include <array> #include <cache.h> #include <cstddef> #include <filesystem> @@ -18,8 +17,8 @@ #include <utility> #include <vector> -Terrain::Terrain(std::shared_ptr<GeoData> gd) : - geoData {std::move(gd)}, grass {Texture::cachedTexture.get("grass.png")}, +Terrain::Terrain(std::shared_ptr<GeoData> tm) : + geoData {std::move(tm)}, grass {Texture::cachedTexture.get("grass.png")}, water {Texture::cachedTexture.get("water.png")} { generateMeshes(); @@ -29,51 +28,23 @@ void Terrain::generateMeshes() { std::vector<unsigned int> indices; - const auto isize = geoData->getSize() - glm::uvec2 {1, 1}; - indices.reserve(static_cast<std::size_t>(isize.x * isize.y) * 6); - - const auto limit = geoData->getLimit(); - // Indices - constexpr std::array<glm::ivec2, 6> indices_offsets {{ - {0, 0}, - {1, 0}, - {1, 1}, - {0, 0}, - {1, 1}, - {0, 1}, - }}; - for (auto y = limit.first.y; y < limit.second.y; y += 1) { - for (auto x = limit.first.x; x < limit.second.x; x += 1) { - std::transform(indices_offsets.begin(), indices_offsets.end(), std::back_inserter(indices), - [this, x, y](const auto off) { - return geoData->at(x + off.x, y + off.y); - }); - } - } - - const auto nodes = geoData->getNodes(); - const auto scale = geoData->getScale(); + indices.reserve(geoData->n_faces() * 3); std::vector<Vertex> vertices; - vertices.reserve(nodes.size()); - // Positions - for (auto y = limit.first.y; y <= limit.second.y; y += 1) { - for (auto x = limit.first.x; x <= limit.second.x; x += 1) { - const glm::vec2 xy {x, y}; - vertices.emplace_back((xy * scale) ^ nodes[geoData->at(x, y)].height, xy, ::up); - } - } - // Normals - const glm::uvec2 size = geoData->getSize(); - for (auto y = limit.first.y + 1; y < limit.second.y; y += 1) { - for (auto x = limit.first.x + 1; x < limit.second.x; x += 1) { - const auto n {geoData->at(x, y)}; - const auto a = vertices[n - 1].pos; - const auto b = vertices[n - size.x].pos; - const auto c = vertices[n + 1].pos; - const auto d = vertices[n + size.x].pos; - vertices[n].normal = -glm::normalize(glm::cross(b - d, a - c)); - } - } + vertices.reserve(geoData->n_vertices()); + std::map<GeoData::VertexHandle, size_t> vertexIndex; + std::transform(geoData->vertices_begin(), geoData->vertices_end(), std::back_inserter(vertices), + [this, &vertexIndex](const GeoData::VertexHandle v) { + vertexIndex.emplace(v, vertexIndex.size()); + const glm::vec3 p = geoData->point(v); + return Vertex {p, p / 10.F, geoData->normal(v)}; + }); + std::for_each( + geoData->faces_begin(), geoData->faces_end(), [this, &vertexIndex, &indices](const GeoData::FaceHandle f) { + std::transform(geoData->fv_begin(f), geoData->fv_end(f), std::back_inserter(indices), + [&vertexIndex](const GeoData::VertexHandle v) { + return vertexIndex[v]; + }); + }); meshes.create<Mesh>(vertices, indices); } |