pipeline/body_mesh.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/pipeline/body_mesh.h" | ||
| 2 | |||
| 3 | #include "terraingen/util/cells.h" | ||
| 4 | #include "terraingen/util/checks.h" | ||
| 5 | |||
| 6 | #include <numbers> | ||
| 7 | #include <stdexcept> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | namespace terraingen { | ||
| 11 | |||
| 12 | 17 | TriangleMesh BodyMesh(const Terrain& terrain, std::size_t cellsPerFace, double spacing, | |
| 13 | unsigned threads) { | ||
| 14 | 17 | if (!terrain.surface.IsBody()) { | |
| 15 | 2 | throw std::invalid_argument("A body mesh needs a terrain on a sphere"); | |
| 16 | } | ||
| 17 | 15 | if (cellsPerFace == 0) { | |
| 18 | 1 | throw std::invalid_argument("A body mesh needs at least one cell per face"); | |
| 19 | } | ||
| 20 | 14 | RequireNonNegative(spacing, "Sample spacing"); | |
| 21 | 13 | const double radius = terrain.surface.Radius(); | |
| 22 | 13 | const double quarter = std::numbers::pi / 4.0; | |
| 23 | 13 | const double step = 2.0 * quarter / static_cast<double>(cellsPerFace); | |
| 24 | 13 | const double sampled = spacing > 0.0 ? spacing : radius * step; | |
| 25 | 13 | const std::size_t side = cellsPerFace + 1; | |
| 26 | 26 | std::vector<Vec3d> directions(6 * side * side); | |
| 27 | 13 | std::vector<Vec2d> points(directions.size()); | |
| 28 | 203995 | for (std::size_t v = 0; v < directions.size(); ++v) { | |
| 29 | 203982 | const auto face = static_cast<int>(v / (side * side)); | |
| 30 | 203982 | const std::size_t i = v % side; | |
| 31 | 203982 | const std::size_t j = (v / side) % side; | |
| 32 | 203982 | directions[v] = CubeSphereCells::Direction(face, -quarter + static_cast<double>(i) * step, | |
| 33 | 203982 | -quarter + static_cast<double>(j) * step); | |
| 34 | 203982 | points[v] = terrain.surface.PointOf(directions[v]); | |
| 35 | } | ||
| 36 | 13 | const std::vector<float> heights = EvaluatePoints(terrain, points, sampled, threads); | |
| 37 | 13 | TriangleMesh mesh; | |
| 38 | 13 | mesh.positions.resize(directions.size()); | |
| 39 | 203995 | for (std::size_t v = 0; v < directions.size(); ++v) { | |
| 40 | 203982 | const double r = terrain.surface.RadiusAt(points[v]) + | |
| 41 | 203982 | (std::isnan(heights[v]) ? 0.0 : static_cast<double>(heights[v])); | |
| 42 | 203982 | mesh.positions[v] = directions[v] * r; | |
| 43 | } | ||
| 44 | 91 | for (std::size_t face = 0; face < 6; ++face) { | |
| 45 | 78 | const auto base = static_cast<std::uint32_t>(face * side * side); | |
| 46 | 2682 | for (std::size_t j = 0; j < cellsPerFace; ++j) { | |
| 47 | 201300 | for (std::size_t i = 0; i < cellsPerFace; ++i) { | |
| 48 | 198696 | const auto a = static_cast<std::uint32_t>(base + j * side + i); | |
| 49 | 198696 | const auto b = a + 1u; | |
| 50 | 198696 | const auto c = static_cast<std::uint32_t>(a + side); | |
| 51 | 198696 | const auto d = c + 1u; | |
| 52 | 198696 | mesh.triangles.push_back({a, b, d}); | |
| 53 | 198696 | mesh.triangles.push_back({a, d, c}); | |
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | 26 | return mesh; | |
| 58 | 13 | } | |
| 59 | |||
| 60 | 7 | IcqModel BodyIcq(const Terrain& terrain, std::size_t q, double spacing, unsigned threads) { | |
| 61 | 7 | if (!terrain.surface.IsBody()) { | |
| 62 | 1 | throw std::invalid_argument("An ICQ model needs a terrain on a body"); | |
| 63 | } | ||
| 64 | 6 | if (q == 0) { | |
| 65 | 1 | throw std::invalid_argument("An ICQ model needs Q > 0"); | |
| 66 | } | ||
| 67 | 5 | RequireNonNegative(spacing, "Sample spacing"); | |
| 68 | const double sampled = | ||
| 69 | 4 | spacing > 0.0 ? spacing | |
| 70 | 4 | : terrain.surface.Radius() * std::numbers::pi / 2.0 / static_cast<double>(q); | |
| 71 | 4 | IcqModel model; | |
| 72 | 4 | model.q = q; | |
| 73 | 4 | model.vertices.resize(6 * (q + 1) * (q + 1)); | |
| 74 | 8 | std::vector<Vec3d> directions(model.vertices.size()); | |
| 75 | 4 | std::vector<Vec2d> points(model.vertices.size()); | |
| 76 | 28 | for (std::size_t face = 1; face <= 6; ++face) { | |
| 77 | 540 | for (std::size_t j = 0; j <= q; ++j) { | |
| 78 | 26796 | for (std::size_t i = 0; i <= q; ++i) { | |
| 79 | 26280 | const std::size_t k = model.Index(i, j, face); | |
| 80 | 26280 | directions[k] = IcqDirection(q, i, j, face); | |
| 81 | 26280 | points[k] = terrain.surface.PointOf(directions[k]); | |
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | 4 | const std::vector<float> heights = EvaluatePoints(terrain, points, sampled, threads); | |
| 86 | 26284 | for (std::size_t k = 0; k < model.vertices.size(); ++k) { | |
| 87 | 26280 | const double r = terrain.surface.RadiusAt(points[k]) + | |
| 88 | 26280 | (std::isnan(heights[k]) ? 0.0 : static_cast<double>(heights[k])); | |
| 89 | 26280 | model.vertices[k] = directions[k] * r; | |
| 90 | } | ||
| 91 | 8 | return model; | |
| 92 | 4 | } | |
| 93 | |||
| 94 | } // namespace terraingen | ||
| 95 |