载入中...
搜索中...
未找到
PrototypeKit.cpp
浏览该文件的文档.
2
6
7#include <algorithm>
8#include <array>
9#include <cmath>
10#include <string>
11
12namespace eve::procgen {
13namespace {
14
20using prototype_detail::Vec3;
21
22constexpr std::array<PrototypePieceDescriptor, 75> kPieces{{
23 {"arrow", "Arrow", 1.0f, 0.18f, 2.0f},
24 {"box", "Open Box", 1.5f, 1.0f, 1.5f},
25 {"coin", "Coin", 0.8f, 0.12f, 0.8f},
26 {"cone", "Cone", 1.0f, 1.5f, 1.0f},
27 {"cone1", "Cone Low", 1.2f, 0.8f, 1.2f},
28 {"cone2", "Cone Tall", 0.8f, 2.0f, 0.8f},
29 {"cone3", "Frustum", 1.0f, 1.5f, 1.0f},
30 {"cone4", "Pyramid", 1.2f, 1.5f, 1.2f},
31 {"cube", "Cube", 1.0f, 1.0f, 1.0f},
32 {"cube4", "Low Block", 1.0f, 0.5f, 1.0f},
33 {"cube5", "Tall Block", 1.0f, 2.0f, 1.0f},
34 {"cube6", "Wide Block", 2.0f, 1.0f, 1.0f},
35 {"cube7", "Platform Block", 2.0f, 0.35f, 2.0f},
36 {"cube8", "Beam Block", 3.0f, 0.4f, 0.4f},
37 {"cube9", "Inset Block", 1.2f, 1.2f, 1.2f},
38 {"cylinder", "Cylinder", 1.0f, 1.0f, 1.0f},
39 {"cylinder1", "Cylinder Detailed", 1.0f, 1.0f, 1.0f},
40 {"cylinder2", "Cylinder Low", 1.3f, 0.5f, 1.3f},
41 {"cylinder3", "Cylinder Tall", 0.8f, 2.0f, 0.8f},
42 {"cylinder4", "Hex Cylinder", 1.0f, 1.0f, 1.0f},
43 {"cylinder5", "Octagonal Column", 1.0f, 2.0f, 1.0f},
44 {"door", "Door", 1.0f, 2.1f, 0.15f},
45 {"door1", "Door Framed", 1.2f, 2.2f, 0.2f},
46 {"door2", "Door Double", 1.8f, 2.2f, 0.18f},
47 {"door3", "Door Reinforced", 1.2f, 2.2f, 0.22f},
48 {"fence-edge", "Fence Edge", 0.4f, 1.2f, 0.4f},
49 {"fence-wood", "Wood Fence", 2.5f, 1.2f, 0.2f},
50 {"fence", "Fence", 2.5f, 1.2f, 0.16f},
51 {"fence2", "Fence Dense", 2.5f, 1.4f, 0.18f},
52 {"fence3", "Fence Low", 2.5f, 0.8f, 0.18f},
53 {"ground-corner", "Ground Corner", 2.0f, 0.15f, 2.0f},
54 {"ground", "Ground Tile", 2.0f, 0.12f, 2.0f},
55 {"ground1", "Ground Slab", 2.0f, 0.3f, 2.0f},
56 {"key", "Key", 1.8f, 0.12f, 0.7f},
57 {"ladder", "Ladder", 1.0f, 2.5f, 0.16f},
58 {"ladder1", "Wide Ladder", 1.4f, 3.0f, 0.18f},
59 {"pillar", "Pillar", 0.8f, 2.5f, 0.8f},
60 {"pillar1", "Pillar Square", 0.8f, 2.5f, 0.8f},
61 {"pillar2", "Pillar Round", 0.8f, 2.5f, 0.8f},
62 {"pillar3", "Pillar Heavy", 1.0f, 3.0f, 1.0f},
63 {"pillar4", "Pillar Tapered", 0.9f, 2.5f, 0.9f},
64 {"railing-edge", "Railing Edge", 0.35f, 1.0f, 0.35f},
65 {"railing", "Railing", 2.5f, 1.0f, 0.12f},
66 {"ramp", "Ramp", 2.0f, 1.0f, 2.0f},
67 {"ramp1", "Ramp Wide", 3.0f, 1.2f, 2.0f},
68 {"sphere", "Sphere", 1.0f, 1.0f, 1.0f},
69 {"sphere1", "Sphere Detailed", 1.0f, 1.0f, 1.0f},
70 {"sphere2", "Ellipsoid", 1.2f, 1.8f, 1.2f},
71 {"spike", "Spike", 0.5f, 1.2f, 0.5f},
72 {"spikes-big", "Spikes Big", 2.0f, 1.2f, 2.0f},
73 {"spikes-small", "Spikes Small", 2.0f, 0.6f, 2.0f},
74 {"stairs-corner", "Stairs Corner", 2.0f, 1.5f, 2.0f},
75 {"stairs-corner1", "Stairs Corner Wide", 3.0f, 1.5f, 3.0f},
76 {"stairs", "Stairs", 2.0f, 1.5f, 2.5f},
77 {"stairs1", "Stairs Solid", 2.0f, 1.5f, 2.5f},
78 {"stairs2", "Stairs Low", 2.0f, 0.8f, 2.5f},
79 {"toggle-switch", "Toggle Switch", 0.8f, 0.6f, 0.5f},
80 {"torus", "Torus", 1.4f, 0.35f, 1.4f},
81 {"wall-corner-bottom", "Wall Corner Bottom", 2.0f, 1.0f, 2.0f},
82 {"wall-corner-bottom1", "Wall Corner Bottom Thin", 2.0f, 0.6f, 2.0f},
83 {"wall-corner-bottom2", "Wall Corner Bottom Heavy", 2.0f, 1.2f, 2.0f},
84 {"wall-corner-top", "Wall Corner Top", 2.0f, 1.0f, 2.0f},
85 {"wall-corner-top1", "Wall Corner Top Thin", 2.0f, 0.6f, 2.0f},
86 {"wall-corner", "Wall Corner", 2.0f, 3.0f, 2.0f},
87 {"wall-corner1", "Wall Corner Short", 2.0f, 2.0f, 2.0f},
88 {"wall-corner4", "Wall Corner Heavy", 2.0f, 3.0f, 2.0f},
89 {"wall-door", "Wall Door", 3.0f, 3.0f, 0.25f},
90 {"wall-door1", "Wall Door Wide", 4.0f, 3.0f, 0.25f},
91 {"wall-door2", "Wall Door Tall", 3.0f, 4.0f, 0.25f},
92 {"wall-window", "Wall Window", 3.0f, 3.0f, 0.25f},
93 {"wall-window1", "Wall Window Wide", 4.0f, 3.0f, 0.25f},
94 {"wall", "Wall", 3.0f, 3.0f, 0.25f},
95 {"wall1", "Wall Half", 3.0f, 1.5f, 0.25f},
96 {"window", "Window", 1.2f, 1.2f, 0.15f},
97 {"window1", "Window Wide", 1.8f, 1.2f, 0.15f},
98}};
99
100const PrototypePieceDescriptor* findPiece(std::string_view id) {
101 const auto it = std::find_if(kPieces.begin(), kPieces.end(), [id](const auto& piece) { return piece.id == id; });
102 return it == kPieces.end() ? nullptr : &*it;
103}
104
105struct BuildContext {
106 MeshBuild mesh;
107 float w;
108 float h;
109 float d;
110 float t;
112 int steps;
113};
114
115float positiveParam(const Params& params, const char* name, float fallback) {
116 return params.has(name) ? params.getFloat(name, fallback) : fallback;
117}
118
119void addFrame(BuildContext& c, float openingWidth, float openingHeight, float sill, const char* group) {
120 const float side = std::max(c.t, (c.w - openingWidth) * 0.5f);
121 addBox(c.mesh, {-c.w * 0.5f + side * 0.5f, c.h * 0.5f, 0}, {side, c.h, c.d}, group);
122 addBox(c.mesh, {c.w * 0.5f - side * 0.5f, c.h * 0.5f, 0}, {side, c.h, c.d}, group);
123 if (sill > 0.0f) addBox(c.mesh, {0, sill * 0.5f, 0}, {openingWidth, sill, c.d}, group);
124 const float top = std::max(c.t, c.h - sill - openingHeight);
125 addBox(c.mesh, {0, c.h - top * 0.5f, 0}, {openingWidth, top, c.d}, group);
126}
127
128void addCorner(BuildContext& c, const char* group) {
129 addBox(c.mesh, {0, c.h * 0.5f, -c.d * 0.5f + c.t * 0.5f}, {c.w, c.h, c.t}, group);
130 addBox(c.mesh, {-c.w * 0.5f + c.t * 0.5f, c.h * 0.5f, 0}, {c.t, c.h, c.d}, group);
131}
132
133void addFence(BuildContext& c, int posts, int rails, bool pickets) {
134 posts = std::max(2, posts);
135 for (int i = 0; i < posts; ++i) {
136 const float x = -c.w * 0.5f + c.w * float(i) / float(posts - 1);
137 addBox(c.mesh, {x, c.h * 0.5f, 0}, {c.t, c.h, c.d}, "posts");
138 }
139 for (int i = 0; i < rails; ++i) {
140 const float y = c.h * float(i + 1) / float(rails + 1);
141 addBox(c.mesh, {0, y, 0}, {c.w, c.t, c.d * 0.75f}, "rails");
142 }
143 if (pickets) {
144 const int count = std::max(3, posts * 2 - 1);
145 for (int i = 1; i < count - 1; ++i) {
146 const float x = -c.w * 0.5f + c.w * float(i) / float(count - 1);
147 addBox(c.mesh, {x, c.h * 0.45f, 0}, {c.t * 0.55f, c.h * 0.9f, c.d * 0.6f}, "pickets");
148 }
149 }
150}
151
152void addLadder(BuildContext& c) {
153 addBox(c.mesh, {-c.w * 0.5f + c.t * 0.5f, c.h * 0.5f, 0}, {c.t, c.h, c.d}, "rails");
154 addBox(c.mesh, {c.w * 0.5f - c.t * 0.5f, c.h * 0.5f, 0}, {c.t, c.h, c.d}, "rails");
155 for (int i = 1; i < c.steps; ++i) {
156 addBox(c.mesh, {0, c.h * float(i) / float(c.steps), 0}, {c.w, c.t * 0.75f, c.d * 0.75f}, "rungs");
157 }
158}
159
160void addStairs(BuildContext& c, bool corner) {
161 for (int i = 0; i < c.steps; ++i) {
162 const float fraction = float(i + 1) / float(c.steps);
163 const float stepDepth = c.d / float(c.steps);
164 const float z = c.d * 0.5f - stepDepth * (float(i) + 0.5f);
165 addBox(c.mesh, {0, c.h * fraction * 0.5f, z}, {c.w, c.h * fraction, stepDepth}, "steps");
166 if (corner) {
167 const float x = c.w * 0.5f - stepDepth * (float(i) + 0.5f);
168 addBox(c.mesh, {x, c.h * fraction * 0.5f, 0}, {stepDepth, c.h * fraction, c.d}, "corner-steps");
169 }
170 }
171}
172
173void buildPiece(std::string_view id, BuildContext& c) {
174 if (id == "arrow") {
175 addBox(c.mesh, {0, c.h * 0.5f, -c.d * 0.15f}, {c.w * 0.22f, c.h, c.d * 0.7f}, "shaft");
176 addWedge(c.mesh, {0, c.h * 0.5f, c.d * 0.35f}, {c.w, c.h, c.d * 0.3f}, false, "head");
177 } else if (id == "box") {
178 addBox(c.mesh, {0, c.t * 0.5f, 0}, {c.w, c.t, c.d}, "base");
179 addBox(c.mesh, {-c.w * 0.5f + c.t * 0.5f, c.h * 0.5f, 0}, {c.t, c.h, c.d}, "walls");
180 addBox(c.mesh, {c.w * 0.5f - c.t * 0.5f, c.h * 0.5f, 0}, {c.t, c.h, c.d}, "walls");
181 addBox(c.mesh, {0, c.h * 0.5f, -c.d * 0.5f + c.t * 0.5f}, {c.w, c.h, c.t}, "walls");
182 addBox(c.mesh, {0, c.h * 0.5f, c.d * 0.5f - c.t * 0.5f}, {c.w, c.h, c.t}, "walls");
183 } else if (id == "coin") {
184 addCylinder(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.5f, c.w * 0.5f, c.h, c.detail, "coin");
185 } else if (id.starts_with("cone")) {
186 int variant = id == "cone" ? 0 : id.back() - '0';
187 const int sides = variant == 4 ? 4 : c.detail;
188 const float top = variant == 3 ? c.w * 0.22f : 0.0f;
189 addCylinder(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.5f, top, c.h, sides, "body");
190 } else if (id.starts_with("cube")) {
191 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "body");
192 if (id == "cube9") addBox(c.mesh, {0, c.h + c.t * 0.25f, 0}, {c.w * 0.72f, c.t * 0.5f, c.d * 0.72f}, "inset");
193 } else if (id.starts_with("cylinder")) {
194 const int variant = id == "cylinder" ? 0 : id.back() - '0';
195 const int sides = variant == 4 ? 6 : (variant == 5 ? 8 : c.detail + (variant == 1 ? 8 : 0));
196 addCylinder(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.5f, c.w * 0.5f, c.h, sides, "body");
197 } else if (id.starts_with("door")) {
198 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "panel");
199 if (id != "door") {
200 addFrame(c, c.w - 2.0f * c.t, c.h - 2.0f * c.t, 0.0f, "frame");
201 if (id == "door3") addBox(c.mesh, {0, c.h * 0.55f, c.d}, {c.w * 0.9f, c.t, c.t}, "brace");
202 }
203 } else if (id.starts_with("fence")) {
204 const bool edge = id == "fence-edge";
205 if (edge)
206 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "post");
207 else
208 addFence(c, id == "fence2" ? 7 : 5, id == "fence3" ? 1 : 2, id == "fence-wood");
209 } else if (id.starts_with("ground")) {
210 if (id == "ground-corner") {
211 addBox(c.mesh, {0, c.h * 0.5f, -c.d * 0.25f}, {c.w, c.h, c.d * 0.5f}, "ground");
212 addBox(c.mesh, {-c.w * 0.25f, c.h * 0.5f, c.d * 0.25f}, {c.w * 0.5f, c.h, c.d * 0.5f}, "ground");
213 } else
214 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "ground");
215 } else if (id == "key") {
216 addTorus(c.mesh, {-c.w * 0.35f, c.h * 0.5f, 0}, c.d * 0.28f, c.t * 0.45f, c.detail, 6, "ring");
217 addBox(c.mesh, {c.w * 0.1f, c.h * 0.5f, 0}, {c.w * 0.65f, c.t, c.t}, "shaft");
218 addBox(c.mesh, {c.w * 0.38f, c.h * 0.5f, c.d * 0.13f}, {c.t, c.t, c.d * 0.35f}, "teeth");
219 } else if (id.starts_with("ladder")) {
220 addLadder(c);
221 } else if (id.starts_with("pillar")) {
222 const bool square = id == "pillar1";
223 if (square)
224 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w * 0.65f, c.h, c.d * 0.65f}, "shaft");
225 else
226 addCylinder(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.3f, id == "pillar4" ? c.w * 0.22f : c.w * 0.3f, c.h,
227 id == "pillar2" ? c.detail : 8, "shaft");
228 addBox(c.mesh, {0, c.t * 0.5f, 0}, {c.w, c.t, c.d}, "base");
229 addBox(c.mesh, {0, c.h - c.t * 0.5f, 0}, {c.w, c.t, c.d}, "capital");
230 } else if (id.starts_with("railing")) {
231 if (id == "railing-edge")
232 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "post");
233 else
234 addFence(c, 5, 1, false);
235 } else if (id.starts_with("ramp")) {
236 addWedge(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, false, "ramp");
237 } else if (id.starts_with("sphere")) {
238 addSphere(c.mesh, {0, c.h * 0.5f, 0}, {c.w * 0.5f, c.h * 0.5f, c.d * 0.5f},
239 id == "sphere1" ? std::max(4, c.detail / 2) : std::max(3, c.detail / 3),
240 id == "sphere1" ? c.detail * 2 : c.detail, "body");
241 } else if (id == "spike") {
242 addCylinder(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.5f, 0.0f, c.h, 4, "spike");
243 } else if (id.starts_with("spikes")) {
244 const int count = id == "spikes-big" ? 3 : 5;
245 for (int z = 0; z < count; ++z)
246 for (int x = 0; x < count; ++x) {
247 const float px = -c.w * 0.5f + c.w * (float(x) + 0.5f) / float(count);
248 const float pz = -c.d * 0.5f + c.d * (float(z) + 0.5f) / float(count);
249 addCylinder(c.mesh, {px, c.h * 0.5f, pz}, std::min(c.w, c.d) * 0.35f / float(count), 0, c.h, 4,
250 "spikes");
251 }
252 } else if (id.starts_with("stairs")) {
253 addStairs(c, id.starts_with("stairs-corner"));
254 } else if (id == "toggle-switch") {
255 addBox(c.mesh, {0, c.t * 0.5f, 0}, {c.w, c.t, c.d}, "base");
256 addWedge(c.mesh, {0, c.t + (c.h - c.t) * 0.5f, 0}, {c.w * 0.3f, c.h - c.t, c.d * 0.3f}, false, "lever");
257 } else if (id == "torus") {
258 addTorus(c.mesh, {0, c.h * 0.5f, 0}, c.w * 0.36f, c.h * 0.5f, c.detail * 2, std::max(4, c.detail / 2), "ring");
259 } else if (id.starts_with("wall-corner")) {
260 addCorner(c, "wall");
261 } else if (id.starts_with("wall-door")) {
262 addFrame(c, c.w * 0.42f, c.h * 0.72f, 0.0f, "wall");
263 } else if (id.starts_with("wall-window")) {
264 addFrame(c, c.w * 0.5f, c.h * 0.42f, c.h * 0.28f, "wall");
265 } else if (id.starts_with("wall")) {
266 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.w, c.h, c.d}, "wall");
267 } else if (id.starts_with("window")) {
268 addFrame(c, c.w - 2.0f * c.t, c.h - 2.0f * c.t, c.t, "frame");
269 addBox(c.mesh, {0, c.h * 0.5f, 0}, {c.t * 0.6f, c.h - 2.0f * c.t, c.d * 0.55f}, "mullion");
270 }
271}
272
273RecipeDescriptor recipeDescriptor(const PrototypePieceDescriptor& piece) {
274 RecipeDescriptor schema{"prototype." + std::string(piece.id), std::string(piece.displayName), "Prototype 3D", {}};
275 schema.params.push_back(ParamDescriptor::floating("scale", "Uniform Scale", 1.0f, 0.01f, 100.0f, 0.05f));
276 schema.params.push_back(ParamDescriptor::floating("width", "Width", piece.defaultWidth, 0.01f, 1000.0f, 0.05f));
277 schema.params.push_back(ParamDescriptor::floating("height", "Height", piece.defaultHeight, 0.01f, 1000.0f, 0.05f));
278 schema.params.push_back(ParamDescriptor::floating("depth", "Depth", piece.defaultDepth, 0.01f, 1000.0f, 0.05f));
279 schema.params.push_back(ParamDescriptor::floating("thickness", "Member Thickness", 0.12f, 0.005f, 100.0f, 0.01f));
280 schema.params.push_back(ParamDescriptor::integer("detail", "Radial Detail", 12, 3, 64));
281 schema.params.push_back(ParamDescriptor::integer("steps", "Steps / Rungs", 6, 2, 64));
282 schema.params.push_back(
283 ParamDescriptor::floating("uvScale", "UV Tiles per World Unit", 0.5f, 0.01f, 100.0f, 0.05f));
284 return schema;
285}
286
287} // namespace
288
289std::span<const PrototypePieceDescriptor> prototypePieceDescriptors() noexcept { return kPieces; }
290
292 const PrototypePieceDescriptor* piece = findPiece(pieceId);
293 if (!piece) {
295 eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "unknown prototype piece", "pieceId"));
296 }
297 const float scale = positiveParam(params, "scale", 1.0f);
298 const float uvScale = positiveParam(params, "uvScale", 0.5f);
299 BuildContext context{{},
300 positiveParam(params, "width", piece->defaultWidth) * scale,
301 positiveParam(params, "height", piece->defaultHeight) * scale,
302 positiveParam(params, "depth", piece->defaultDepth) * scale,
303 positiveParam(params, "thickness", 0.12f) * scale,
304 std::clamp(params.getInt("detail", 12), 3, 64),
305 std::clamp(params.getInt("steps", 6), 2, 64)};
306 if (!std::isfinite(scale) || scale <= 0.0f || !std::isfinite(context.w) || !std::isfinite(context.h) ||
307 !std::isfinite(uvScale) || !std::isfinite(context.d) || !std::isfinite(context.t) || context.w <= 0.0f ||
308 context.h <= 0.0f || context.d <= 0.0f || context.t <= 0.0f) {
311 "prototype dimensions and scale must be finite and positive", "params"));
312 }
313 context.t = std::min({context.t, context.w * 0.45f, context.h * 0.45f, context.d * 0.45f});
314 buildPiece(pieceId, context);
315 if (context.mesh.empty()) {
317 eve::DiagnosticCode::Failed, "prototype generator produced an empty mesh", "pieceId"));
318 }
319 for (float& uv : context.mesh.uvs()) uv *= uvScale;
320 context.mesh.setMeta("recipe", "prototype." + std::string(pieceId));
321 context.mesh.setMeta("origin", "ground-center");
322 context.mesh.setMeta("source", "procedural");
324}
325
327 for (const auto& piece : kPieces) {
328 const std::string id(piece.id);
329 registry.registerRecipe(recipeDescriptor(piece),
330 [id](const Params& params, MeshBuild& out, std::string& error) {
331 auto generated = generatePrototypePiece(id, params);
332 if (!generated.ok()) {
333 error = generated.status().describe();
334 return false;
335 }
336 out = std::move(generated).takeValue();
337 return true;
338 });
339 }
340}
341
342} // namespace eve::procgen
SQInteger top
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string variant
building::EdgeCurveGroup group
float uv
float pz
std::int32_t c
int h
std::vector< Colorf > px
std::array< float, 3 > scale
std::string name
std::string error
Definition Package.cpp:60
std::string id
Definition PlayHost.cpp:108
float d
int steps
int detail
float t
Mesh * mesh
const RoadEdge * edge
std::uint32_t count
ecs::EntityHandle side
int sides
Definition TreeMesh.cpp:322
const VegetationPresetContext & context
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
EVENGINE_API_DOMAINS public API.
void registerRecipe(const std::string &id, MeshRecipeFn fn)
Register a recipe without metadata.
Owning, typed generation parameters.
Definition Params.h:27
std::vector< ParamSpec > params
void addWedge(MeshBuild &mesh, Vec3 center, Vec3 size, bool riseAlongX, const char *group)
Adds wedge.
void addTorus(MeshBuild &mesh, Vec3 center, float majorRadius, float minorRadius, int majorSegments, int minorSegments, const char *group)
Adds torus.
void addCylinder(MeshBuild &mesh, Vec3 center, float radiusBottom, float radiusTop, float height, int sides, const char *group)
Adds cylinder.
void addBox(MeshBuild &mesh, Vec3 center, Vec3 size, const char *group)
Adds box.
void addSphere(MeshBuild &mesh, Vec3 center, Vec3 radii, int rings, int sides, const char *group)
Adds sphere.
eve::Result< MeshBuild > generatePrototypePiece(std::string_view pieceId, const Params &params)
Build one prototype module as an owning CPU triangle mesh.
void registerPrototypePieceRecipes(MeshRecipeRegistry &registry)
Register all prototype modules as prototype.* mesh recipes.
std::span< const PrototypePieceDescriptor > prototypePieceDescriptors() noexcept
Return the stable catalogue of 75 procedural prototype modules.
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.
Metadata for one centered, Y-up module in the built-in prototype construction kit.
float defaultWidth
Default X extent in world units.
float defaultHeight
Default Y extent in world units.
float defaultDepth
Default Z extent in world units.