载入中...
搜索中...
未找到
LinearStructure.cpp
浏览该文件的文档.
3
4#include <algorithm>
5#include <cmath>
6#include <string>
7
8namespace eve::procgen {
9namespace {
10
11struct V3 {
12 float x = 0.f, y = 0.f, z = 0.f;
13};
14
15V3 operator+(V3 a, V3 b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
16V3 operator-(V3 a, V3 b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
17V3 operator*(V3 a, float s) { return {a.x * s, a.y * s, a.z * s}; }
18V3 operator-(V3 a) { return {-a.x, -a.y, -a.z}; }
19float dot(V3 a, V3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
20V3 cross(V3 a, V3 b) {
21 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
22}
23V3 normalize(V3 a) {
24 const float l = std::sqrt(dot(a, a));
25 return l > 1e-8f ? V3{a.x / l, a.y / l, a.z / l} : V3{0.f, 1.f, 0.f};
26}
27
28// Per-kind geometry defaults.
29struct Dims {
30 float height = 1.f;
31 float depth = 1.f;
32 float thickness = 0.1f;
33};
34
35// Face corner tables for an oriented box (local axes u,v,w). Corner indices
36// reference local coords: 0=(-U,-V,-W) 1=(+U,-V,-W) 2=(+U,+V,-W) 3=(-U,+V,-W)
37// 4=(-U,-V,+W) 5=(+U,-V,+W) 6=(+U,+V,+W) 7=(-U,+V,+W).
38const int kFaceCorners[6][4] = {
39 {1, 2, 6, 5}, // +U
40 {4, 7, 3, 0}, // -U
41 {3, 7, 6, 2}, // +V
42 {0, 1, 5, 4}, // -V
43 {4, 5, 6, 7}, // +W
44 {1, 0, 3, 2}, // -W
45};
46const int kFaceNormalAxis[6] = {0, 0, 1, 1, 2, 2};
47
48// UV axis priority so the long/horizontal axis maps to texture U: U(0) > W(2) > V(1).
49int axisPriority(int axis) { return axis == 0 ? 2 : (axis == 2 ? 1 : 0); }
50
51void emitQuad(MeshBuild &out, const V3 corner[4], const V3 &normal, const V3 &t1, const V3 &t2,
52 float uvScale) {
53 for (int i = 0; i < 4; ++i) {
54 const V3 &c = corner[i];
55 out.addVertex(c.x, c.y, c.z, normal.x, normal.y, normal.z, dot(c, t1) * uvScale,
56 dot(c, t2) * uvScale);
57 }
58 const uint32_t base = uint32_t(out.getVertexCount()) - 4u;
59 out.addTriangle(base, base + 1, base + 2);
60 out.addTriangle(base, base + 2, base + 3);
61}
62
63void emitOrientedBox(MeshBuild &out, const V3 &center, const V3 &u, const V3 &v, const V3 &w,
64 float ex, float ey, float ez, float uvScale) {
65 const float lu[8] = {-ex, ex, ex, -ex, -ex, ex, ex, -ex};
66 const float lv[8] = {-ey, -ey, ey, ey, -ey, -ey, ey, ey};
67 const float lw[8] = {-ez, -ez, -ez, -ez, ez, ez, ez, ez};
68 V3 world[8];
69 for (int i = 0; i < 8; ++i) world[i] = center + u * lu[i] + v * lv[i] + w * lw[i];
70
71 for (int f = 0; f < 6; ++f) {
72 const int a = kFaceNormalAxis[f];
73 V3 n = a == 0 ? u : (a == 1 ? v : w);
74 if (f % 2 == 1) n = -n;
75 // Two tangent axes are the two axes != normal axis.
76 int others[2];
77 int oi = 0;
78 for (int ax = 0; ax < 3; ++ax)
79 if (ax != a) others[oi++] = ax;
80 V3 t1, t2;
81 if (axisPriority(others[0]) >= axisPriority(others[1])) {
82 t1 = others[0] == 0 ? u : (others[0] == 1 ? v : w);
83 t2 = others[1] == 0 ? u : (others[1] == 1 ? v : w);
84 } else {
85 t1 = others[1] == 0 ? u : (others[1] == 1 ? v : w);
86 t2 = others[0] == 0 ? u : (others[0] == 1 ? v : w);
87 }
88 V3 corner[4];
89 for (int c = 0; c < 4; ++c) corner[c] = world[kFaceCorners[f][c]];
90 emitQuad(out, corner, n, t1, t2, uvScale);
91 }
92}
93
94// Axis-aligned box; `x0..x1` along X, `y0..y1` along Y, `z0..z1` along Z.
95void addAABB(MeshBuild &out, float x0, float y0, float z0, float x1, float y1, float z1,
96 float uvScale) {
97 const V3 center{(x0 + x1) * 0.5f, (y0 + y1) * 0.5f, (z0 + z1) * 0.5f};
98 emitOrientedBox(out, center, V3{1, 0, 0}, V3{0, 1, 0}, V3{0, 0, 1}, (x1 - x0) * 0.5f,
99 (y1 - y0) * 0.5f, (z1 - z0) * 0.5f, uvScale);
100}
101
102// Oriented square beam from a to b with half-thickness `radius`.
103void addBeam(MeshBuild &out, V3 a, V3 b, float radius, float uvScale) {
104 const V3 d = b - a;
105 const float len = std::sqrt(dot(d, d));
106 if (len < 1e-6f) return;
107 const V3 n = d * (1.f / len);
108 V3 t = std::fabs(n.x) < 0.9f ? V3{0, 1, 0} : V3{0, 0, 1};
109 const V3 v = normalize(t - n * dot(t, n));
110 const V3 w = cross(n, v);
111 emitOrientedBox(out, (a + b) * 0.5f, n, v, w, len * 0.5f, radius, radius, uvScale);
112}
113
114// --- Wooden fence: posts at each unit boundary + three horizontal rails. ---
115void buildFence(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
116 const float postW = dm.thickness;
117 const float railTh = dm.thickness * 0.55f;
118 const float railD = dm.depth * 0.5f;
119 for (int k = 0; k < segments; ++k) {
120 const float x0 = float(k) * L;
121 addAABB(out, x0, 0.f, -dm.depth * 0.5f, x0 + postW, dm.height, dm.depth * 0.5f, uvScale);
122 for (int r = 0; r < 3; ++r) {
123 const float y = dm.height * (0.22f + 0.28f * float(r));
124 addAABB(out, x0 + postW, y, -railD, x0 + L, y + railTh, railD, uvScale);
125 }
126 }
127}
128
129// --- Stone wall: rubble body + wider top cap. ---
130void buildStoneWall(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
131 for (int k = 0; k < segments; ++k) {
132 const float x0 = float(k) * L;
133 addAABB(out, x0, 0.f, -dm.depth * 0.5f, x0 + L, dm.height, dm.depth * 0.5f, uvScale);
134 addAABB(out, x0, dm.height, -dm.depth * 0.55f, x0 + L, dm.height + dm.thickness,
135 dm.depth * 0.55f, uvScale);
136 }
137}
138
139// --- Bridge: deck + handrails + cross beams. ---
140void buildBridge(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
141 const float deckTh = dm.thickness;
142 const float railInset = 0.18f;
143 const float railTh = 0.08f;
144 for (int k = 0; k < segments; ++k) {
145 const float x0 = float(k) * L;
146 addAABB(out, x0, 0.f, -dm.depth * 0.5f, x0 + L, deckTh, dm.depth * 0.5f, uvScale);
147 for (int s = 0; s < 2; ++s) {
148 const float z = (s == 0 ? -1.f : 1.f) * (dm.depth * 0.5f - railInset);
149 addAABB(out, x0, deckTh, z - railTh * 0.5f, x0 + L, deckTh + dm.height,
150 z + railTh * 0.5f, uvScale);
151 }
152 const float beamTh = 0.10f;
153 addAABB(out, x0 + L / 3.f, 0.f, -dm.depth * 0.5f, x0 + L / 3.f + beamTh, deckTh,
154 dm.depth * 0.5f, uvScale);
155 addAABB(out, x0 + 2.f * L / 3.f, 0.f, -dm.depth * 0.5f, x0 + 2.f * L / 3.f + beamTh,
156 deckTh, dm.depth * 0.5f, uvScale);
157 }
158}
159
160// --- Great Wall: body, walkway, outer merlons + inner guard rail. ---
161void buildGreatWall(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
162 const int n = std::max(2, int(std::lround(L / 0.5f)));
163 const float period = L / float(n);
164 const float mw = period * 0.5f;
165 const float merlonH = dm.thickness * 1.2f;
166 const float mDepth = dm.depth * 0.45f;
167 const float innerTh = 0.12f;
168 const float innerH = 0.25f;
169 for (int k = 0; k < segments; ++k) {
170 const float x0 = float(k) * L;
171 addAABB(out, x0, 0.f, -dm.depth * 0.5f, x0 + L, dm.height, dm.depth * 0.5f, uvScale);
172 for (int i = 0; i < n; ++i) {
173 const float mx = x0 + float(i) * period;
174 addAABB(out, mx, dm.height, dm.depth * 0.5f - mDepth, mx + mw, dm.height + merlonH,
175 dm.depth * 0.5f, uvScale);
176 }
177 addAABB(out, x0, dm.height, -dm.depth * 0.5f, x0 + L, dm.height + innerH,
178 -dm.depth * 0.5f + innerTh, uvScale);
179 }
180}
181
182// --- Hedge: leafy base mass + overlapping rounded bush blobs. ---
183void buildHedge(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
184 const int n = std::max(2, int(std::lround(L / 0.6f)));
185 const float period = L / float(n);
186 const float bw = period * 0.7f;
187 for (int k = 0; k < segments; ++k) {
188 const float x0 = float(k) * L;
189 addAABB(out, x0, 0.f, -dm.depth * 0.5f, x0 + L, dm.height * 0.55f, dm.depth * 0.5f,
190 uvScale);
191 for (int i = 0; i < n; ++i) {
192 const float cx = x0 + float(i) * period + period * 0.5f;
193 addAABB(out, cx - bw * 0.5f, dm.height * 0.45f, -dm.depth * 0.52f,
194 cx + bw * 0.5f, dm.height * 0.95f, dm.depth * 0.52f, uvScale);
195 }
196 }
197}
198
199// --- Cheval de frise: top rail + crossed X legs + feet. ---
200void buildChevalDeFrise(MeshBuild &out, int segments, float L, const Dims &dm, float uvScale) {
201 const float railTh = dm.thickness * 1.4f;
202 const float bw = L * 0.35f;
203 const float dz = dm.depth * 0.5f;
204 for (int k = 0; k < segments; ++k) {
205 const float x0 = float(k) * L;
206 addAABB(out, x0, dm.height - railTh, -dm.depth * 0.5f, x0 + L, dm.height,
207 dm.depth * 0.5f, uvScale);
208 const float cx = x0 + L * 0.5f;
209 const float legRadius = dm.thickness * 0.5f;
210 addBeam(out, V3{cx - bw, 0.f, dz}, V3{cx + bw, dm.height - railTh * 0.5f, -dz},
211 legRadius, uvScale);
212 addBeam(out, V3{cx - bw, 0.f, -dz}, V3{cx + bw, dm.height - railTh * 0.5f, dz},
213 legRadius, uvScale);
214 addAABB(out, cx - bw - 0.06f, 0.f, -dm.depth * 0.55f, cx - bw + 0.10f, 0.06f,
215 dm.depth * 0.55f, uvScale);
216 addAABB(out, cx + bw - 0.06f, 0.f, -dm.depth * 0.55f, cx + bw + 0.10f, 0.06f,
217 dm.depth * 0.55f, uvScale);
218 }
219}
220
221} // namespace
222
223bool generateLinearStructure(const std::string &kind, const Params &params, MeshBuild &out,
224 std::string &error) {
225 const int segments = std::clamp(params.getInt("segments", 6), 1, 256);
226 const float L = std::max(0.1f, params.getFloat("segLength", 1.f));
227 const float uvScale = std::max(0.f, params.getFloat("uvRepeat", 2.f));
228
229 Dims dm;
230 if (kind == "mesh.fence") {
231 dm = {1.1f, 0.09f, 0.08f};
232 } else if (kind == "mesh.stonewall") {
233 dm = {1.0f, 0.5f, 0.12f};
234 } else if (kind == "mesh.bridge") {
235 dm = {1.2f, 2.4f, 0.12f};
236 } else if (kind == "mesh.greatwall") {
237 dm = {1.6f, 1.0f, 0.18f};
238 } else if (kind == "mesh.hedge") {
239 dm = {0.9f, 0.8f, 0.06f};
240 } else if (kind == "mesh.chevaldefrise") {
241 dm = {0.9f, 0.7f, 0.08f};
242 } else {
243 error = "unknown linear structure '" + kind +
244 "' (use mesh.fence|mesh.stonewall|mesh.bridge|mesh.greatwall|mesh.hedge|"
245 "mesh.chevaldefrise)";
246 return false;
247 }
248 dm.height = std::max(0.05f, params.getFloat("height", dm.height));
249 dm.depth = std::max(0.02f, params.getFloat("depth", dm.depth));
250 dm.thickness = std::max(0.005f, params.getFloat("thickness", dm.thickness));
251
252 out.clear();
253 out.reserve(segments * 400, segments * 1200);
254
255 if (kind == "mesh.fence") {
256 buildFence(out, segments, L, dm, uvScale);
257 } else if (kind == "mesh.stonewall") {
258 buildStoneWall(out, segments, L, dm, uvScale);
259 } else if (kind == "mesh.bridge") {
260 buildBridge(out, segments, L, dm, uvScale);
261 } else if (kind == "mesh.greatwall") {
262 buildGreatWall(out, segments, L, dm, uvScale);
263 } else if (kind == "mesh.hedge") {
264 buildHedge(out, segments, L, dm, uvScale);
265 } else if (kind == "mesh.chevaldefrise") {
266 buildChevalDeFrise(out, segments, L, dm, uvScale);
267 }
268
269 const float scale = std::max(0.01f, params.getFloat("scale", 1.f));
270 if (scale != 1.f) {
271 for (float &p : out.positions()) p *= scale;
272 }
273
274 if (out.empty()) {
275 error = kind + ": empty mesh (check segments/segLength/scale)";
276 return false;
277 }
278 out.setMeta("algorithm", kind);
279 out.setMeta("segments", std::to_string(segments));
280 return true;
281}
282
284 registry.registerRecipe(
285 "mesh.fence", [](const Params &p, MeshBuild &o, std::string &e) {
286 return generateLinearStructure("mesh.fence", p, o, e);
287 });
288 registry.registerRecipe(
289 "mesh.stonewall", [](const Params &p, MeshBuild &o, std::string &e) {
290 return generateLinearStructure("mesh.stonewall", p, o, e);
291 });
292 registry.registerRecipe(
293 "mesh.bridge", [](const Params &p, MeshBuild &o, std::string &e) {
294 return generateLinearStructure("mesh.bridge", p, o, e);
295 });
296 registry.registerRecipe(
297 "mesh.greatwall", [](const Params &p, MeshBuild &o, std::string &e) {
298 return generateLinearStructure("mesh.greatwall", p, o, e);
299 });
300 registry.registerRecipe(
301 "mesh.hedge", [](const Params &p, MeshBuild &o, std::string &e) {
302 return generateLinearStructure("mesh.hedge", p, o, e);
303 });
304 registry.registerRecipe(
305 "mesh.chevaldefrise", [](const Params &p, MeshBuild &o, std::string &e) {
306 return generateLinearStructure("mesh.chevaldefrise", p, o, e);
307 });
308}
309
310} // namespace eve::procgen
float cx
Definition CardTypes.cpp:31
Tok kind
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
int w
std::string error
float depth
float thickness
uint32_t a
uint32_t b
uint32_t c
Texture * normal
float f
glm::vec4 p[6]
int d
int v
float scale
Definition TreeMesh.cpp:122
uint32_t s
Definition Weather.cpp:28
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:14
const std::vector< float > & positions() const
Definition MeshBuild.h:39
void reserve(int vertexCount, int indexCount)
Definition MeshBuild.cpp:13
void setMeta(const std::string &key, const std::string &value)
Definition MeshBuild.cpp:81
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Generation parameters. Algorithm-specific keys live in values as strings (no overloads; typed setters...
Definition Params.h:13
float getFloat(const std::string &key, float defaultValue) const
Definition Params.cpp:32
int getInt(const std::string &key, int defaultValue) const
Definition Params.cpp:23
void registerLinearStructureRecipes(MeshRecipeRegistry &registry)
Register all built-in linear structure mesh recipes into a registry.
bool generateLinearStructure(const std::string &kind, const Params &params, MeshBuild &out, std::string &error)
Procedural linear, tileable structures (fences, walls, bridges, the Great Wall, hedges,...