TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
70 / 70
100.0%
Branches:
44 / 44
100.0%
Functions:
4 / 4
100.0%

layers/boulders/boulder_fragments.cpp
Line Branch Exec Source
1 #include "terraingen/layers/boulders/boulder_fragments.h"
2
3 #include "terraingen/random/stream.h"
4
5 #include <algorithm>
6 #include <cmath>
7 #include <numbers>
8 #include <stdexcept>
9
10 namespace terraingen {
11
12 namespace {
13
14 constexpr std::uint32_t kFragmentStream = 0x46524147u;
15
16 /// The seed points of `pieces` pieces of a shape whose box is `box`: around the middle, a third
17 /// of the way out, a little above or below it so the cracks tilt.
18 135 std::vector<Vec3d> SeedPoints(const Box3d& box, int pieces, RandomStream& random) {
19 135 const Vec3d centre = (box.min + box.max) * 0.5;
20 135 const Vec3d half = (box.max - box.min) * 0.5;
21 135 const double turn = 2.0 * std::numbers::pi / pieces;
22
1/1
✓ Branch 1 taken 135 times.
135 const double start = turn * random.NextDouble();
23 135 std::vector<Vec3d> seeds;
24
2/2
✓ Branch 0 taken 533 times.
✓ Branch 1 taken 135 times.
668 for (int k = 0; k < pieces; ++k) {
25
1/1
✓ Branch 1 taken 533 times.
533 const double angle = start + turn * (k + 0.3 * (random.NextDouble() - 0.5));
26
1/1
✓ Branch 1 taken 533 times.
533 const double out = 0.35 + 0.15 * random.NextDouble();
27
1/1
✓ Branch 1 taken 533 times.
533 const double rise = 0.2 * (random.NextDouble() - 0.5);
28
1/1
✓ Branch 1 taken 533 times.
533 seeds.push_back(centre + Vec3d{out * half.x * std::cos(angle),
29 533 out * half.y * std::sin(angle), rise * half.z});
30 }
31 270 return seeds;
32 }
33
34 /// The seed points of `pieces` slabs of a shape whose box is `box`: spread along one horizontal
35 /// line through the middle, a little off it and above or below it, so the cracks between them run
36 /// roughly parallel right through the shape.
37 202 std::vector<Vec3d> SlabSeedPoints(const Box3d& box, int pieces, RandomStream& random) {
38 202 const Vec3d centre = (box.min + box.max) * 0.5;
39 202 const Vec3d half = (box.max - box.min) * 0.5;
40
1/1
✓ Branch 1 taken 202 times.
202 const double angle = std::numbers::pi * random.NextDouble();
41 202 const Vec3d along{std::cos(angle), std::sin(angle), 0.0};
42 202 const Vec3d across{-along.y, along.x, 0.0};
43 202 const double reach = half.x * std::fabs(along.x) + half.y * std::fabs(along.y);
44 202 const double side = half.x * std::fabs(across.x) + half.y * std::fabs(across.y);
45 202 const double step = 2.0 * reach / pieces;
46 202 std::vector<Vec3d> seeds;
47
2/2
✓ Branch 0 taken 725 times.
✓ Branch 1 taken 202 times.
927 for (int k = 0; k < pieces; ++k) {
48
1/1
✓ Branch 1 taken 725 times.
725 const double at = (k - 0.5 * (pieces - 1) + 0.4 * (random.NextDouble() - 0.5)) * step;
49
1/1
✓ Branch 1 taken 725 times.
725 const double off = 0.3 * side * (random.NextDouble() - 0.5);
50
1/1
✓ Branch 1 taken 725 times.
725 const double rise = 0.3 * half.z * (random.NextDouble() - 0.5);
51
1/1
✓ Branch 6 taken 725 times.
725 seeds.push_back(centre + along * at + across * off + Vec3d{0.0, 0.0, rise});
52 }
53 404 return seeds;
54 }
55
56 /// `mesh` cut to the cell of seed `k` among `seeds`: every vertex beyond a plane halfway to
57 /// another seed is moved along its ray from seed `k` onto that plane.
58 1258 TriangleMesh CutToCell(const TriangleMesh& mesh, const std::vector<Vec3d>& seeds, std::size_t k) {
59 1258 TriangleMesh piece = mesh;
60
2/2
✓ Branch 1 taken 5166 times.
✓ Branch 2 taken 1258 times.
6424 for (std::size_t j = 0; j < seeds.size(); ++j) {
61
2/2
✓ Branch 0 taken 1258 times.
✓ Branch 1 taken 3908 times.
5166 if (j == k) {
62 1258 continue;
63 }
64 3908 const Vec3d between = seeds[j] - seeds[k];
65 3908 const double length = Length(between);
66 3908 const Vec3d normal = between * (1.0 / length);
67 3908 const double plane = 0.5 * length;
68
2/2
✓ Branch 5 taken 1963784 times.
✓ Branch 6 taken 3908 times.
1967692 for (Vec3d& p : piece.positions) {
69 1963784 const Vec3d ray = p - seeds[k];
70 1963784 const double along = Dot(normal, ray);
71
2/2
✓ Branch 0 taken 710301 times.
✓ Branch 1 taken 1253483 times.
1963784 if (along > plane) {
72 710301 p = seeds[k] + ray * (plane / along);
73 }
74 }
75 }
76 1258 return piece;
77 }
78
79 } // namespace
80
81 339 std::vector<BoulderFragment> FragmentBoulderShape(const BoulderShape& parent, int pieces,
82 std::uint64_t seed, BoulderCracks cracks) {
83
4/4
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 337 times.
339 if (pieces < 2 || pieces > 12) {
84
1/1
✓ Branch 2 taken 2 times.
2 throw std::invalid_argument("A boulder breaks into 2 to 12 pieces");
85 }
86 RandomStream random(static_cast<std::uint32_t>(seed), kFragmentStream,
87
1/1
✓ Branch 1 taken 337 times.
337 static_cast<std::uint32_t>(seed >> 32));
88
1/1
✓ Branch 1 taken 337 times.
337 const Box3d box = Bounds(parent.mesh);
89 const std::vector<Vec3d> seeds = cracks == BoulderCracks::Slabs
90
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 135 times.
337 ? SlabSeedPoints(box, pieces, random)
91
2/2
✓ Branch 1 taken 202 times.
✓ Branch 4 taken 135 times.
337 : SeedPoints(box, pieces, random);
92 337 std::vector<BoulderFragment> fragments;
93
2/2
✓ Branch 1 taken 1258 times.
✓ Branch 2 taken 337 times.
1595 for (std::size_t k = 0; k < seeds.size(); ++k) {
94
1/1
✓ Branch 1 taken 1258 times.
1258 const TriangleMesh cut = CutToCell(parent.mesh, seeds, k);
95
1/1
✓ Branch 1 taken 1258 times.
1258 const Box3d bounds = Bounds(cut);
96 1258 const Vec3d extents = bounds.max - bounds.min;
97
1/1
✓ Branch 1 taken 1258 times.
1258 BoulderFragment fragment;
98 1258 fragment.centre = (bounds.min + bounds.max) * 0.5;
99 1258 fragment.scale = std::fmax(extents.x, extents.y);
100
1/1
✓ Branch 1 taken 1258 times.
1258 fragment.shape.mesh.triangles = cut.triangles;
101
2/2
✓ Branch 4 taken 637204 times.
✓ Branch 5 taken 1258 times.
638462 for (const Vec3d& p : cut.positions) {
102
1/1
✓ Branch 3 taken 637204 times.
637204 fragment.shape.mesh.positions.push_back((p - fragment.centre) * (1.0 / fragment.scale));
103 }
104 1258 fragment.shape.style = parent.style;
105 1258 fragment.shape.intermediate = std::fmin(extents.x, extents.y) / fragment.scale;
106 1258 fragment.shape.shortAxis = extents.z / fragment.scale;
107
1/1
✓ Branch 1 taken 1258 times.
1258 fragment.shape.material = parent.material;
108
1/1
✓ Branch 2 taken 1258 times.
1258 fragments.push_back(std::move(fragment));
109 1258 }
110 674 return fragments;
111 337 }
112
113 } // namespace terraingen
114