Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOStandaloneAtmosphereGenerator.m
Go to the documentation of this file.
1/*
2 OOStandaloneAtmosphereGenerator.m
3
4 Generator for atmosphere textures when the planet is using a
5 non-generated diffuse map.
6
7
8 Oolite
9 Copyright (C) 2004-2013 Giles C Williams and contributors
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 MA 02110-1301, USA.
25*/
26
27#import "OOCocoa.h"
28#import "OOStellarBody.h"
29
30#if NEW_PLANETS
31
32
33#define DEBUG_DUMP ( 0 && OOLITE_DEBUG)
34#define DEBUG_DUMP_RAW ( 1 && DEBUG_DUMP)
35
36#define ALBEDO_FACTOR 0.7f // Overall darkening of everything, allowing better contrast for snow and specular highlights.
37
40#import "OOColor.h"
41
42#ifndef TEXGEN_TEST_RIG
43#import "OOTexture.h"
44#import "Universe.h"
45#endif
46
47#if DEBUG_DUMP
48#import "MyOpenGLView.h"
49#endif
50
51
52#define FREE(x) do { if (0) { void *x__ = x; x__ = x__; } /* Preceeding is for type checking only. */ void **x_ = (void **)&(x); free(*x_); *x_ = NULL; } while (0)
53
54
55#define PLANET_TEXTURE_OPTIONS (kOOTextureMinFilterLinear | kOOTextureMagFilterLinear | kOOTextureRepeatS | kOOTextureNoShrink)
56
57
58enum
59{
60 kRandomBufferSize = 128
61};
62
63
64@interface OOStandaloneAtmosphereGenerator (Private)
65
66#if DEBUG_DUMP_RAW
67- (void) dumpNoiseBuffer:(float *)noise;
68#endif
69
70@end
71
72
73static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key);
74
75static BOOL FillFBMBuffer(OOStandaloneAtmosphereGeneratorInfo *info);
76static float QFactor(float *accbuffer, int x, int y, unsigned width, float polar_y_value, float bias, float polar_y);
77static float BlendAlpha(float fraction, float a, float b);
78static FloatRGBA CloudMix(OOStandaloneAtmosphereGeneratorInfo *info, float q, float nearPole);
79
80
81enum
82{
83 kPlanetScale256x256 = 1,
84 kPlanetScale512x512,
85 kPlanetScale1024x1024,
86 kPlanetScale2048x2048,
87 kPlanetScale4096x4096,
88
89 kPlanetScaleReducedDetail = kPlanetScale512x512,
90 kPlanetScaleFullDetail = kPlanetScale1024x1024
91};
92
93
95
96- (id) initWithPlanetInfo:(NSDictionary *)planetInfo
97{
98 OOLog(@"texture.planet.generate", @"%@", @"Initialising standalone atmosphere generator");
99
100 // AllowCubeMap not used yet but might be in future
101 if ((self = [super initWithPath:[NSString stringWithFormat:@"OOStandaloneAtmosphereTexture@%p", self] options:kOOTextureAllowCubeMap]))
102 {
103 OOLog(@"texture.planet.generate",@"Extracting parameters for generator %@",self);
104 [[planetInfo objectForKey:@"noise_map_seed"] getValue:&_info.seed];
105 OOLog(@"texture.planet.generate", @"%@", @"Extracting atmosphere parameters");
106 // we are an atmosphere:
107 _info.cloudAlpha = [planetInfo oo_floatForKey:@"cloud_alpha" defaultValue:1.0f];
108 _info.cloudFraction = OOClamp_0_1_f([planetInfo oo_floatForKey:@"cloud_fraction" defaultValue:0.3]);
109 _info.cloudColor = FloatRGBFromDictColor(planetInfo, @"cloud_color");
110 _info.paleCloudColor = FloatRGBFromDictColor(planetInfo, @"polar_cloud_color");
111
112 OOGraphicsDetail detailLevel = [UNIVERSE detailLevel];
113
114#ifndef TEXGEN_TEST_RIG
115 if (detailLevel < DETAIL_LEVEL_SHADERS)
116 {
117 _planetScale = kPlanetScaleReducedDetail;
118 }
119 else
120 {
121 _planetScale = kPlanetScaleFullDetail;
122 }
123#else
124 _planetScale = kPlanetScale4096x4096;
125#endif
126 _info.perlin3d = [planetInfo oo_boolForKey:@"perlin_3d" defaultValue:detailLevel > DETAIL_LEVEL_SHADERS];
127 _info.planetAspectRatio = _info.perlin3d ? 2 : 1;
128 _info.planetScaleOffset = 8 - _info.planetAspectRatio;
129 }
130
131 return self;
132}
133
134
135+ (OOTexture *) planetTextureWithInfo:(NSDictionary *)planetInfo
136{
137 OOTexture *result = nil;
138 OOStandaloneAtmosphereGenerator *generator = [[self alloc] initWithPlanetInfo:planetInfo];
139 if (generator != nil)
140 {
141 result = [OOTexture textureWithGenerator:generator];
142 [generator release];
143 }
144
145 return result;
146}
147
148
149+ (BOOL) generateAtmosphereTexture:(OOTexture **)texture withInfo:(NSDictionary *)planetInfo
150{
151 NSParameterAssert(texture != NULL);
152
153 OOStandaloneAtmosphereGenerator *atmoGen = [[[self alloc] initWithPlanetInfo:planetInfo] autorelease];
154 if (atmoGen == nil) return NO;
155
156 *texture = [OOTexture textureWithGenerator:atmoGen];
157
158 return *texture != nil;
159}
160
161
162- (void) dealloc
163{
164 [super dealloc];
165}
166
167
168- (NSString *) descriptionComponents
169{
170 return [NSString stringWithFormat:@"seed: %u,%u", _info.seed.high, _info.seed.low];
171}
172
173
174- (uint32_t) textureOptions
175{
176 return PLANET_TEXTURE_OPTIONS;
177}
178
179
180- (NSString *) cacheKey
181{
182 return [NSString stringWithFormat:@"OOStandaloneAtmosphereGenerator-@%u\n%u,%u/%u,%u/%f/%f/%f,%f,%f/%f,%f,%f/%f,%f,%f",
183 _planetScale,
184 _info.width, _info.height, _info.seed.high, _info.seed.low,
185 _info.cloudAlpha, _info.cloudFraction,
186 _info.airColor.r, _info.airColor.g, _info.airColor.b,
187 _info.cloudColor.r, _info.cloudColor.g, _info.cloudColor.b,
188 _info.paleCloudColor.r, _info.paleCloudColor.g, _info.paleCloudColor.b
189 ];
190}
191
192
193
194- (BOOL)getResult:(OOPixMap *)outData
195 format:(OOTextureDataFormat *)outFormat
196 width:(uint32_t *)outWidth
197 height:(uint32_t *)outHeight
198{
199 BOOL waiting = NO;
200 if (![self isReady])
201 {
202 waiting = true;
203 OOLog(@"texture.planet.generate.wait", @"%s generator %@", "Waiting for", self);
204 }
205
206 BOOL result = [super getResult:outData format:outFormat originalWidth:outWidth originalHeight:outHeight];
207
208 if (waiting)
209 {
210 OOLog(@"texture.planet.generate.dequeue", @"%s generator %@", result ? "Dequeued" : "Failed to dequeue", self);
211 }
212 else
213 {
214 OOLog(@"texture.planet.generate.dequeue", @"%s generator %@ without waiting.", result ? "Dequeued" : "Failed to dequeue", self);
215 }
216
217 return result;
218}
219
220/* TODO: fix duplication between here and OOPlanetTextureGenerator of
221 * various noise, interpolation, etc. functions */
222- (void) loadTexture
223{
224 OOLog(@"texture.planet.generate.begin", @"Started generator %@", self);
225
226 BOOL success = NO;
227
228 uint8_t *aBuffer = NULL, *apx = NULL;
229 float *randomBuffer = NULL;
230
231 _height = _info.height = 1 << (_planetScale + _info.planetScaleOffset);
232 _width = _info.width = _height * _info.planetAspectRatio;
233
234#define FAIL_IF(cond) do { if (EXPECT_NOT(cond)) goto END; } while (0)
235#define FAIL_IF_NULL(x) FAIL_IF((x) == NULL)
236
237 aBuffer = malloc(4 * _width * _height);
238 FAIL_IF_NULL(aBuffer);
239 apx = aBuffer;
240
241 FAIL_IF(!FillFBMBuffer(&_info));
242#if DEBUG_DUMP_RAW
243 [self dumpNoiseBuffer:_info.fbmBuffer];
244#endif
245
246 float paleClouds = (_info.cloudFraction * _info.fbmBuffer[0] < 1.0f - _info.cloudFraction) ? 0.0f : 1.0f;
247
248 int x, y;
249 FloatRGBA color;
250 float q, nearPole;
251 float rHeight = 1.0f / _height;
252 float fy, fHeight = _height;
253
254 float cloudFraction = _info.cloudFraction;
255
256 for (y = (int)_height - 1, fy = (float)y; y >= 0; y--, fy--)
257 {
258 nearPole = (2.0f * fy - fHeight) * rHeight;
259 nearPole *= nearPole;
260
261 for (x = (int)_width - 1; x >= 0; x--)
262 {
263 q = QFactor(_info.fbmBuffer, x, y, _width, paleClouds, cloudFraction, nearPole);
264 color = CloudMix(&_info, q, nearPole);
265 *apx++ = 255.0f * color.r;
266 *apx++ = 255.0f * color.g;
267 *apx++ = 255.0f * color.b;
268 *apx++ = 255.0f * color.a * _info.cloudAlpha;
269 }
270 }
271
272 success = YES;
273 _format = kOOTextureDataRGBA;
274
275END:
276 FREE(_info.fbmBuffer);
277 FREE(randomBuffer);
278 if (success)
279 {
280 _data = aBuffer;
281 }
282 else
283 {
284 FREE(aBuffer);
285 }
286
287 OOLog(@"texture.planet.generate.complete", @"Completed generator %@ %@successfully", self, success ? @"" : @"un");
288
289#if DEBUG_DUMP
290 if (success)
291 {
292 NSString *diffuseName = [NSString stringWithFormat:@"atmosphere-%u-%u-diffuse-new", _info.seed.high, _info.seed.low];
293 NSString *lightsName = [NSString stringWithFormat:@"atmosphere-%u-%u-alpha-new", _info.seed.high, _info.seed.low];
294
295 [[UNIVERSE gameView] dumpRGBAToRGBFileNamed:diffuseName
296 andGrayFileNamed:lightsName
297 bytes:aBuffer
298 width:_width
299 height:_height
300 rowBytes:_width * 4];
301 }
302#endif
303}
304
305
306#if DEBUG_DUMP_RAW
307
308- (void) dumpNoiseBuffer:(float *)noise
309{
310 NSString *noiseName = [NSString stringWithFormat:@"atmosphere-%u-%u-noise-new", _info.seed.high, _info.seed.low];
311
312 uint8_t *noisePx = malloc(_width * _height);
313 unsigned x, y;
314 for (y = 0; y < _height; y++)
315 {
316 for (x = 0; x < _width; x++)
317 {
318 noisePx[y * _width + x] = 255.0f * noise[y * _width + x];
319 }
320 }
321
322 [[UNIVERSE gameView] dumpGrayToFileNamed:noiseName
323 bytes:noisePx
324 width:_width
325 height:_height
326 rowBytes:_width];
327 FREE(noisePx);
328}
329
330#endif
331
332@end
333
334
335OOINLINE float Lerp(float v0, float v1, float fraction)
336{
337 // Linear interpolation - equivalent to v0 * (1.0f - fraction) + v1 * fraction.
338 return v0 + fraction * (v1 - v0);
339}
340
341
342static FloatRGB Blend(float fraction, FloatRGB a, FloatRGB b)
343{
344 return (FloatRGB)
345 {
346 Lerp(b.r, a.r, fraction),
347 Lerp(b.g, a.g, fraction),
348 Lerp(b.b, a.b, fraction)
349 };
350}
351
352
353static float BlendAlpha(float fraction, float a, float b)
354{
355 return Lerp(b, a, fraction);
356}
357
358
359static FloatRGBA CloudMix(OOStandaloneAtmosphereGeneratorInfo *info, float q, float nearPole)
360{
361//#define AIR_ALPHA (0.15f)
362//#define CLOUD_ALPHA (1.0f)
363// CIM: make distinction between cloud and not-cloud bigger
364#define AIR_ALPHA (0.05f)
365#define CLOUD_ALPHA (2.0f)
366
367#define POLAR_BOUNDARY (0.33f)
368#define CLOUD_BOUNDARY (0.5f)
369#define RECIP_CLOUD_BOUNDARY (1.0f / CLOUD_BOUNDARY)
370
371 FloatRGB cloudColor = info->cloudColor;
372 float alpha = info->cloudAlpha, portion = 0.0f;
373
374 q -= CLOUD_BOUNDARY * 0.5f;
375
376 if (nearPole > POLAR_BOUNDARY)
377 {
378 portion = nearPole > POLAR_BOUNDARY + 0.2f ? 1.0f : (nearPole - POLAR_BOUNDARY) * 5.0f;
379 cloudColor = Blend(portion, info->paleCloudColor, cloudColor);
380
381 portion = nearPole > POLAR_BOUNDARY + 0.625f ? 1.0f : (nearPole - POLAR_BOUNDARY) * 1.6f;
382 }
383
384 if (q <= 0.0f)
385 {
386 if (q >= -CLOUD_BOUNDARY)
387 {
388 alpha *= BlendAlpha(-q * 0.5f * RECIP_CLOUD_BOUNDARY + 0.5f, CLOUD_ALPHA, AIR_ALPHA);
389 }
390 else
391 {
392 alpha *= CLOUD_ALPHA;
393 }
394 }
395 else
396 {
397 if (q < CLOUD_BOUNDARY)
398 {
399 alpha *= BlendAlpha( q * 0.5f * RECIP_CLOUD_BOUNDARY + 0.5f, AIR_ALPHA,CLOUD_ALPHA);
400 }
401 else
402 {
403 alpha *= AIR_ALPHA;
404 }
405 }
406 // magic numbers! at the poles we have fairly thin air.
407 alpha *= BlendAlpha(portion, 0.6f, 1.0f);
408 if (alpha > 1.0)
409 {
410 alpha = 1.0;
411 }
412
413 return (FloatRGBA){ cloudColor.r, cloudColor.g, cloudColor.b, alpha };
414}
415
416
417static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key)
418{
419 OOColor *color = [dictionary objectForKey:key];
420 NSCAssert1([color isKindOfClass:[OOColor class]], @"Expected OOColor, got %@", [color class]);
421
422 return (FloatRGB){ [color redComponent] * ALBEDO_FACTOR, [color greenComponent] * ALBEDO_FACTOR, [color blueComponent] * ALBEDO_FACTOR };
423}
424
425
426OOINLINE float Hermite(float q)
427{
428 return 3.0f * q * q - 2.0f * q * q * q;
429}
430
431
432#if __BIG_ENDIAN__
433#define iman_ 1
434#else
435#define iman_ 0
436#endif
437
438 // (same behaviour as, but faster than, FLOAT->INT)
439 //Works OK for -32728 to 32727.99999236688
440OOINLINE int32_t fast_floor(double val)
441{
442 val += 68719476736.0 * 1.5;
443 return (((int32_t*)&val)[iman_] >> 16);
444}
445
446
447static BOOL GenerateFBMNoise(OOStandaloneAtmosphereGeneratorInfo *info);
448static BOOL GenerateFBMNoise3D(OOStandaloneAtmosphereGeneratorInfo *info);
449
450
451static BOOL FillFBMBuffer(OOStandaloneAtmosphereGeneratorInfo *info)
452{
453 NSCParameterAssert(info != NULL);
454
455 // Allocate result buffer.
456 info->fbmBuffer = calloc(info->width * info->height, sizeof (float));
457 if (info->fbmBuffer != NULL)
458 {
459 if (!info->perlin3d)
460 {
461 GenerateFBMNoise(info);
462 }
463 else
464 {
465 GenerateFBMNoise3D(info);
466 }
467
468 return YES;
469 }
470 return NO;
471}
472
473
474
475
476enum
477{
478 // Size of permutation buffer used to map integer coordinates to gradients. Must be power of two.
479 kPermutationCount = 1 << 10,
480 kPermutationMask = kPermutationCount - 1,
481
482 // Number of different gradient vectors used. The most important thing is that the gradients are evenly distributed and sum to 0.
483 kGradientCount = 12
484};
485
486
487#define LUT_DOT 1
488
489
490#if !LUT_DOT
491static const Vector kGradients[kGradientCount] =
492{
493 { 1, 1, 0 },
494 { -1, 1, 0 },
495 { 1, -1, 0 },
496 { -1, -1, 0 },
497 { 1, 0, 1 },
498 { -1, 0, 1 },
499 { 1, 0, -1 },
500 { -1, 0, -1 },
501 { 0, 1, 1 },
502 { 0, -1, 1 },
503 { 0, 1, -1 },
504 { 0, -1, -1 }
505};
506#else
507static const uint8_t kGradients[kGradientCount][3] =
508{
509 { 2, 2, 1 },
510 { 0, 2, 1 },
511 { 2, 0, 1 },
512 { 0, 0, 1 },
513 { 2, 1, 2 },
514 { 0, 1, 2 },
515 { 2, 1, 0 },
516 { 0, 1, 0 },
517 { 1, 2, 2 },
518 { 1, 0, 2 },
519 { 1, 2, 0 },
520 { 1, 0, 0 }
521};
522
523
524/* Attempted speedup that didn't pan out, but might inspire something better.
525 Since our gradient vectors' components are all -1, 0 or 1, we should be
526 able to calculate the dot product without any multiplication at all, by
527 simply summing the right combination of (x, y, z), (0, 0, 0) and (-x, -y, -z).
528
529 This turns out to be slightly slower than using multiplication, even if
530 the negations are precalculated.
531*/
532OOINLINE float TDot3(const uint8_t grad[3], float x, float y, float z)
533{
534 float xt[3] = { -x, 0.0f, x };
535 float yt[3] = { -y, 0.0f, y };
536 float zt[3] = { -z, 0.0f, z };
537
538 return xt[grad[0]] + yt[grad[1]] + zt[grad[2]];
539}
540#endif
541
542
543// Sample 3D noise function defined by kGradients and permutation table at point p.
544static float SampleNoise3D(OOStandaloneAtmosphereGeneratorInfo *info, Vector p)
545{
546 uint16_t *permutations = info->permutations;
547
548 // Split coordinates into integer and fractional parts.
549 float fx = floor(p.x);
550 float fy = floor(p.y);
551 float fz = floor(p.z);
552 int X = fx;
553 int Y = fy;
554 int Z = fz;
555 float x = p.x - fx;
556 float y = p.y - fy;
557 float z = p.z - fz;
558
559 // Select gradient for each corner.
560#define PERM(v) permutations[(v) & kPermutationMask]
561
562 unsigned PZ0 = PERM(Z);
563 unsigned PZ1 = PERM(Z + 1);
564
565 unsigned PY0Z0 = PERM(Y + PZ0);
566 unsigned PY1Z0 = PERM(Y + 1 + PZ0);
567 unsigned PY0Z1 = PERM(Y + PZ1);
568 unsigned PY1Z1 = PERM(Y + 1 + PZ1);
569
570 unsigned gi000 = PERM(X + PY0Z0);
571 unsigned gi010 = PERM(X + PY1Z0);
572 unsigned gi100 = PERM(X + 1 + PY0Z0);
573 unsigned gi110 = PERM(X + 1 + PY1Z0);
574 unsigned gi001 = PERM(X + PY0Z1);
575 unsigned gi011 = PERM(X + PY1Z1);
576 unsigned gi101 = PERM(X + 1 + PY0Z1);
577 unsigned gi111 = PERM(X + 1 + PY1Z1);
578
579#undef PERM
580
581 // Calculate noise contributions from each of the eight corners.
582#if !LUT_DOT
583#define DOT3(idx, x_, y_, z_) dot_product(kGradients[(idx) % kGradientCount], (Vector){ (x_), (y_), (z_) })
584#else
585#define DOT3(idx, x_, y_, z_) TDot3(kGradients[(idx) % kGradientCount], (x_), (y_), (z_))
586#endif
587
588 float x1 = x - 1.0f;
589 float y1 = y - 1.0f;
590 float z1 = z - 1.0f;
591 float n000 = DOT3(gi000, x , y , z );
592 float n010 = DOT3(gi010, x , y1, z );
593 float n100 = DOT3(gi100, x1, y , z );
594 float n110 = DOT3(gi110, x1, y1, z );
595 float n001 = DOT3(gi001, x , y , z1);
596 float n011 = DOT3(gi011, x , y1, z1);
597 float n101 = DOT3(gi101, x1, y , z1);
598 float n111 = DOT3(gi111, x1, y1, z1);
599
600#undef DOT3
601
602 // Compute the fade curve value for each of x, y, z
603 float u = Hermite(x);
604 float v = Hermite(y);
605 float w = Hermite(z);
606
607 // Interpolate along the contributions from each of the corners.
608 float nx00 = Lerp(n000, n100, u);
609 float nx01 = Lerp(n001, n101, u);
610 float nx10 = Lerp(n010, n110, u);
611 float nx11 = Lerp(n011, n111, u);
612
613 float nxy0 = Lerp(nx00, nx10, v);
614 float nxy1 = Lerp(nx01, nx11, v);
615
616 float nxyz = Lerp(nxy0, nxy1, w);
617
618 return nxyz;
619}
620
621
622/* Generate shuffled permutation order - each value from 0 to
623 kPermutationCount - 1 occurs exactly once. This shuffling provides all the
624 randomness in the resulting noise. Don't worry, though - for
625 kPermutationCount = 1024 this allows for 4e2567 different noise maps,
626 which is a lot more than RanRot will actually give us.
627*/
628static BOOL MakePermutationTable(OOStandaloneAtmosphereGeneratorInfo *info)
629{
630 uint16_t *perms = malloc(sizeof *info->permutations * kPermutationCount);
631 if (EXPECT_NOT(perms == NULL)) return NO;
632
633 /* Fisher-Yates/Durstenfeld/Knuth shuffle, "inside-out" variant.
634 Based on pseudocode from http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
635
636 When comparing to the pseudocode, note that it generates a one-based
637 series, but this version generates a zero-based series.
638 */
639 perms[0] = 0;
640 uint16_t *curr = perms;
641 uint16_t n;
642 for (n = 1; n < kPermutationCount; n++)
643 {
644 uint16_t j = RanrotWithSeed(&info->seed) & kPermutationMask;
645 *++curr = perms[j];
646 perms[j] = n - 1;
647 }
648
649 info->permutations = perms;
650 return YES;
651}
652
653
654static BOOL GenerateFBMNoise3D(OOStandaloneAtmosphereGeneratorInfo *info)
655{
656 BOOL OK = NO;
657
658 FAIL_IF(!MakePermutationTable(info));
659
660 unsigned x, y, width = info->width, height = info->height;
661 float lon, lat; // Longitude and latitude in radians.
662 float dlon = 2.0f * M_PI / width;
663 float dlat = M_PI / height;
664 float *px = info->fbmBuffer;
665
666 for (y = 0, lat = -M_PI_2; y < height; y++, lat += dlat)
667 {
668 float las = sin(lat);
669 float lac = cos(lat);
670
671 for (x = 0, lon = -M_PI; x < width; x++, lon += dlon)
672 {
673 // FIXME: in real life, we really don't want sin and cos per pixel.
674 // Convert spherical coordinates to vector.
675 float los = sin(lon);
676 float loc = cos(lon);
677
678 Vector p =
679 {
680 los * lac,
681 las,
682 loc * lac
683 };
684
685#if 1
686 // fBM
687 unsigned octaveMask = 4;
688 float octave = octaveMask;
689 octaveMask -= 1;
690 float scale = 0.4f;
691 float sum = 0;
692
693 while ((octaveMask + 1) < height)
694 {
695 Vector ps = vector_multiply_scalar(p, octave);
696 sum += scale * SampleNoise3D(info, ps);
697
698 octave *= 2.0f;
699 octaveMask = (octaveMask << 1) | 1;
700 scale *= 0.5f;
701 }
702#else
703 // Single octave
704 p = vector_multiply_scalar(p, 4.0f);
705 float sum = 0.5f * SampleNoise3D(info, p);
706#endif
707
708 *px++ = sum + 0.5f;
709 }
710 }
711
712END:
713 FREE(info->permutations);
714 return OK;
715}
716
717// Old 2D value noise.
718
719static void FillRandomBuffer(float *randomBuffer, RANROTSeed seed)
720{
721 unsigned i, len = kRandomBufferSize * kRandomBufferSize;
722 for (i = 0; i < len; i++)
723 {
724 randomBuffer[i] = randfWithSeed(&seed);
725 }
726}
727
728
729static void AddNoise(OOStandaloneAtmosphereGeneratorInfo *info, float *randomBuffer, float octave, unsigned octaveMask, float scale, float *qxBuffer, int *ixBuffer)
730{
731 unsigned x, y;
732 unsigned width = info->width, height = info->height;
733 int ix, jx, iy, jy;
734 float rr = octave / width;
735 float fx, fy, qx, qy, rix, rjx, rfinal;
736 float *dst = info->fbmBuffer;
737
738 for (fy = 0, y = 0; y < height; fy++, y++)
739 {
740 qy = fy * rr;
741 iy = fast_floor(qy);
742 jy = (iy + 1) & octaveMask;
743 qy = Hermite(qy - iy);
744 iy &= (kRandomBufferSize - 1);
745 jy &= (kRandomBufferSize - 1);
746
747 for (fx = 0, x = 0; x < width; fx++, x++)
748 {
749 if (y == 0)
750 {
751 // first pass: initialise buffers.
752 qx = fx * rr;
753 ix = fast_floor(qx);
754 qx -= ix;
755 ix &= (kRandomBufferSize - 1);
756 ixBuffer[x] = ix;
757 qxBuffer[x] = Hermite(qx);
758 }
759 else
760 {
761 // later passes: grab the stored values.
762 ix = ixBuffer[x];
763 qx = qxBuffer[x];
764 }
765
766 jx = (ix + 1) & octaveMask;
767 jx &= (kRandomBufferSize - 1);
768
769 rix = Lerp(randomBuffer[iy * kRandomBufferSize + ix], randomBuffer[iy * kRandomBufferSize + jx], qx);
770 rjx = Lerp(randomBuffer[jy * kRandomBufferSize + ix], randomBuffer[jy * kRandomBufferSize + jx], qx);
771 rfinal = Lerp(rix, rjx, qy);
772
773 *dst++ += scale * rfinal;
774 }
775 }
776}
777
778
779static BOOL GenerateFBMNoise(OOStandaloneAtmosphereGeneratorInfo *info)
780{
781 // Allocate the temporary buffers we need in one fell swoop, to avoid administrative overhead.
782 size_t randomBufferSize = kRandomBufferSize * kRandomBufferSize * sizeof (float);
783 size_t qxBufferSize = info->width * sizeof (float);
784 size_t ixBufferSize = info->width * sizeof (int);
785 char *sharedBuffer = malloc(randomBufferSize + qxBufferSize + ixBufferSize);
786 if (sharedBuffer == NULL) return NO;
787
788 float *randomBuffer = (float *)sharedBuffer;
789 float *qxBuffer = (float *)(sharedBuffer + randomBufferSize);
790 int *ixBuffer = (int *)(sharedBuffer + randomBufferSize + qxBufferSize);
791
792 // Get us some value noise.
793 FillRandomBuffer(randomBuffer, info->seed);
794
795 // Generate basic fBM noise.
796 unsigned height = info->height;
797 unsigned octaveMask = 8 * info->planetAspectRatio;
798 float octave = octaveMask;
799 octaveMask -= 1;
800 float scale = 0.5f;
801
802 while ((octaveMask + 1) < height)
803 {
804 AddNoise(info, randomBuffer, octave, octaveMask, scale, qxBuffer, ixBuffer);
805 octave *= 2.0f;
806 octaveMask = (octaveMask << 1) | 1;
807 scale *= 0.5f;
808 }
809
810 FREE(sharedBuffer);
811 return YES;
812}
813
814
815
816static float QFactor(float *accbuffer, int x, int y, unsigned width, float polar_y_value, float bias, float polar_y)
817{
818 float q = accbuffer[y * width + x]; // 0.0 -> 1.0
819 q += bias;
820
821 // Polar Y smooth.
822 q = q * (1.0f - polar_y) + polar_y * polar_y_value;
823
824 return q;
825}
826
827
828
829
830
831
832
833
834#endif // NEW_PLANETS
#define EXPECT_NOT(x)
#define OOINLINE
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define M_PI_2
Definition OOMaths.h:76
#define M_PI
Definition OOMaths.h:73
return self
OOPixMapFormat
Definition OOPixMap.h:39
return nil
float y
float x
@ kOOTextureDataRGBA
Definition OOTexture.h:109
@ kOOTextureAllowCubeMap
Definition OOTexture.h:61
OOGraphicsDetail
Definition OOTypes.h:243
@ DETAIL_LEVEL_SHADERS
Definition OOTypes.h:246
#define CLOUD_ALPHA
static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key)
static FloatRGB Blend(float fraction, FloatRGB a, FloatRGB b)
float blueComponent()
Definition OOColor.m:362
float redComponent()
Definition OOColor.m:350
float greenComponent()
Definition OOColor.m:356
id textureWithGenerator:(OOTextureGenerator *generator)
Definition OOTexture.m:217
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
float randfWithSeed(RANROTSeed *ioSeed)
unsigned RanrotWithSeed(RANROTSeed *ioSeed)