载入中...
搜索中...
未找到
CableTextures.cpp
浏览该文件的文档.
2
3#include "image/ImageData.h"
8
9#include <algorithm>
10#include <cmath>
11#include <cstdint>
12#include <string>
13#include <utility>
14#include <vector>
15
16namespace eve::procgen {
17namespace {
18
19constexpr float kPi = 3.14159265358979323846f;
20
21float smoothstep(float edge0, float edge1, float x) {
22 if (edge0 == edge1) return x < edge0 ? 0.f : 1.f;
23 const float t = std::clamp((x - edge0) / (edge1 - edge0), 0.f, 1.f);
24 return t * t * (3.f - 2.f * t);
25}
26
27float mixf(float a, float b, float t) {
28 t = std::clamp(t, 0.f, 1.f);
29 return a + (b - a) * t;
30}
31
32struct Rgb {
33 float r = 0.f, g = 0.f, b = 0.f;
34};
35
36Rgb mix(Rgb a, Rgb b, float t) {
37 t = std::clamp(t, 0.f, 1.f);
38 return {a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t};
39}
40
41void writePixel(image::ImageData& img, int x, int y, Rgb c) {
42 img.setPixel(x, y, image::ImageData::Colorf{c.r, c.g, c.b, 1.f});
43}
44
45struct CableBake {
46 std::unique_ptr<image::ImageData> albedo;
47 std::vector<float> height;
48 bool seamless = true;
49 int w = 0;
50 int h = 0;
51};
52
53struct CablePbrDefaults {
54 float roughnessLow = 0.35f;
55 float roughnessHigh = 0.75f;
56 float metallic = 0.85f;
57 float normalStrength = 5.f;
58 float aoStrength = 1.2f;
59 float heightStrength = 1.f;
60};
61
62void appendPbrParams(RecipeDescriptor& schema, const CablePbrDefaults& d) {
63 schema.params.push_back(
64 ParamDescriptor::floating("roughnessLow", "Low Roughness", d.roughnessLow, 0.f, 1.f, 0.01f));
65 schema.params.push_back(
66 ParamDescriptor::floating("roughnessHigh", "High Roughness", d.roughnessHigh, 0.f, 1.f, 0.01f));
67 schema.params.push_back(ParamDescriptor::floating("metallic", "Metallic", d.metallic, 0.f, 1.f, 0.01f));
68 schema.params.push_back(
69 ParamDescriptor::floating("normalStrength", "Normal Strength", d.normalStrength, 0.01f, 32.f, 0.05f));
70 schema.params.push_back(
71 ParamDescriptor::floating("aoStrength", "AO Strength", d.aoStrength, 0.f, 5.f, 0.05f));
72 schema.params.push_back(
73 ParamDescriptor::floating("heightStrength", "Height Strength", d.heightStrength, 0.f, 16.f, 0.05f));
74}
75
76std::unique_ptr<PbrTextureSet> assembleCablePbr(CableBake bake, const Params& params, const CablePbrDefaults& defaults,
77 std::string& error) {
78 if (!bake.albedo || bake.height.size() != size_t(bake.w) * size_t(bake.h)) {
79 error = "cable bake missing albedo or height";
80 return {};
81 }
82 const int w = bake.w;
83 const int h = bake.h;
84 const bool seamless = bake.seamless;
85 const float roughnessLow =
86 std::clamp(params.getFloat("roughnessLow", defaults.roughnessLow), 0.f, 1.f);
87 const float roughnessHigh =
88 std::clamp(params.getFloat("roughnessHigh", defaults.roughnessHigh), 0.f, 1.f);
89 const float metallic = std::clamp(params.getFloat("metallic", defaults.metallic), 0.f, 1.f);
90 const float normalStrength =
91 std::max(0.01f, params.getFloat("normalStrength", defaults.normalStrength));
92 const float aoStrength = std::clamp(params.getFloat("aoStrength", defaults.aoStrength), 0.f, 5.f);
93 const float heightStrength =
94 std::max(0.f, params.getFloat("heightStrength", defaults.heightStrength));
95
96 auto set = std::make_unique<PbrTextureSet>();
97 set->albedo = bake.albedo.release();
98 set->normal = heightToNormalImage(bake.height, w, h, normalStrength, seamless).release();
99 if (!set->normal) {
100 error = "cable normal generation failed";
101 return {};
102 }
103
104 std::vector<float> rough(size_t(w * h));
105 std::vector<float> heightMap(size_t(w * h));
106 std::vector<float> ao(size_t(w * h));
107 const int r = std::max(1, std::min(w, h) / 64);
108 for (int y = 0; y < h; ++y) {
109 for (int x = 0; x < w; ++x) {
110 const size_t i = size_t(y) * size_t(w) + size_t(x);
111 const float hh = bake.height[i];
112 // Ridges (high height) are smoother; grooves are rougher.
113 rough[i] = mixf(roughnessHigh, roughnessLow, hh);
114 heightMap[i] = std::clamp(0.5f + (hh - 0.5f) * heightStrength, 0.f, 1.f);
115
116 float sum = 0.f;
117 int cnt = 0;
118 for (int dy = -r; dy <= r; ++dy) {
119 for (int dx = -r; dx <= r; ++dx) {
120 int xx = x + dx, yy = y + dy;
121 if (seamless) {
122 xx = ((xx % w) + w) % w;
123 yy = ((yy % h) + h) % h;
124 } else {
125 if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue;
126 }
127 sum += bake.height[size_t(yy) * size_t(w) + size_t(xx)];
128 ++cnt;
129 }
130 }
131 const float local = cnt > 0 ? sum / float(cnt) : hh;
132 ao[i] = std::clamp(1.f - aoStrength * std::max(0.f, local - hh), 0.f, 1.f);
133 }
134 }
135 set->roughness = grayscaleImage(rough, w, h).release();
136 set->metallic = grayscaleImage(std::vector<float>(size_t(w * h), metallic), w, h).release();
137 set->height = grayscaleImage(heightMap, w, h).release();
138 set->ao = grayscaleImage(ao, w, h).release();
139 if (!set->roughness || !set->metallic || !set->height || !set->ao) {
140 error = "cable PBR map allocation failed";
141 return {};
142 }
143 return set;
144}
145
146struct CommonOpts {
147 int strands = 6;
148 float twist = 2.f;
149 float gap = 0.12f;
150 float wear = 0.25f;
151 float contrast = 0.55f;
152 int colors = 8;
153 bool seamless = true;
154};
155
156CommonOpts commonFromParams(const Params& params, int defaultStrands, float defaultTwist) {
157 CommonOpts o;
158 o.strands = std::clamp(params.getInt("strands", defaultStrands), 2, 16);
159 o.twist = std::clamp(params.getFloat("twist", defaultTwist), 0.25f, 16.f);
160 o.gap = std::clamp(params.getFloat("gap", 0.12f), 0.f, 0.45f);
161 o.wear = std::clamp(params.getFloat("wear", 0.25f), 0.f, 1.f);
162 o.contrast = std::clamp(params.getFloat("contrast", 0.55f), 0.f, 1.f);
163 o.colors = std::max(2, params.getInt("colors", 8));
164 o.seamless = params.getInt("seamless", 1) != 0;
165 return o;
166}
167
169void strandField(float u, float v, const CommonOpts& o, float& height, float& strandId) {
170 const float twisted = u * float(o.strands) + v * o.twist;
171 const float cell = twisted - std::floor(twisted);
172 const float dist = std::min(cell, 1.f - cell) * 2.f; // 0 at edge, 1 at center
173 const float ridge = smoothstep(o.gap, 1.f, dist);
174 height = ridge;
175 strandId = std::floor(twisted);
176}
177
178CableBake bakeSteelCable(const Params& params, std::string& error) {
179 const int w = std::clamp(params.getWidth() > 0 ? params.getWidth() : 256, 8, 4096);
180 const int h = std::clamp(params.getHeight() > 0 ? params.getHeight() : 256, 8, 4096);
181 CommonOpts o = commonFromParams(params, 6, 3.f);
182 const float polish = std::clamp(params.getFloat("polish", 0.45f), 0.f, 1.f);
183 NoiseField noise;
184 noise.seed = uint32_t(params.getSeed());
185 noise.periodX = o.seamless ? 8 : 0;
186 noise.periodY = o.seamless ? 8 : 0;
187
188 CableBake bake;
189 bake.w = w;
190 bake.h = h;
191 bake.seamless = o.seamless;
192 bake.albedo = std::make_unique<image::ImageData>(w, h, "RGBA8");
193 bake.height.assign(size_t(w * h), 0.f);
194 if (!bake.albedo) {
195 error = "steel cable albedo allocation failed";
196 return {};
197 }
198
199 const Rgb bright{0.72f, 0.74f, 0.76f};
200 const Rgb mid{0.48f, 0.50f, 0.52f};
201 const Rgb dark{0.28f, 0.30f, 0.32f};
202
203 for (int y = 0; y < h; ++y) {
204 for (int x = 0; x < w; ++x) {
205 const float u = (float(x) + 0.5f) / float(w);
206 const float v = (float(y) + 0.5f) / float(h);
207 float hh = 0.f, sid = 0.f;
208 strandField(u, v, o, hh, sid);
209 const float n =
210 noise.fbmPerlin(u * 6.f + sid * 0.17f, v * 4.f, 3, 2.f, 0.5f);
211 const float scratch = noise.ridgedPerlin(u * 18.f, v * 2.f + sid, 2, 2.2f, 0.55f);
212 float shade = mixf(0.35f, 1.f, hh);
213 shade = mixf(shade, shade * (0.85f + 0.3f * n), o.contrast);
214 shade = mixf(shade, shade * (1.f - 0.35f * scratch), o.wear);
215 shade = mixf(shade, mixf(shade, 1.f, polish * 0.25f), polish);
216 Rgb col = mix(dark, mix(mid, bright, shade), shade);
217 if (o.colors < 64) {
218 const float bands = float(o.colors);
219 col.r = std::floor(col.r * bands) / bands;
220 col.g = std::floor(col.g * bands) / bands;
221 col.b = std::floor(col.b * bands) / bands;
222 }
223 writePixel(*bake.albedo, x, y, col);
224 bake.height[size_t(y) * size_t(w) + size_t(x)] =
225 std::clamp(hh * 0.85f + n * 0.08f - scratch * 0.05f * o.wear, 0.f, 1.f);
226 }
227 }
228 return bake;
229}
230
231CableBake bakeIronChain(const Params& params, std::string& error) {
232 const int w = std::clamp(params.getWidth() > 0 ? params.getWidth() : 256, 8, 4096);
233 const int h = std::clamp(params.getHeight() > 0 ? params.getHeight() : 256, 8, 4096);
234 CommonOpts o = commonFromParams(params, 4, 1.5f);
235 const float rust = std::clamp(params.getFloat("rust", 0.35f), 0.f, 1.f);
236 NoiseField noise;
237 noise.seed = uint32_t(params.getSeed());
238 noise.periodX = o.seamless ? 8 : 0;
239 noise.periodY = o.seamless ? 8 : 0;
240
241 CableBake bake;
242 bake.w = w;
243 bake.h = h;
244 bake.seamless = o.seamless;
245 bake.albedo = std::make_unique<image::ImageData>(w, h, "RGBA8");
246 bake.height.assign(size_t(w * h), 0.f);
247 if (!bake.albedo) {
248 error = "iron chain albedo allocation failed";
249 return {};
250 }
251
252 const Rgb steel{0.42f, 0.44f, 0.46f};
253 const Rgb dark{0.18f, 0.17f, 0.16f};
254 const Rgb rustA{0.55f, 0.28f, 0.12f};
255 const Rgb rustB{0.38f, 0.18f, 0.08f};
256
257 for (int y = 0; y < h; ++y) {
258 for (int x = 0; x < w; ++x) {
259 const float u = (float(x) + 0.5f) / float(w);
260 const float v = (float(y) + 0.5f) / float(h);
261 // Brushed metal with circumferential bands + sparse rivet pits.
262 const float band = 0.5f + 0.5f * std::sin(u * 2.f * kPi * float(o.strands) + v * o.twist * kPi);
263 const float n = noise.fbmPerlin(u * 5.f, v * 5.f, 4, 2.f, 0.5f);
264 const float pit = smoothstep(0.72f, 0.92f, noise.ridgedPerlin(u * 14.f, v * 14.f, 2, 2.f, 0.5f));
265 const float rustMask =
266 smoothstep(0.55f, 0.9f, noise.fbm(u * 3.f, v * 3.f, 3, 2.f, 0.55f)) * rust;
267 float hh = mixf(0.35f, 0.85f, band) * (1.f - pit * 0.55f);
268 hh = mixf(hh, hh * 0.7f, rustMask);
269 Rgb col = mix(dark, steel, mixf(0.4f, 1.f, band) * (0.75f + 0.25f * n));
270 col = mix(col, mix(rustB, rustA, n), rustMask);
271 col = mix(col, dark, pit * 0.65f);
272 col.r = mixf(col.r, col.r * (1.f - 0.2f * o.wear), o.wear);
273 col.g = mixf(col.g, col.g * (1.f - 0.2f * o.wear), o.wear);
274 col.b = mixf(col.b, col.b * (1.f - 0.15f * o.wear), o.wear);
275 if (o.colors < 64) {
276 const float bands = float(o.colors);
277 col.r = std::floor(col.r * bands) / bands;
278 col.g = std::floor(col.g * bands) / bands;
279 col.b = std::floor(col.b * bands) / bands;
280 }
281 writePixel(*bake.albedo, x, y, col);
282 bake.height[size_t(y) * size_t(w) + size_t(x)] = std::clamp(hh, 0.f, 1.f);
283 }
284 }
285 return bake;
286}
287
288CableBake bakeHempRope(const Params& params, std::string& error) {
289 const int w = std::clamp(params.getWidth() > 0 ? params.getWidth() : 256, 8, 4096);
290 const int h = std::clamp(params.getHeight() > 0 ? params.getHeight() : 256, 8, 4096);
291 CommonOpts o = commonFromParams(params, 3, 2.5f);
292 const float fiber = std::clamp(params.getFloat("fiber", 0.65f), 0.f, 1.f);
293 NoiseField noise;
294 noise.seed = uint32_t(params.getSeed());
295 noise.periodX = o.seamless ? 8 : 0;
296 noise.periodY = o.seamless ? 8 : 0;
297
298 CableBake bake;
299 bake.w = w;
300 bake.h = h;
301 bake.seamless = o.seamless;
302 bake.albedo = std::make_unique<image::ImageData>(w, h, "RGBA8");
303 bake.height.assign(size_t(w * h), 0.f);
304 if (!bake.albedo) {
305 error = "hemp rope albedo allocation failed";
306 return {};
307 }
308
309 const Rgb light{0.72f, 0.58f, 0.34f};
310 const Rgb mid{0.52f, 0.40f, 0.22f};
311 const Rgb dark{0.32f, 0.24f, 0.12f};
312
313 for (int y = 0; y < h; ++y) {
314 for (int x = 0; x < w; ++x) {
315 const float u = (float(x) + 0.5f) / float(w);
316 const float v = (float(y) + 0.5f) / float(h);
317 float hh = 0.f, sid = 0.f;
318 strandField(u, v, o, hh, sid);
319 const float n =
320 noise.fbmPerlin(u * 10.f + sid, v * 18.f, 4, 2.1f, 0.5f);
321 const float fluff = noise.valueNoise(u * 32.f, v * 40.f);
322 float shade = mixf(0.25f, 1.f, hh);
323 shade = mixf(shade, shade * (0.7f + 0.5f * n), fiber);
324 shade = mixf(shade, shade * (0.85f + 0.3f * fluff), o.wear * 0.5f + 0.2f);
325 Rgb col = mix(dark, mix(mid, light, shade), shade);
326 // Occasional darker fiber flecks.
327 if (fluff > 0.78f) col = mix(col, dark, 0.35f * fiber);
328 if (o.colors < 64) {
329 const float bands = float(o.colors);
330 col.r = std::floor(col.r * bands) / bands;
331 col.g = std::floor(col.g * bands) / bands;
332 col.b = std::floor(col.b * bands) / bands;
333 }
334 writePixel(*bake.albedo, x, y, col);
335 bake.height[size_t(y) * size_t(w) + size_t(x)] =
336 std::clamp(hh * 0.8f + n * 0.15f * fiber + fluff * 0.05f, 0.f, 1.f);
337 }
338 }
339 return bake;
340}
341
342std::unique_ptr<image::ImageData> steelRecipe(const Params& params, std::string& error) {
343 return bakeSteelCable(params, error).albedo;
344}
345std::unique_ptr<image::ImageData> ironRecipe(const Params& params, std::string& error) {
346 return bakeIronChain(params, error).albedo;
347}
348std::unique_ptr<image::ImageData> hempRecipe(const Params& params, std::string& error) {
349 return bakeHempRope(params, error).albedo;
350}
351
352void appendCommonParams(RecipeDescriptor& schema, int strands, float twist) {
353 schema.params.push_back(ParamDescriptor::integer("strands", "Strand Count", strands, 2, 16));
354 schema.params.push_back(ParamDescriptor::floating("twist", "Twist Rate", twist, 0.25f, 16.f, 0.05f));
355 schema.params.push_back(ParamDescriptor::floating("gap", "Groove Width", 0.12f, 0.f, 0.45f, 0.01f));
356 schema.params.push_back(ParamDescriptor::floating("wear", "Wear", 0.25f, 0.f, 1.f, 0.01f));
357 schema.params.push_back(ParamDescriptor::floating("contrast", "Contrast", 0.55f, 0.f, 1.f, 0.01f));
358 schema.params.push_back(ParamDescriptor::integer("colors", "Color Bands", 8, 2, 64));
359 schema.params.push_back(ParamDescriptor::boolean("seamless", "Seamless", true));
360}
361
362RecipeDescriptor steelDescriptor() {
363 RecipeDescriptor schema =
364 RecipeDescriptor::grid("tex.cable.steel", "Steel Cable", "Cable", 8, 8, 4096, 4096);
365 appendCommonParams(schema, 6, 3.f);
366 schema.params.push_back(ParamDescriptor::floating("polish", "Polish", 0.45f, 0.f, 1.f, 0.01f));
367 return schema;
368}
369
370RecipeDescriptor ironDescriptor() {
371 RecipeDescriptor schema =
372 RecipeDescriptor::grid("tex.chain.iron", "Iron Chain Metal", "Cable", 8, 8, 4096, 4096);
373 appendCommonParams(schema, 4, 1.5f);
374 schema.params.push_back(ParamDescriptor::floating("rust", "Rust", 0.35f, 0.f, 1.f, 0.01f));
375 return schema;
376}
377
378RecipeDescriptor hempDescriptor() {
379 RecipeDescriptor schema =
380 RecipeDescriptor::grid("tex.rope.hemp", "Hemp Rope", "Cable", 8, 8, 4096, 4096);
381 appendCommonParams(schema, 3, 2.5f);
382 schema.params.push_back(ParamDescriptor::floating("fiber", "Fiber Detail", 0.65f, 0.f, 1.f, 0.01f));
383 return schema;
384}
385
386CablePbrDefaults steelPbrDefaults() { return {0.42f, 0.22f, 0.92f, 6.5f, 1.15f, 1.1f}; }
387CablePbrDefaults ironPbrDefaults() { return {0.55f, 0.30f, 0.78f, 5.5f, 1.35f, 1.05f}; }
388CablePbrDefaults hempPbrDefaults() { return {0.82f, 0.55f, 0.02f, 4.5f, 1.2f, 1.2f}; }
389
390} // namespace
391
393 std::string error;
394 auto img = steelRecipe(params, error);
395 if (!img) {
398 error.empty() ? "steel cable texture generation failed" : error, "recipe"));
399 }
400 return eve::Result<std::unique_ptr<image::ImageData>>::success(std::move(img));
401}
402
404 std::string error;
405 auto img = ironRecipe(params, error);
406 if (!img) {
409 error.empty() ? "iron chain texture generation failed" : error, "recipe"));
410 }
411 return eve::Result<std::unique_ptr<image::ImageData>>::success(std::move(img));
412}
413
415 std::string error;
416 auto img = hempRecipe(params, error);
417 if (!img) {
420 error.empty() ? "hemp rope texture generation failed" : error, "recipe"));
421 }
422 return eve::Result<std::unique_ptr<image::ImageData>>::success(std::move(img));
423}
424
426 std::string error;
427 CableBake bake = bakeSteelCable(params, error);
428 auto set = assembleCablePbr(std::move(bake), params, steelPbrDefaults(), error);
429 if (!set) {
432 error.empty() ? "steel cable PBR generation failed" : error, "recipe"));
433 }
434 return eve::Result<std::unique_ptr<PbrTextureSet>>::success(std::move(set));
435}
436
438 std::string error;
439 CableBake bake = bakeIronChain(params, error);
440 auto set = assembleCablePbr(std::move(bake), params, ironPbrDefaults(), error);
441 if (!set) {
444 error.empty() ? "iron chain PBR generation failed" : error, "recipe"));
445 }
446 return eve::Result<std::unique_ptr<PbrTextureSet>>::success(std::move(set));
447}
448
450 std::string error;
451 CableBake bake = bakeHempRope(params, error);
452 auto set = assembleCablePbr(std::move(bake), params, hempPbrDefaults(), error);
453 if (!set) {
456 error.empty() ? "hemp rope PBR generation failed" : error, "recipe"));
457 }
458 return eve::Result<std::unique_ptr<PbrTextureSet>>::success(std::move(set));
459}
460
462 registry.registerRecipe(steelDescriptor(), steelRecipe);
463 registry.registerRecipe(ironDescriptor(), ironRecipe);
464 registry.registerRecipe(hempDescriptor(), hempRecipe);
465}
466
468 RecipeDescriptor steel = steelDescriptor();
469 steel.id = "pbr.cable.steel";
470 steel.displayName = "Steel Cable PBR";
471 steel.category = "Material";
472 appendPbrParams(steel, steelPbrDefaults());
473 registry.registerPbrRecipe(std::move(steel), [](const Params& params, std::string& error) {
474 CableBake bake = bakeSteelCable(params, error);
475 return assembleCablePbr(std::move(bake), params, steelPbrDefaults(), error);
476 });
477
478 RecipeDescriptor iron = ironDescriptor();
479 iron.id = "pbr.chain.iron";
480 iron.displayName = "Iron Chain PBR";
481 iron.category = "Material";
482 appendPbrParams(iron, ironPbrDefaults());
483 registry.registerPbrRecipe(std::move(iron), [](const Params& params, std::string& error) {
484 CableBake bake = bakeIronChain(params, error);
485 return assembleCablePbr(std::move(bake), params, ironPbrDefaults(), error);
486 });
487
488 RecipeDescriptor hemp = hempDescriptor();
489 hemp.id = "pbr.rope.hemp";
490 hemp.displayName = "Hemp Rope PBR";
491 hemp.category = "Material";
492 appendPbrParams(hemp, hempPbrDefaults());
493 registry.registerPbrRecipe(std::move(hemp), [](const Params& params, std::string& error) {
494 CableBake bake = bakeHempRope(params, error);
495 return assembleCablePbr(std::move(bake), params, hempPbrDefaults(), error);
496 });
497}
498
499} // namespace eve::procgen
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
int mid
Definition AnimSmr.cpp:120
float twist
float roughnessLow
float aoStrength
float wear
bool seamless
float heightStrength
float contrast
float gap
float normalStrength
float roughnessHigh
int strands
graphics::Texture * albedo
tensor::Graph g
Definition GpuGraph.cpp:7
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::int32_t c
int h
std::uint32_t height
std::string local
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float metallic
std::string error
Definition Package.cpp:60
float d
float t
std::vector< float > colors
Light3D::Data * light
float dy
float dx
Cell cell
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
medialoader::Colorf Colorf
Definition ImageData.h:41
Owning, typed generation parameters.
Definition Params.h:27
EVENGINE_API_DOMAINS public API.
Definition PbrMaterial.h:55
void registerPbrRecipe(const std::string &id, PbrRecipeFn fn)
Register a recipe without metadata.
EVENGINE_API_DOMAINS public API.
void registerRecipe(const std::string &id, TextureRecipeFn fn)
Register a recipe without metadata.
std::vector< ParamSpec > params
void registerCableTextureRecipes(TextureRecipeRegistry &registry)
Register tex.cable.steel / tex.chain.iron / tex.rope.hemp.
eve::Result< std::unique_ptr< image::ImageData > > generateIronChainTexture(const Params &params)
Generate a parameterized iron / rusted metal albedo (tex.chain.iron). @ownership On success the calle...
void registerCablePbrRecipes(PbrRecipeRegistry &registry)
Register pbr.cable.steel / pbr.chain.iron / pbr.rope.hemp.
eve::Result< std::unique_ptr< image::ImageData > > generateSteelCableTexture(const Params &params)
Generate a parameterized steel-cable braid albedo (tex.cable.steel). @ownership On success the caller...
eve::Result< std::unique_ptr< PbrTextureSet > > generateHempRopePbr(const Params &params)
Full PBR map set for hemp rope (pbr.rope.hemp). @ownership On success the caller exclusively owns the...
eve::Result< std::unique_ptr< PbrTextureSet > > generateIronChainPbr(const Params &params)
Full PBR map set for iron chain metal (pbr.chain.iron). @ownership On success the caller exclusively ...
std::unique_ptr< image::ImageData > grayscaleImage(const std::vector< float > &values, int w, int h)
Builds a grayscale RGBA8 image and returns unique ownership to the caller.
eve::Result< std::unique_ptr< PbrTextureSet > > generateSteelCablePbr(const Params &params)
Full PBR map set for steel cable (pbr.cable.steel). @ownership On success the caller exclusively owns...
eve::Result< std::unique_ptr< image::ImageData > > generateHempRopeTexture(const Params &params)
Generate a parameterized hemp-rope fiber albedo (tex.rope.hemp). @ownership On success the caller exc...
std::unique_ptr< image::ImageData > heightToNormalImage(const std::vector< float > &height, int w, int h, float strength, bool seamless)
Builds a normal-map image and transfers its unique ownership to the caller.
static ParamDescriptor boolean(std::string key, std::string label, bool defaultValue)
Construct a Boolean descriptor stored as zero or one in Params.
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.
Definition ParamSchema.h:52
static RecipeDescriptor grid(std::string id, std::string displayName, std::string category, int minimumWidth, int minimumHeight, int maximumWidth=4096, int maximumHeight=4096)
Create a schema with common seed, width and height parameters.