LCOV - code coverage report
Current view: top level - Core/Materials - OOStandaloneAtmosphereGenerator.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 1 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          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

Generated by: LCOV version 1.14