Line data Source code
1 0 : /*
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 :
38 : #import "OOStandaloneAtmosphereGenerator.h"
39 : #import "OOCollectionExtractors.h"
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 :
58 : enum
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 :
73 : static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key);
74 :
75 : static BOOL FillFBMBuffer(OOStandaloneAtmosphereGeneratorInfo *info);
76 : static float QFactor(float *accbuffer, int x, int y, unsigned width, float polar_y_value, float bias, float polar_y);
77 : static float BlendAlpha(float fraction, float a, float b);
78 : static FloatRGBA CloudMix(OOStandaloneAtmosphereGeneratorInfo *info, float q, float nearPole);
79 :
80 :
81 : enum
82 : {
83 : kPlanetScale256x256 = 1,
84 : kPlanetScale512x512,
85 : kPlanetScale1024x1024,
86 : kPlanetScale2048x2048,
87 : kPlanetScale4096x4096,
88 :
89 : kPlanetScaleReducedDetail = kPlanetScale512x512,
90 : kPlanetScaleFullDetail = kPlanetScale1024x1024
91 : };
92 :
93 :
94 : @implementation OOStandaloneAtmosphereGenerator
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 :
275 : END:
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 :
335 : OOINLINE 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 :
342 : static 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 :
353 : static float BlendAlpha(float fraction, float a, float b)
354 : {
355 : return Lerp(b, a, fraction);
356 : }
357 :
358 :
359 : static 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 :
417 : static 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 :
426 : OOINLINE 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
440 : OOINLINE int32_t fast_floor(double val)
441 : {
442 : val += 68719476736.0 * 1.5;
443 : return (((int32_t*)&val)[iman_] >> 16);
444 : }
445 :
446 :
447 : static BOOL GenerateFBMNoise(OOStandaloneAtmosphereGeneratorInfo *info);
448 : static BOOL GenerateFBMNoise3D(OOStandaloneAtmosphereGeneratorInfo *info);
449 :
450 :
451 : static 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 :
476 : enum
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
491 : static 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
507 : static 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 : */
532 : OOINLINE 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.
544 : static 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 : */
628 : static 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 :
654 : static 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 :
712 : END:
713 : FREE(info->permutations);
714 : return OK;
715 : }
716 :
717 : // Old 2D value noise.
718 :
719 : static 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 :
729 : static 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 :
779 : static 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 :
816 : static 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
|