1 /**
2 * @file
3 * @brief Procedurally generated dungeon layouts.
4 **/
5 #include "AppHdr.h"
6
7 #include "dgn-proclayouts.h"
8
9 #include <cmath>
10
11 #include "coord.h"
12 #include "coordit.h"
13 #include "files.h"
14 #include "perlin.h"
15 #include "tag-version.h"
16 #include "terrain.h"
17
_pick_pseudorandom_wall(uint64_t val)18 static dungeon_feature_type _pick_pseudorandom_wall(uint64_t val)
19 {
20 static dungeon_feature_type features[] =
21 {
22 DNGN_STONE_WALL,
23 DNGN_STONE_WALL,
24 DNGN_STONE_WALL,
25 DNGN_STONE_WALL,
26 DNGN_ROCK_WALL,
27 DNGN_ROCK_WALL,
28 DNGN_ROCK_WALL,
29 DNGN_CRYSTAL_WALL,
30 DNGN_METAL_WALL
31 };
32 return features[val%9];
33 }
34
35 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const36 ColumnLayout::operator()(const coord_def &p, const uint32_t offset) const
37 {
38 int x = abs(p.x) % (_col_width + _col_space);
39 int y = abs(p.y) % (_row_width + _row_space);
40 if (x < _col_width && y < _row_width)
41 {
42 int w = _col_width + _col_space;
43 dungeon_feature_type feat = _pick_pseudorandom_wall(hash3(p.x/w, p.y/w, 2));
44 return ProceduralSample(p, feat, offset + 4096);
45 }
46 return ProceduralSample(p, DNGN_FLOOR, offset + 4096);
47 }
48
49 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const50 DiamondLayout::operator()(const coord_def &p, const uint32_t offset) const
51 {
52 uint8_t halfCell = w + s;
53 uint8_t cellSize = halfCell * 2;
54 uint8_t x = abs(abs(p.x) % cellSize - halfCell);
55 uint8_t y = abs(abs(p.y) % cellSize - halfCell);
56 if (x+y < w)
57 {
58 dungeon_feature_type feat = _pick_pseudorandom_wall(hash3(p.x/w, p.y/w, 2));
59 return ProceduralSample(p, feat, offset + 4096);
60 }
61 return ProceduralSample(p, DNGN_FLOOR, offset + 4096);
62 }
63
_get_changepoint(const worley::noise_datum & n,const double scale)64 static uint32_t _get_changepoint(const worley::noise_datum &n, const double scale)
65 {
66 return max(1, (int) floor((n.distance[1] - n.distance[0]) * scale) - 5);
67 }
68
69 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const70 WorleyLayout::operator()(const coord_def &p, const uint32_t offset) const
71 {
72 const double offset_scale = 5000.0;
73 double x = p.x / scale;
74 double y = p.y / scale;
75 double z = offset / offset_scale;
76 worley::noise_datum n = worley::noise(x, y, z + seed);
77
78 const uint32_t changepoint = offset + _get_changepoint(n, offset_scale);
79 const uint8_t size = layouts.size();
80 bool parity = n.id[0] % 4;
81 uint32_t id = n.id[0] / 4;
82 const uint8_t choice = parity
83 ? id % size
84 : min(id % size, (id / size) % size);
85 const coord_def pd = p + id;
86 ProceduralSample sample = (*layouts[(choice + seed) % size])(pd, offset);
87
88 return ProceduralSample(p, sample.feat(),
89 min(changepoint, sample.changepoint()));
90 }
91
92 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const93 ChaosLayout::operator()(const coord_def &p, const uint32_t offset) const
94 {
95 uint64_t base = hash3(p.x, p.y, seed);
96 uint32_t density = baseDensity + seed % 50 + (seed >> 16) % 60;
97 if ((base % 1000) < density)
98 return ProceduralSample(p, _pick_pseudorandom_wall(base/3), offset + 4096);
99 return ProceduralSample(p, DNGN_FLOOR, offset + 4096);
100 }
101
102 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const103 RoilingChaosLayout::operator()(const coord_def &p, const uint32_t offset) const
104 {
105 const double scale = (density - 350) + 4800;
106 double x = p.x;
107 double y = p.y;
108 double z = offset / scale;
109 worley::noise_datum n = worley::noise(x, y, z);
110 const uint32_t changepoint = offset + _get_changepoint(n, scale);
111 ProceduralSample sample = ChaosLayout(n.id[0] + seed, density)(p, offset);
112 return ProceduralSample(p, sample.feat(), min(sample.changepoint(), changepoint));
113 }
114
115 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const116 WastesLayout::operator()(const coord_def &p, const uint32_t offset) const
117 {
118 double x = p.x;
119 double y = p.y;
120 double z = offset / 3;
121 worley::noise_datum n = worley::noise(x, y, z);
122 const uint32_t changepoint = offset + _get_changepoint(n, 3);
123 ProceduralSample sample = ChaosLayout(n.id[0], 10)(p, offset);
124 dungeon_feature_type feat = feat_is_solid(sample.feat())
125 ? DNGN_ROCK_WALL : DNGN_FLOOR;
126 return ProceduralSample(p, feat, min(sample.changepoint(), changepoint));
127 }
128
129 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const130 RiverLayout::operator()(const coord_def &p, const uint32_t offset) const
131 {
132 const double scale = 10000;
133 const double scalar = 90.0;
134 double x = (p.x + perlin::fBM(p.x/4.0, p.y/4.0, seed, 5) * 3) / scalar;
135 double y = (p.y + perlin::fBM(p.x/4.0 + 3.7, p.y/4.0 + 1.9, seed + 4, 5) * 3) / scalar;
136 worley::noise_datum n = worley::noise(x, y, offset / scale + seed);
137 const uint32_t changepoint = offset + _get_changepoint(n, scale);
138 if ((n.id[0] ^ n.id[1] ^ seed) % 4)
139 return layout(p, offset);
140
141 double delta = n.distance[1] - n.distance[0];
142 if (delta < 1.5/scalar)
143 {
144 dungeon_feature_type feat = DNGN_SHALLOW_WATER;
145 uint64_t hash = hash3(p.x, p.y, n.id[0] + seed);
146 if (!(hash % 5))
147 feat = DNGN_DEEP_WATER;
148 if (!(hash % 23))
149 feat = DNGN_TREE;
150 return ProceduralSample(p, feat, changepoint);
151 }
152 return layout(p, offset);
153 }
154
155 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const156 NewAbyssLayout::operator()(const coord_def &p, const uint32_t offset) const
157 {
158 const double scale = 1.0 / 3.2;
159 uint64_t base = hash3(p.x, p.y, seed);
160 worley::noise_datum noise = worley::noise(
161 p.x * scale,
162 p.y * scale,
163 offset / 1000.0);
164 dungeon_feature_type feat = DNGN_FLOOR;
165
166 int dist = noise.distance[0] * 100;
167 bool isWall = (dist > 118 || dist < 30);
168 int delta = min(abs(dist - 118), abs(30 - dist));
169
170 if ((noise.id[0] + noise.id[1]) % 6 == 0)
171 isWall = false;
172
173 if (base % 3 == 0)
174 isWall = !isWall;
175
176 if (isWall)
177 {
178 int fuzz = (base / 3) % 3 ? 0 : (base / 9) % 3 - 1;
179 feat = _pick_pseudorandom_wall(noise.id[0] + fuzz);
180 }
181
182 return ProceduralSample(p, feat, offset + delta);
183 }
184
sanitize_feature(dungeon_feature_type feature,bool strict)185 dungeon_feature_type sanitize_feature(dungeon_feature_type feature, bool strict)
186 {
187 if (feat_is_gate(feature)
188 #if TAG_MAJOR_VERSION == 34
189 || feature == DNGN_TELEPORTER
190 #endif
191 || feature == DNGN_TRANSPORTER
192 || feature == DNGN_TRANSPORTER_LANDING)
193 {
194 return DNGN_STONE_ARCH;
195 }
196 if (feat_is_stair(feature) || feat_is_sealed(feature))
197 return strict ? DNGN_FLOOR : DNGN_STONE_ARCH;
198 if (feat_is_altar(feature) || feat_is_trap(feature))
199 return DNGN_FLOOR;
200
201 switch (feature)
202 {
203 case DNGN_SEALED_DOOR:
204 return DNGN_CLOSED_DOOR;
205 case DNGN_SEALED_CLEAR_DOOR:
206 return DNGN_CLOSED_CLEAR_DOOR;
207 case DNGN_PERMAROCK_WALL:
208 return DNGN_ROCK_WALL;
209 case DNGN_CLEAR_PERMAROCK_WALL:
210 return DNGN_CLEAR_ROCK_WALL;
211 case DNGN_SLIMY_WALL:
212 return DNGN_CRYSTAL_WALL; // !?
213 case DNGN_UNSEEN:
214 case DNGN_ENDLESS_SALT:
215 return DNGN_FLOOR;
216 case DNGN_OPEN_SEA:
217 return DNGN_DEEP_WATER;
218 case DNGN_LAVA_SEA:
219 return DNGN_LAVA;
220 case DNGN_ENTER_SHOP:
221 return DNGN_ABANDONED_SHOP;
222 default:
223 return feature;
224 }
225 }
226
LevelLayout(level_id id,uint32_t _seed,const ProceduralLayout & _layout)227 LevelLayout::LevelLayout(level_id id, uint32_t _seed, const ProceduralLayout &_layout) : seed(_seed), layout(_layout)
228 {
229 if (!is_existing_level(id))
230 {
231 for (rectangle_iterator ri(0); ri; ++ri)
232 grid(*ri) = DNGN_UNSEEN;
233 return;
234 }
235 level_excursion le;
236 le.go_to(id);
237 grid = feature_grid(env.grid);
238 for (rectangle_iterator ri(0); ri; ++ri)
239 {
240 grid(*ri) = sanitize_feature(grid(*ri), true);
241 if (!in_bounds(*ri))
242 {
243 grid(*ri) = DNGN_UNSEEN;
244 continue;
245 }
246
247 uint32_t solid_count = 0;
248 for (adjacent_iterator ai(*ri); ai; ++ai)
249 solid_count += cell_is_solid(*ai);
250 coord_def p = *ri;
251 uint64_t base = hash3(p.x, p.y, seed);
252 int div = base % 2 ? 12 : 11;
253 switch (solid_count)
254 {
255 case 8:
256 grid(*ri) = DNGN_UNSEEN;
257 break;
258 case 7:
259 case 6:
260 case 5:
261 if (!((base / 2) % div))
262 grid(*ri) = DNGN_UNSEEN;
263 break;
264 case 0:
265 case 1:
266 if (!(base % 14))
267 grid(*ri) = DNGN_UNSEEN;
268 break;
269 }
270 }
271 }
272
273 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const274 LevelLayout::operator()(const coord_def &p, const uint32_t offset) const
275 {
276 coord_def cp = clip(p);
277 dungeon_feature_type feat = grid(cp);
278 if (feat == DNGN_UNSEEN)
279 return layout(p, offset);
280 return ProceduralSample(p, feat, offset + 4096);
281 }
282
283 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const284 NoiseLayout::operator()(const coord_def &p, const uint32_t offset) const
285 {
286 return ProceduralSample(p, DNGN_FLOOR, offset + 4096);
287 }
288
289 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const290 ForestLayout::operator()(const coord_def &p, const uint32_t offset) const
291 {
292 dungeon_feature_type feat = DNGN_FLOOR;
293
294 const static WorleyFunction base(0.32,0.4,0.5,0,0,0);
295 const static WorleyFunction offx(0.6,0.6,0.2,854.3,123.4,0.0);
296 const static WorleyFunction offy(0.6,0.6,0.2,123.2,3623.51,0.0);
297 const static WorleyDistortFunction tfunc(base,offx,2.0,offy,1.5);
298
299 worley::noise_datum fn = tfunc.datum(p.x,p.y,offset);
300
301 // Split the id into some 8-bit numbers to use for randomness
302 uint8_t rand[2] = { (uint8_t)(fn.id[0] >> 24), (uint8_t)(fn.id[0] >> 16) };
303 // , fn.id[0] >> 8 & 0x000000ff, fn.id[0] & 0x000000ff };
304
305 double diff = fn.distance[1]-fn.distance[0];
306 float size_factor = (float)rand[1]/(255.0 * 2.0) + 0.15;
307 // 75% chance of trees
308 if (rand[0]<0xC0)
309 {
310 // Size of clump depends on factor
311 if (diff > size_factor)
312 feat = DNGN_TREE;
313 }
314 // Small chance of henge
315 else if (rand[0]<0xC8)
316 {
317 if (diff > (size_factor+0.3) && diff < (size_factor+0.4))
318 feat = DNGN_STONE_ARCH;
319 }
320 // Somewhat under 25% chance of pool
321 else if (rand[0]<0xE0)
322 {
323 if (diff > size_factor*2.0)
324 feat = DNGN_DEEP_WATER;
325 else if (diff > size_factor)
326 feat = DNGN_SHALLOW_WATER;
327 }
328 // 25% chance of empty cell
329 return ProceduralSample(p, feat, offset + 1); // Delta is always 1 because the layout will be clamped
330 }
331
332 // An expansive underworld containing seas, rivers, lakes, forests, cities, mountains, and perhaps more...
333 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const334 ClampLayout::operator()(const coord_def &p, const uint32_t offset) const
335 {
336 uint32_t cycle = offset / clamp;
337 uint32_t order = hash3(p.x, p.y, 0xDEADBEEF + cycle);
338 if (bursty)
339 order &= hash3(p.x + 31, p.y - 37, 0x0DEFACED + cycle);
340 order %= clamp;
341 uint32_t clamp_offset = (offset + order) / clamp * clamp;
342 ProceduralSample sample = layout(p, clamp_offset);
343 uint32_t cp = max(sample.changepoint(), offset + order);
344 return ProceduralSample(p, sample.feat(), cp);
345 }
346
347 ProceduralSample
operator ()(const coord_def & p,const uint32_t offset) const348 UnderworldLayout::operator()(const coord_def &p, const uint32_t offset) const
349 {
350 // Define various environmental functions based on noise. These factors
351 // combine to determine what terrain gets drawn at a given coordinate.
352
353 // Wetness gives us features like water, and optimal wetness will be required for plants
354 const static SimplexFunction func_wet(0.5,0.5,3.0,3463.128,-3737.987,0,2);
355 // Terrain height gives us mountains, rivers, ocean
356 const static SimplexFunction func_height(0.3,0.3,1.0,7.543,2.123,0,4);
357 // Temperament. Plants struggle to grow in extreme temperatures, and we only see lava in hot places.
358 const static SimplexFunction func_hot(0.1,0.1,2.0,111.612,11.243,0,1);
359 // Citification; areas with a high settle factor will tend to feature "man"-made architecture
360 // TODO: Citi/gentrification could use a worley layer instead (or a mix) to have better geometry
361 // and stop things like wall types suddenly changing halfway through a city.
362 const static SimplexFunction func_city(0.2,0.2,1.0,2.732,22.43,0,1);
363 // Gentrification; some cities are richer than others, this affects wall types
364 // but we can also choose what features to build inside the cities, influencing
365 // features like statues, fountains, plants, regularness, and even monsters/loot
366 const static SimplexFunction func_rich(0.05,0.05,1,9.543,5.543,0,1);
367
368 // To create lots of lines everywhere that can often be perpendicular to other
369 // features; for creating bridges, dividing walls
370 const static WorleyFunction func_lateral(0.2,0.2,1,2000.543,1414.823,0);
371
372 // Jitter needs to be completely random everywhere, so this can be used
373 // instead of normal random methods:
374 // if (jitter < (chance_in_1)) { ... }
375 const static SimplexFunction func_jitter(10,10,0.5,1123.543,2451.143,0,5);
376
377 // Compute all our environment factors at the current spot
378 double wet = func_wet(p, offset);
379 double height = func_height(p, offset);
380 double hot = func_hot(p, offset);
381 double city = func_city(p, offset);
382 double rich = func_rich(p, offset);
383 double lateral = func_lateral(p, offset);
384 double jitter = func_jitter(p, offset);
385
386 // TODO: The abyss doesn't support all of these yet but would be nice if:
387 // * Clusters of plants around water edge
388 // * Hot and wet areas are "tropical" with plants/trees (and steam)
389 // * Extremely hot or cold areas should generate fire or ice clouds respectively.
390 // If an "ice" feature were ever created this would be a good place for it.
391 // * Wet cities have
392 // * City + water areas have lateral bridges
393 // * Borrow some easing functions from somewhere to better
394 // control how features vary across bounaries
395 // * Look at surrounding squares to determine gradients - will help
396 // with lateral features and also e.g. growing plants on sunlit mountainsides...
397 // * Use some lateral wetness to try and join mountain streams up to rivers...
398 // * Petrified trees and other fun stuff in extreme temperatures
399 // * Cities - Make the decor more interesting, and choose fountain types based on wetness
400 // * Cities - Pave floor within wall limit
401 // * Cities - might sometimes want to modify the terrain based on what was here before the city.
402 // e.g. if the city was built on water then there should be fountains,
403 // flooding, pools, aqueducts, with lava we get furnaces, etc.
404 // * Rather than basing all the factors purely on separate perlin layers,
405 // could combine some factors; e.g. cities thrive best at optimal combinations
406 // of wet, height and hot, so the city/rich factor could be based on how close to optimum those three are...
407
408 // Factors controlling how the environment is mapped to terrain
409 double water_depth = 0.2 * wet;
410 double water_deep_depth = 0.07 * wet;
411 double mountain_height = 0.8;
412 double mountain_top_height = 0.95;
413
414 // Default feature
415 dungeon_feature_type feat = DNGN_FLOOR;
416
417 // Lakes and rivers
418 if (height < water_depth)
419 feat = DNGN_SHALLOW_WATER;
420 if (height < water_deep_depth)
421 feat = DNGN_DEEP_WATER;
422
423 if (height > mountain_height)
424 {
425 double dist_to_top = (height - mountain_height) / (mountain_top_height - mountain_height);
426 if (height > mountain_top_height
427 || lateral < dist_to_top)
428 {
429 if (hot > 0.7 && (dist_to_top>=1.0 || lateral/dist_to_top < 0.2))
430 feat = DNGN_LAVA;
431 else
432 feat = DNGN_ROCK_WALL;
433 }
434 }
435
436 // Forest
437 bool enable_forest = true;
438 // Forests fill an important gap in the middling height gap between water and mountainous regions
439 double forest_start_height = 0.43;
440 double forest_end_height = 0.57;
441
442 if (enable_forest)
443 {
444 // A narrow river running through the middle of forresty heights at good wetness levels
445 bool is_river = (abs(height-0.5) < (wet/10.0));
446 if (is_river)
447 feat = DNGN_SHALLOW_WATER;
448
449 // Forests are somewhat finnicky about their conditions now
450 double forest =
451 _optimum_range(height, forest_start_height, forest_end_height)
452 * _optimum_range(wet, 0.5, 0.8)
453 * _optimum_range(hot, 0.4, 0.6);
454
455 // Forest should now be 1.0 in the center of the range, 0.0 at the end
456 if (jitter < (forest * 0.5))
457 feat = DNGN_DEMONIC_TREE;
458 }
459
460 // City
461 double city_outer_limit = 0.4;
462 double city_wall_limit = 0.65;
463 double city_wall_width = 0.05;
464 double city_inner_wall_limit = 0.8;
465 bool enable_city = true;
466
467 // Cities become less likely at extreme heights and depths
468 double extreme_proximity = max(0.0,abs(height-0.5)-0.3) * 5.0;
469 city = city * (1.0 - extreme_proximity);
470
471 if (enable_city && city >= city_outer_limit)
472 {
473 dungeon_feature_type city_wall = DNGN_ROCK_WALL;
474 if (rich > 0.5) city_wall = DNGN_STONE_WALL;
475 else if (rich > 0.75) city_wall = DNGN_METAL_WALL;
476 else if (rich > 0.9) city_wall = DNGN_CRYSTAL_WALL;
477
478 // Doors and windows
479 if (jitter>0.5 && jitter<0.6) city_wall = DNGN_CLOSED_DOOR;
480 if (jitter>0.7 && jitter<0.75) city_wall = DNGN_CLEAR_STONE_WALL;
481
482 // Outer cloisters
483 if (city < city_wall_limit)
484 {
485 if ((lateral >= 0.3 && lateral < 0.4 || lateral >= 0.6 && lateral < 0.7)
486 || (lateral >= 0.4 && lateral < 0.6 && city < (city_outer_limit+city_wall_width)))
487 {
488 feat = city_wall;
489 }
490 else if (lateral >= 0.4 && lateral < 0.6)
491 feat = DNGN_FLOOR;
492 }
493
494 // Main outer wall
495 if (city >= city_wall_limit)
496 {
497 // Within outer wall reset all terrain to floor.
498 feat = DNGN_FLOOR;
499
500 if (city < (city_wall_limit + city_wall_width))
501 feat = city_wall;
502 // Wall of inner halls
503 else if (city >= city_inner_wall_limit)
504 {
505 if (city < (city_inner_wall_limit + city_wall_width))
506 feat = city_wall;
507 // Decide on what decor we want within the inner walls
508 else if (jitter > 0.9)
509 {
510 if (rich>0.8)
511 feat = DNGN_FOUNTAIN_BLUE;
512 else if (rich>0.5)
513 feat = DNGN_GRANITE_STATUE;
514 else if (rich>0.2)
515 feat = DNGN_GRATE;
516 else
517 feat = DNGN_STONE_ARCH;
518 }
519 }
520 }
521 }
522
523 int delta = 1;
524
525 return ProceduralSample(p, feat, offset + delta);
526 }
527
_optimum_range(const double val,const double rstart,const double rend) const528 double NoiseLayout::_optimum_range(const double val, const double rstart, const double rend) const
529 {
530 double mid = (rstart + rend) / 2.0;
531 return _optimum_range_mid(val, rstart, mid, mid, rend);
532 }
_optimum_range_mid(const double val,const double rstart,const double rmax1,const double rmax2,const double rend) const533 double NoiseLayout::_optimum_range_mid(const double val, const double rstart, const double rmax1, const double rmax2, const double rend) const
534 {
535 if (rmax1 <= val && val <= rmax2) return 1.0;
536 if (val <= rstart || val >= rend) return 0.0;
537 if (val < rmax1)
538 return (val - rstart) / (rmax1-rstart);
539 return 1.0 - (val - rmax2)/(rend - rmax2);
540 }
541
operator ()(const coord_def & p,const uint32_t offset) const542 double ProceduralFunction::operator()(const coord_def &p, const uint32_t offset) const
543 {
544 return ProceduralFunction::operator()(p.x,p.y,offset);
545 }
546
operator ()(double,double,double) const547 double ProceduralFunction::operator()(double, double, double) const
548 {
549 return 0;
550 }
551
operator ()(const coord_def & p,const uint32_t offset) const552 double SimplexFunction::operator()(const coord_def &p, const uint32_t offset) const
553 {
554 return SimplexFunction::operator()(p.x,p.y,offset);
555 }
556
operator ()(double x,double y,double z) const557 double SimplexFunction::operator()(double x, double y, double z) const
558 {
559 double hx = (x / (double)10 + seed_x) * scale_x;
560 double hy = (y / (double)10 + seed_y) * scale_y;
561 double hz = (z / (double)10000 + seed_z) * scale_z;
562 // Use octaval simplex and scale into a 0..1 range
563 return perlin::fBM(hx, hy, hz, octaves) / 2.0 + 0.5;
564 }
565
operator ()(const coord_def & p,const uint32_t offset) const566 double WorleyFunction::operator()(const coord_def &p, const uint32_t offset) const
567 {
568 return WorleyFunction::operator()(p.x,p.y,offset);
569 }
570
operator ()(double x,double y,double z) const571 double WorleyFunction::operator()(double x, double y, double z) const
572 {
573 worley::noise_datum d = datum(x,y,z);
574 return d.distance[1]-d.distance[0];
575 }
576
datum(double x,double y,double z) const577 worley::noise_datum WorleyFunction::datum(double x, double y, double z) const
578 {
579 double hx = (x * (double)0.8 + seed_x) * scale_x;
580 double hy = (y * (double)0.8 + seed_y) * scale_y;
581 double hz = (z * (double)0.0008 + seed_z) * scale_z;
582 return worley::noise(hx, hy, hz);
583 }
584
operator ()(double x,double y,double z) const585 double DistortFunction::operator()(double x, double y, double z) const
586 {
587 double offx = off_x(x,y,z);
588 double offy = off_y(x,y,z);
589 return base(x+offx,y+offy,z);
590 }
591
datum(double x,double y,double z) const592 worley::noise_datum WorleyDistortFunction::datum(double x, double y, double z) const
593 {
594 double offx = off_x(x,y,z);
595 double offy = off_y(x,y,z);
596 return wbase.datum(x+offx,y+offy,z);
597 }
598