12 float x = 0.f,
y = 0.f,
z = 0.f;
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; }
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};
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};
38const int kFaceCorners[6][4] = {
46const int kFaceNormalAxis[6] = {0, 0, 1, 1, 2, 2};
49int axisPriority(
int axis) {
return axis == 0 ? 2 : (axis == 2 ? 1 : 0); }
51void emitQuad(MeshBuild &out,
const V3 corner[4],
const V3 &
normal,
const V3 &t1,
const V3 &t2,
53 for (
int i = 0; i < 4; ++i) {
54 const V3 &
c = corner[i];
56 dot(
c, t2) * uvScale);
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);
63void emitOrientedBox(MeshBuild &out,
const V3 ¢er,
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};
69 for (
int i = 0; i < 8; ++i) world[i] = center +
u * lu[i] +
v * lv[i] +
w * lw[i];
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;
78 for (
int ax = 0; ax < 3; ++ax)
79 if (ax !=
a) others[oi++] = ax;
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);
85 t1 = others[1] == 0 ?
u : (others[1] == 1 ?
v :
w);
86 t2 = others[0] == 0 ?
u : (others[0] == 1 ?
v :
w);
89 for (
int c = 0;
c < 4; ++
c) corner[
c] = world[kFaceCorners[
f][
c]];
90 emitQuad(out, corner,
n, t1, t2, uvScale);
95void addAABB(MeshBuild &out,
float x0,
float y0,
float z0,
float x1,
float y1,
float z1,
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);
103void addBeam(MeshBuild &out, V3
a, V3
b,
float radius,
float uvScale) {
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);
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);
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);
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);
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);
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);
177 addAABB(out, x0, dm.height, -dm.depth * 0.5f, x0 + L, dm.height + innerH,
178 -dm.depth * 0.5f + innerTh, uvScale);
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,
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);
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},
212 addBeam(out, V3{
cx - bw, 0.f, -dz}, V3{
cx + bw, dm.height - railTh * 0.5f, dz},
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);
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));
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};
243 error =
"unknown linear structure '" +
kind +
244 "' (use mesh.fence|mesh.stonewall|mesh.bridge|mesh.greatwall|mesh.hedge|"
245 "mesh.chevaldefrise)";
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));
253 out.
reserve(segments * 400, segments * 1200);
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);
269 const float scale = std::max(0.01f, params.
getFloat(
"scale", 1.f));
275 error =
kind +
": empty mesh (check segments/segLength/scale)";
279 out.
setMeta(
"segments", std::to_string(segments));
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
const std::vector< float > & positions() const
void reserve(int vertexCount, int indexCount)
void setMeta(const std::string &key, const std::string &value)
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Generation parameters. Algorithm-specific keys live in values as strings (no overloads; typed setters...
float getFloat(const std::string &key, float defaultValue) const
int getInt(const std::string &key, int defaultValue) const
void registerLinearStructureRecipes(MeshRecipeRegistry ®istry)
Register all built-in linear structure mesh recipes into a registry.
bool generateLinearStructure(const std::string &kind, const Params ¶ms, MeshBuild &out, std::string &error)
Procedural linear, tileable structures (fences, walls, bridges, the Great Wall, hedges,...