载入中...
搜索中...
未找到
HexRiverMesh.cpp
浏览该文件的文档.
1#include "hexmap/HexMapMesh.h"
2
3#include "hexmap/HexCell.h"
5#include "hexmap/HexMetrics.h"
6#include "hexmap/HexNoise.h"
7
8#include <array>
9#include <cmath>
10#include <cstdint>
11
12namespace eve::hexmap {
13namespace {
14
16struct SurfaceVertex {
17 HexVec3 position;
18 float u = 0.f;
19 float v = 0.f;
20};
21
28[[nodiscard]] bool isDegenerate(HexVec3 a, HexVec3 b, HexVec3 c) noexcept {
29 const HexVec3 ab = b - a;
30 const HexVec3 ac = c - a;
31 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};
32 return (normal.x * normal.x + normal.y * normal.y + normal.z * normal.z) <= 1e-8f;
33}
34
44void emitTriangle(HexMeshData& out, const SurfaceVertex& a, const SurfaceVertex& b, const SurfaceVertex& c,
45 const HexNoise& noise) {
46 const HexVec3 pa = noise.perturb(a.position);
47 const HexVec3 pb = noise.perturb(b.position);
48 const HexVec3 pc = noise.perturb(c.position);
49 if (isDegenerate(pa, pb, pc)) return;
50 out.addTriangle(out.addVertex(pa, a.u, a.v), out.addVertex(pb, b.u, b.v), out.addVertex(pc, c.u, c.v));
51}
52
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]] HexVec3 acrossDirection(HexVec3 forward) noexcept {
67 const float length = std::sqrt(forward.x * forward.x + forward.z * forward.z);
68 if (length <= 1e-6f) return HexVec3{0.f, 0.f, 0.f};
69 return HexVec3{forward.z / length, 0.f, -forward.x / length};
70}
71
73struct StripStation {
74 HexVec3 position;
75 HexVec3 across;
76 float halfWidth = 0.f;
77 float u = 0.f;
78};
79
93void emitSegment(HexMeshData& out, const StripStation& from, const StripStation& to, const HexNoise& noise) {
94 const SurfaceVertex minus0{from.position - from.across * from.halfWidth, from.u, 0.f};
95 const SurfaceVertex plus0{from.position + from.across * from.halfWidth, from.u, 1.f};
96 const SurfaceVertex plus1{to.position + to.across * to.halfWidth, to.u, 1.f};
97 const SurfaceVertex minus1{to.position - to.across * to.halfWidth, to.u, 0.f};
98 emitQuad(out, minus0, plus0, plus1, minus1, noise);
99}
100
101} // namespace
102
103void buildRiverMesh(const HexMap& map, std::int32_t chunkIndex, HexMeshData& out) {
104 out.clear();
105 if (chunkIndex < 0 || chunkIndex >= map.chunkCount()) {
106 out.finalize();
107 return;
108 }
109
110 const HexNoise& noise = map.noise();
112
113 for (std::int32_t row = 0; row < HexMetrics::kChunkSizeZ; ++row) {
114 for (std::int32_t column = 0; column < HexMetrics::kChunkSizeX; ++column) {
115 const HexCoordinates coordinates = map.chunkCell(chunkIndex, column, row);
116 const HexCellData* data = map.cell(coordinates);
117 if (data == nullptr || !map.hasRiver(coordinates)) continue;
118
119 const HexVec3 ground = map.cellGroundPosition(coordinates);
120 const HexVec3 centre{ground.x, HexMetrics::streamBedY(map.elevation(coordinates)), ground.z};
121
122 // Per-edge nodes of the channel, keyed by HexDirection index.
123 std::array<bool, 6> connected{};
124 std::array<bool, 6> incoming{};
125 std::array<bool, 6> outgoing{};
126 std::array<HexVec3, 6> edgePosition{};
127 std::int32_t connectionCount = 0;
128 for (std::int32_t i = 0; i < kHexDirectionCount; ++i) {
129 const HexDirection direction = static_cast<HexDirection>(i);
130 if (!map.hasRiverThrough(coordinates, direction)) continue;
131 connected[i] = true;
132 incoming[i] = data->flags.hasRiverIn(direction);
133 outgoing[i] = data->flags.hasRiverOut(direction);
134 ++connectionCount;
135 // The edge crossing sits on the solid edge midpoint, the same point the
136 // terrain mesh owns on a shared border.
137 edgePosition[i] = centre + HexMetrics::solidEdgeMiddle(direction);
138 }
139 if (connectionCount == 0) continue;
140
141 std::int32_t emitted = 0;
142
143 // Every incoming/outgoing pair bends through the cell centre.
144 for (std::int32_t i = 0; i < kHexDirectionCount; ++i) {
145 if (!incoming[i]) continue;
146 for (std::int32_t j = 0; j < kHexDirectionCount; ++j) {
147 if (!outgoing[j] || j == i) continue;
148 const StripStation start{edgePosition[i], acrossDirection(centre - edgePosition[i]), halfWidth,
149 0.f};
150 const StripStation middle{centre, acrossDirection(edgePosition[j] - edgePosition[i]), halfWidth,
151 0.5f};
152 const StripStation finish{edgePosition[j], acrossDirection(edgePosition[j] - centre), halfWidth,
153 1.f};
154 emitSegment(out, start, middle, noise);
155 emitSegment(out, middle, finish, noise);
156 ++emitted;
157 }
158 }
159
160 // A begin/end cell (HexFlags::hasRiverBeginOrEnd) has exactly one connection, so
161 // there is no pair: the channel tapers from the edge to the cell centre instead.
162 if (emitted == 0 && connectionCount == 1) {
163 for (std::int32_t i = 0; i < kHexDirectionCount; ++i) {
164 if (!connected[i]) continue;
165 if (outgoing[i]) {
166 const StripStation start{centre, acrossDirection(edgePosition[i] - centre), 0.f, 0.5f};
167 const StripStation finish{edgePosition[i], acrossDirection(edgePosition[i] - centre), halfWidth,
168 1.f};
169 emitSegment(out, start, finish, noise);
170 } else {
171 const StripStation start{edgePosition[i], acrossDirection(centre - edgePosition[i]), halfWidth,
172 0.f};
173 const StripStation finish{centre, acrossDirection(centre - edgePosition[i]), 0.f, 0.5f};
174 emitSegment(out, start, finish, noise);
175 }
176 ++emitted;
177 }
178 }
179
180 // Degenerate topologies (for example a sink fed by two rivers) carry no direction
181 // pair at all; join the collected edges in direction order so the cell is not blank.
182 if (emitted == 0) {
183 std::int32_t previous = -1;
184 for (std::int32_t i = 0; i < kHexDirectionCount; ++i) {
185 if (!connected[i]) continue;
186 if (previous >= 0) {
187 const StripStation start{edgePosition[previous],
188 acrossDirection(centre - edgePosition[previous]), halfWidth, 0.f};
189 const StripStation middle{centre, acrossDirection(edgePosition[i] - edgePosition[previous]),
190 halfWidth, 0.5f};
191 const StripStation finish{edgePosition[i], acrossDirection(edgePosition[i] - centre), halfWidth,
192 1.f};
193 emitSegment(out, start, middle, noise);
194 emitSegment(out, middle, finish, noise);
195 ++emitted;
196 }
197 previous = i;
198 }
199 }
200 }
201 }
202
203 out.finalize();
204}
205
206} // namespace eve::hexmap
Duration start
std::string from
float length
Definition CaveMesh.cpp:94
int column
float u
Definition Grass.cpp:233
std::uint32_t ab
std::uint32_t ac
Packed per-cell state records of the hex map.
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
HexVec3 across
std::int32_t c
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
Texture * normal
graphics::Canvas * previous
float halfWidth
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
const HexNoise & noise() const noexcept
Deterministic noise field shared by this map's mesh builders.
Definition HexMap.h:110
bool hasRiver(HexCoordinates c) const noexcept
Definition HexMap.cpp:332
std::int32_t elevation(HexCoordinates c) const noexcept
Definition HexMap.cpp:300
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
bool hasRiverThrough(HexCoordinates c, HexDirection d) const noexcept
Definition HexMap.cpp:340
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 constexpr float kRiverSurfaceScale
Width of the river channel.
Definition HexMetrics.h:146
static HexVec3 solidEdgeMiddle(HexDirection d) noexcept
Midpoint of the solid edge of direction.
Definition HexMetrics.h:206
static float streamBedY(int elevation) noexcept
Y coordinate of a river bed inside a cell.
Definition HexMetrics.h:269
static constexpr int kChunkSizeZ
Chunk size in the Z dimension.
Definition HexMetrics.h:158
static constexpr float innerRadius() noexcept
Inner radius of a hex cell.
Definition HexMetrics.h:176
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
std::vector< double > forward(const Policy &p, const Observation &o)
Forward.
Definition Learning.h:65
constexpr std::int32_t kHexDirectionCount
Number of hex edges / facing directions.
Definition HexMetrics.h:62
void buildRiverMesh(const HexMap &map, std::int32_t chunkIndex, HexMeshData &out)
Builds the river channel surface of one chunk.
HexDirection
Hex facing directions, counter-clockwise from north-east.
Definition HexMetrics.h:59
One cell of the hex map: packed values plus packed flags.
Definition HexCell.h:244
Axial coordinates of one hex cell.
Minimal 3-component float vector used by the hex mesh builders.
Definition HexMetrics.h:18