载入中...
搜索中...
未找到
HexWaterMesh.cpp
浏览该文件的文档.
1#include "hexmap/HexMapMesh.h"
2
4#include "hexmap/HexMetrics.h"
5#include "hexmap/HexNoise.h"
6
7#include <cstdint>
8
9namespace eve::hexmap {
10namespace {
11
13struct SurfaceVertex {
14 HexVec3 position;
15 float u = 0.f;
16 float v = 0.f;
17};
18
25[[nodiscard]] bool isDegenerate(HexVec3 a, HexVec3 b, HexVec3 c) noexcept {
26 const HexVec3 ab = b - a;
27 const HexVec3 ac = c - a;
28 const HexVec3 normal{ab.y * ac.z - ab.z * ac.y, ab.z * ac.x - ab.x * ac.z, ab.x * ac.y - ab.y * ac.x};
29 return (normal.x * normal.x + normal.y * normal.y + normal.z * normal.z) <= 1e-8f;
30}
31
42void emitTriangle(HexMeshData& out, const SurfaceVertex& a, const SurfaceVertex& b, const SurfaceVertex& c,
43 const HexNoise& noise) {
44 const HexVec3 pa = noise.perturb(a.position);
45 const HexVec3 pb = noise.perturb(b.position);
46 const HexVec3 pc = noise.perturb(c.position);
47 if (isDegenerate(pa, pb, pc)) return;
48 out.addTriangle(out.addVertex(pa, a.u, a.v), out.addVertex(pb, b.u, b.v), out.addVertex(pc, c.u, c.v));
49}
50
59void emitQuad(HexMeshData& out, const SurfaceVertex& r0, const SurfaceVertex& r1, const SurfaceVertex& r2,
60 const SurfaceVertex& r3, const HexNoise& noise) {
61 emitTriangle(out, r0, r3, r1, noise);
62 emitTriangle(out, r1, r3, r2, noise);
63}
64
66[[nodiscard]] float waterHeightOf(const HexMap& map, HexCoordinates coordinates, float offGridHeight) noexcept {
67 return map.contains(coordinates) ? HexMetrics::waterSurfaceY(map.waterLevel(coordinates)) : offGridHeight;
68}
69
76[[nodiscard]] bool isWater(const HexMap& map, HexCoordinates coordinates) noexcept {
77 return map.contains(coordinates) && map.isUnderwater(coordinates);
78}
79
95void appendOpenWater(const HexMap& map, HexCoordinates coordinates, HexDirection direction, HexVec3 centre,
96 const HexNoise& noise, HexMeshData& out) {
97 const SurfaceVertex c1{centre + HexMetrics::firstWaterCorner(direction), 0.f, 0.f};
98 const SurfaceVertex c2{centre + HexMetrics::secondWaterCorner(direction), 0.f, 0.f};
99 emitTriangle(out, SurfaceVertex{centre, 0.f, 0.f}, c1, c2, noise);
100
101 HexCoordinates neighbour{};
102 if (static_cast<std::int32_t>(direction) > static_cast<std::int32_t>(HexDirection::SE) ||
103 !map.getNeighbor(coordinates, direction, neighbour)) {
104 // Only the NE, E and SE edges bridge outward: for a water/water boundary the
105 // other side would emit the same quad, so exactly one chunk owns it. An
106 // off-grid neighbour has nothing to bridge to.
107 return;
108 }
109
111 const float farY = waterHeightOf(map, neighbour, centre.y);
112 const SurfaceVertex e1{{c1.position.x + offset.x, farY, c1.position.z + offset.z}, 0.f, 0.f};
113 const SurfaceVertex e2{{c2.position.x + offset.x, farY, c2.position.z + offset.z}, 0.f, 0.f};
114 emitQuad(out, c1, c2, e2, e1, noise);
115
116 if (static_cast<std::int32_t>(direction) > static_cast<std::int32_t>(HexDirection::E)) return;
117 HexCoordinates nextNeighbour{};
118 if (!map.getNeighbor(coordinates, next(direction), nextNeighbour) || !isWater(map, nextNeighbour)) return;
119
120 // The third cell of the junction is water too, so its own wedge stops at the
121 // water corner and this triangle fills the gap between the two bridges.
122 HexVec3 corner = map.cellGroundPosition(nextNeighbour) + HexMetrics::firstWaterCorner(previous(direction));
123 corner.y = farY;
124 emitTriangle(out, c2, e2, SurfaceVertex{corner, 0.f, 0.f}, noise);
125}
126
148void appendWaterShore(const HexMap& map, HexCoordinates coordinates, HexDirection direction, HexVec3 centre,
149 const HexNoise& noise, HexMeshData& out) {
150 const EdgeVertices near{centre + HexMetrics::firstWaterCorner(direction),
152
153 const SurfaceVertex nearSamples[5] = {SurfaceVertex{near.v1, 0.f, 0.f}, SurfaceVertex{near.v2, 0.f, 0.f},
154 SurfaceVertex{near.v3, 0.f, 0.f}, SurfaceVertex{near.v4, 0.f, 0.f},
155 SurfaceVertex{near.v5, 0.f, 0.f}};
156 const SurfaceVertex wedgeCentre{centre, 0.f, 0.f};
157 for (std::int32_t i = 0; i < 4; ++i) {
158 emitTriangle(out, wedgeCentre, nearSamples[i], nearSamples[i + 1], noise);
159 }
160
161 const HexCoordinates neighbour = coordinates.step(direction);
162 const HexVec3 neighbourGround = map.cellGroundPosition(neighbour);
163 const HexVec3 bank{neighbourGround.x, centre.y, neighbourGround.z};
164 // Second corner first, so sample `i` of the far edge lines up with sample `i` of
165 // the near edge across the strip - the reference orders them the same way.
166 const EdgeVertices far{bank + HexMetrics::secondSolidCorner(opposite(direction)),
168
169 const SurfaceVertex farSamples[5] = {SurfaceVertex{far.v1, 1.f, 0.f}, SurfaceVertex{far.v2, 1.f, 0.f},
170 SurfaceVertex{far.v3, 1.f, 0.f}, SurfaceVertex{far.v4, 1.f, 0.f},
171 SurfaceVertex{far.v5, 1.f, 0.f}};
172 for (std::int32_t i = 0; i < 4; ++i) {
173 emitQuad(out, nearSamples[i], nearSamples[i + 1], farSamples[i + 1], farSamples[i], noise);
174 }
175
176 // Close the junction with the next cell around: its water edge when it is flooded,
177 // its bank corner when it is land. Either way the vertex stays at the water height.
178 HexCoordinates nextNeighbour{};
179 if (!map.getNeighbor(coordinates, next(direction), nextNeighbour)) return;
180 const HexVec3 corner =
181 isWater(map, nextNeighbour)
182 ? map.cellGroundPosition(nextNeighbour) + HexMetrics::firstWaterCorner(previous(direction))
183 : map.cellGroundPosition(nextNeighbour) + HexMetrics::firstSolidCorner(previous(direction));
184 HexVec3 capped = corner;
185 capped.y = centre.y;
186 emitTriangle(out, nearSamples[4], farSamples[4], SurfaceVertex{capped, 0.f, 0.f}, noise);
187}
188
189} // namespace
190
191void buildWaterMesh(const HexMap& map, std::int32_t chunkIndex, HexMeshData& out) {
192 out.clear();
193 if (chunkIndex < 0 || chunkIndex >= map.chunkCount()) {
194 out.finalize();
195 return;
196 }
197
198 const HexNoise& noise = map.noise();
199
200 for (std::int32_t row = 0; row < HexMetrics::kChunkSizeZ; ++row) {
201 for (std::int32_t column = 0; column < HexMetrics::kChunkSizeX; ++column) {
202 const HexCoordinates coordinates = map.chunkCell(chunkIndex, column, row);
203 if (map.cell(coordinates) == nullptr || !map.isUnderwater(coordinates)) continue;
204
205 const HexVec3 ground = map.cellGroundPosition(coordinates);
206 const HexVec3 centre{ground.x, HexMetrics::waterSurfaceY(map.waterLevel(coordinates)), ground.z};
207
208 // Dispatch per direction, exactly like the reference: a flooded direction
209 // gets the open-water wedge, a land direction gets the shore wedge and its
210 // strip. Emitting a full-cell fan up front instead would double the centre
211 // triangles of every shoreline direction and leave the strip to fight it.
212 for (std::int32_t i = 0; i < kHexDirectionCount; ++i) {
213 const auto direction = static_cast<HexDirection>(i);
214 HexCoordinates neighbour{};
215 const bool hasNeighbour = map.getNeighbor(coordinates, direction, neighbour);
216 const bool neighbourWater = hasNeighbour && isWater(map, neighbour);
217 if (hasNeighbour && !neighbourWater) {
218 appendWaterShore(map, coordinates, direction, centre, noise, out);
219 } else {
220 appendOpenWater(map, coordinates, direction, centre, noise, out);
221 }
222 }
223 }
224 }
225
226 out.finalize();
227}
228
229} // namespace eve::hexmap
int column
float u
Definition Grass.cpp:233
std::uint32_t ab
std::uint32_t ac
Per-chunk mesh generation for every hex map surface.
CPU vertex/index container shared by the hex surface builders.
Pointy-top hex metrics, directions and vertex helpers.
Deterministic asset-free noise for hex perturbation and generation.
float v
std::int32_t c
size_t offset
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Texture * normal
graphics::Canvas * previous
RoadLaneDirection direction
An editable, chunked, pointy-top hex map.
Definition HexMap.h:72
std::int32_t chunkCount() const noexcept
Total number of chunks.
Definition HexMap.h:102
std::int32_t waterLevel(HexCoordinates c) const noexcept
Definition HexMap.cpp:304
const HexNoise & noise() const noexcept
Deterministic noise field shared by this map's mesh builders.
Definition HexMap.h:110
bool getNeighbor(HexCoordinates coordinates, HexDirection direction, HexCoordinates &out) const noexcept
Neighbour of coordinates in direction; false when it is off-grid.
Definition HexMap.cpp:87
bool isUnderwater(HexCoordinates c) const noexcept
Definition HexMap.cpp:328
const HexCellData * cell(HexCoordinates coordinates) const noexcept
Cell record, or null when outside the grid. @ownership Borrowed; the map owns the record....
Definition HexMap.cpp:79
HexVec3 cellGroundPosition(HexCoordinates coordinates) const noexcept
Cell centre at y = 0 (no elevation, no perturbation).
Definition HexMap.h:198
HexCoordinates chunkCell(std::int32_t chunkIndex, std::int32_t column, std::int32_t row) const noexcept
Coordinates of the cell at (column, row) inside a chunk.
Definition HexMap.cpp:115
CPU-side triangle soup for one hex chunk surface.
Definition HexMeshData.h:24
void finalize() noexcept
Computes flat per-face normals. Must be called before upload.
void clear() noexcept
Removes every vertex and index, keeping the allocated capacity.
static HexVec3 firstSolidCorner(HexDirection d) noexcept
First solid corner of direction.
Definition HexMetrics.h:202
static HexVec3 secondSolidCorner(HexDirection d) noexcept
Second solid corner of direction.
Definition HexMetrics.h:204
static HexVec3 secondWaterCorner(HexDirection d) noexcept
Second water corner of direction.
Definition HexMetrics.h:212
static float waterSurfaceY(int waterLevel) noexcept
Y coordinate of a water surface at waterLevel.
Definition HexMetrics.h:273
static HexVec3 firstWaterCorner(HexDirection d) noexcept
First water corner of direction.
Definition HexMetrics.h:210
static HexVec3 waterBridge(HexDirection d) noexcept
Bridge vector from the water edge of direction to the neighbour.
Definition HexMetrics.h:218
static constexpr int kChunkSizeZ
Chunk size in the Z dimension.
Definition HexMetrics.h:158
static constexpr int kChunkSizeX
Chunk size in the X dimension.
Definition HexMetrics.h:156
Deterministic four-channel value noise over the XZ plane.
Definition HexNoise.h:23
constexpr HexDirection opposite(HexDirection d) noexcept
The direction opposite to d.
Definition HexMetrics.h:65
constexpr std::int32_t kHexDirectionCount
Number of hex edges / facing directions.
Definition HexMetrics.h:62
EVENGINE_API_WORLD void buildWaterMesh(const HexMap &map, std::int32_t chunkIndex, HexMeshData &out)
Builds the water surface and shore band of one chunk.
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
HexDirection
Hex facing directions, counter-clockwise from north-east.
Definition HexMetrics.h:59
Axial coordinates of one hex cell.
Minimal 3-component float vector used by the hex mesh builders.
Definition HexMetrics.h:18