13 float x = 0.f,
y = 0.f,
z = 0.f;
16Vec3 sub(Vec3
a, Vec3
b) {
return {
a.x -
b.x,
a.y -
b.y,
a.z -
b.z}; }
17Vec3 cross(Vec3
a, Vec3
b) {
18 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};
20Vec3 addScaled(Vec3
p, Vec3
n,
float s) {
return {
p.x +
n.x *
s,
p.y +
n.y *
s,
p.z +
n.z *
s}; }
22Vec3 normalize(Vec3
v) {
23 const float len = std::sqrt(
v.x *
v.x +
v.y *
v.y +
v.z *
v.z);
24 if (len <= 1e-8f)
return {0.f, 1.f, 0.f};
25 return {
v.x / len,
v.y / len,
v.z / len};
30Vec3 lerpFace(Vec3 p0, Vec3 p1, Vec3 p2, Vec3 p3,
float u,
float v) {
32 Vec3 base = {p0.x + (p1.x - p0.x) *
w, p0.y + (p1.y - p0.y) *
w, p0.z + (p1.z - p0.z) *
w};
33 Vec3 top = {p3.x + (p2.x - p3.x) *
w, p3.y + (p2.y - p3.y) *
w, p3.z + (p2.z - p3.z) *
w};
34 return {base.x + (top.x - base.x) *
v, base.y + (top.y - base.y) *
v,
35 base.z + (top.z - base.z) *
v};
40void addQuad(MeshBuild &out, Vec3 p0, Vec3 p1, Vec3 p2, Vec3 p3, Vec3
n,
float u,
float v) {
41 const Vec3 winding = cross(sub(p1, p0), sub(p3, p0));
42 const float d = winding.x *
n.x + winding.y *
n.y + winding.z *
n.z;
48 const uint32_t base = uint32_t(out.getVertexCount());
49 out.addVertex(p0.x, p0.y, p0.z,
n.x,
n.y,
n.z,
u,
v);
50 out.addVertex(p1.x, p1.y, p1.z,
n.x,
n.y,
n.z,
u,
v);
51 out.addVertex(p2.x, p2.y, p2.z,
n.x,
n.y,
n.z,
u,
v);
52 out.addVertex(p3.x, p3.y, p3.z,
n.x,
n.y,
n.z,
u,
v);
53 out.addTriangle(base, base + 1, base + 2);
54 out.addTriangle(base, base + 2, base + 3);
59void addFacade(MeshBuild &out, Vec3 p0, Vec3 p1, Vec3 p2, Vec3 p3, Vec3
n,
int cols,
int rows,
61 addQuad(out, p0, p1, p2, p3,
n, 0.25f, 0.5f);
62 const float margin = 0.12f;
63 for (
int r = 0; r < rows; ++r) {
64 const float v0 = float(r) / float(rows);
65 const float v1 = float(r + 1) / float(rows);
66 const float vi0 = v0 +
margin * (v1 - v0);
67 const float vi1 = v1 -
margin * (v1 - v0);
68 for (
int c = 0;
c < cols; ++
c) {
69 const float u0 = float(
c) / float(cols);
70 const float u1 = float(
c + 1) / float(cols);
71 const float ui0 = u0 +
margin * (u1 - u0);
72 const float ui1 = u1 -
margin * (u1 - u0);
73 const Vec3 w0 = addScaled(lerpFace(p0, p1, p2, p3, ui0, vi0),
n, windowDepth);
74 const Vec3 w1 = addScaled(lerpFace(p0, p1, p2, p3, ui1, vi0),
n, windowDepth);
75 const Vec3 w2 = addScaled(lerpFace(p0, p1, p2, p3, ui1, vi1),
n, windowDepth);
76 const Vec3 w3 = addScaled(lerpFace(p0, p1, p2, p3, ui0, vi1),
n, windowDepth);
77 addQuad(out, w0, w1, w2, w3,
n, 0.75f, 0.5f);
83void addBox(MeshBuild &out,
float cx,
float cz,
float hw,
float hd,
float y0,
float y1,
85 const Vec3 p000 = {
cx - hw, y0, cz - hd};
86 const Vec3 p100 = {
cx + hw, y0, cz - hd};
87 const Vec3 p110 = {
cx + hw, y0, cz + hd};
88 const Vec3 p010 = {
cx - hw, y0, cz + hd};
89 const Vec3 p001 = {
cx - hw, y1, cz - hd};
90 const Vec3 p101 = {
cx + hw, y1, cz - hd};
91 const Vec3 p111 = {
cx + hw, y1, cz + hd};
92 const Vec3 p011 = {
cx - hw, y1, cz + hd};
93 addQuad(out, p000, p100, p101, p001, {0, 0, 1}, 0.25f, 0.5f);
94 addQuad(out, p110, p010, p011, p111, {0, 0, -1}, 0.25f, 0.5f);
95 addQuad(out, p100, p110, p111, p101, {1, 0, 0}, 0.25f, 0.5f);
96 addQuad(out, p010, p000, p001, p011, {-1, 0, 0}, 0.25f, 0.5f);
97 if (top) addQuad(out, p001, p101, p111, p011, {0, 1, 0}, 0.25f, 0.5f);
103 const int tiers = std::clamp(params.
getInt(
"tiers", 5), 1, 24);
104 const float baseWidth = std::max(0.5f, params.
getFloat(
"baseWidth", 10.f));
105 const float baseDepth = std::max(0.5f, params.
getFloat(
"baseDepth", 10.f));
106 const float tierHeight = std::max(0.5f, params.
getFloat(
"tierHeight", 6.f));
107 const float setback = std::clamp(params.
getFloat(
"setback", 0.08f), 0.f, 0.6f);
108 const int windowCols = std::clamp(params.
getInt(
"windowCols", 6), 1, 24);
109 const int windowRows = std::clamp(params.
getInt(
"windowRows", 4), 1, 24);
110 const float windowDepth = std::max(0.f, params.
getFloat(
"windowDepth", 0.04f));
111 const float spireHeight = std::max(0.f, params.
getFloat(
"spireHeight", 0.f));
114 const float cx = 0.f, cz = 0.f;
117 for (
int t = 0; t < tiers; ++t) {
118 const float shrink = 1.f - setback * float(t);
119 const float hw = baseWidth * 0.5f * shrink;
120 const float hd = baseDepth * 0.5f * shrink;
121 const float y0 = yCursor;
122 const float y1 = yCursor + tierHeight;
124 const Vec3 p000 = {
cx - hw, y0, cz - hd};
125 const Vec3 p100 = {
cx + hw, y0, cz - hd};
126 const Vec3 p110 = {
cx + hw, y0, cz + hd};
127 const Vec3 p010 = {
cx - hw, y0, cz + hd};
128 const Vec3 p001 = {
cx - hw, y1, cz - hd};
129 const Vec3 p101 = {
cx + hw, y1, cz - hd};
130 const Vec3 p111 = {
cx + hw, y1, cz + hd};
131 const Vec3 p011 = {
cx - hw, y1, cz + hd};
133 addFacade(out, p000, p100, p101, p001, {0, 0, 1}, windowCols, windowRows, windowDepth);
134 addFacade(out, p110, p010, p011, p111, {0, 0, -1}, windowCols, windowRows, windowDepth);
135 addFacade(out, p100, p110, p111, p101, {1, 0, 0}, windowCols, windowRows, windowDepth);
136 addFacade(out, p010, p000, p001, p011, {-1, 0, 0}, windowCols, windowRows, windowDepth);
142 const float shrink = 1.f - setback * float(tiers - 1);
143 const float hw = baseWidth * 0.5f * shrink;
144 const float hd = baseDepth * 0.5f * shrink;
145 const float y1 = yCursor;
146 addQuad(out, {
cx - hw, y1, cz - hd}, {
cx + hw, y1, cz - hd}, {
cx + hw, y1, cz + hd},
147 {
cx - hw, y1, cz + hd}, {0, 1, 0}, 0.25f, 0.5f);
150 if (spireHeight > 0.f) {
151 const float s = std::min(baseWidth, baseDepth) * 0.045f;
152 addBox(out,
cx, cz,
s,
s, yCursor, yCursor + spireHeight,
true);
156 error =
"mesh.skyscraper: generated an empty mesh";
159 out.
setMeta(
"algorithm",
"mesh.skyscraper");
160 out.
setMeta(
"tiers", std::to_string(tiers));
161 out.
setMeta(
"windowCols", std::to_string(windowCols));
162 out.
setMeta(
"windowRows", std::to_string(windowRows));
163 out.
setMeta(
"spireHeight", std::to_string(spireHeight));
164 out.
setMeta(
"height", std::to_string(
float(tiers) * tierHeight + spireHeight));
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
void setMeta(const std::string &key, const std::string &value)
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
bool generateSkyscraperMesh(const Params ¶ms, MeshBuild &out, std::string &error)
Build a deterministic procedural skyscraper. Registered as the mesh.skyscraper recipe.