12 float x = 0.f,
y = 0.f,
z = 0.f;
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 &
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};
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};
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));
300 "thickness",
"Thickness",
thickness, 0.005f, 10.f, 0.005f));
306 schema(
"mesh.fence",
"Fence", 1.1f, 0.09f, 0.08f),
311 schema(
"mesh.stonewall",
"Stone Wall", 1.f, 0.5f, 0.12f),
316 schema(
"mesh.bridge",
"Bridge", 1.2f, 2.4f, 0.12f),
321 schema(
"mesh.greatwall",
"Great Wall", 1.6f, 1.f, 0.18f),
326 schema(
"mesh.hedge",
"Hedge", 0.9f, 0.8f, 0.06f),
331 schema(
"mesh.chevaldefrise",
"Cheval de Frise", 0.9f, 0.7f, 0.08f),
std::array< float, 3 > scale
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
const std::vector< float > & positions() const
Positions.
void reserve(int vertexCount, int indexCount)
Reserve.
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
EVENGINE_API_DOMAINS public API.
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Register a recipe without metadata.
Owning, typed generation parameters.
std::vector< ParamSpec > params
Vec3 operator-(Vec3 lhs, Vec3 rhs)
Operator -.
Vec3 operator+(Vec3 lhs, Vec3 rhs)
Operator +.
Vec3 operator*(Vec3 value, float scale)
Operator *.
Vec2 normalize(const Vec2 &a)
Normalize.
double dot(const Vec2 &a, const Vec2 &b)
Dot.
double cross(const Vec2 &a, const Vec2 &b)
Cross.
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,...
int axis(int64_t a, size_t rank)
Axis.
static ParamDescriptor floating(std::string key, std::string label, float defaultValue, float minimum, float maximum, float step)
Construct a bounded floating-point descriptor.
static ParamDescriptor integer(std::string key, std::string label, int defaultValue, int minimum, int maximum, int step=1)
Construct a bounded integer descriptor.
Complete metadata and parameter schema shared by every procgen recipe family.
std::vector< ParamDescriptor > params