LCOV - code coverage report
Current view: top level - Core/Materials - OOPlanetTextureGenerator.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             :         OOPlanetTextureGenerator.m
       3             :         
       4             :         Generator for planet diffuse maps.
       5             :         
       6             :         
       7             :         Oolite
       8             :         Copyright (C) 2004-2013 Giles C Williams and contributors
       9             :         
      10             :         This program is free software; you can redistribute it and/or
      11             :         modify it under the terms of the GNU General Public License
      12             :         as published by the Free Software Foundation; either version 2
      13             :         of the License, or (at your option) any later version.
      14             :         
      15             :         This program is distributed in the hope that it will be useful,
      16             :         but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :         GNU General Public License for more details.
      19             :         
      20             :         You should have received a copy of the GNU General Public License
      21             :         along with this program; if not, write to the Free Software
      22             :         Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      23             :         MA 02110-1301, USA.
      24             : */
      25             : 
      26             : #import "OOCocoa.h"
      27             : #import "OOStellarBody.h"
      28             : 
      29             : #if NEW_PLANETS
      30             : 
      31             : 
      32             : #define DEBUG_DUMP                      (       0       && OOLITE_DEBUG)
      33             : #define DEBUG_DUMP_RAW          (       1       && DEBUG_DUMP)
      34             : 
      35             : #define POLAR_CAPS                      1
      36             : #define ALBEDO_FACTOR           0.7f    // Overall darkening of everything, allowing better contrast for snow and specular highlights.
      37             : 
      38             : 
      39             : #import "OOPlanetTextureGenerator.h"
      40             : #import "OOCollectionExtractors.h"
      41             : #import "OOColor.h"
      42             : 
      43             : #ifndef TEXGEN_TEST_RIG
      44             : #import "OOTexture.h"
      45             : #import "Universe.h"
      46             : #endif
      47             : 
      48             : #if DEBUG_DUMP
      49             : #import "MyOpenGLView.h"
      50             : #endif
      51             : 
      52             : 
      53             : #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)
      54             : 
      55             : 
      56             : #define PLANET_TEXTURE_OPTIONS  (kOOTextureMinFilterLinear | kOOTextureMagFilterLinear | kOOTextureRepeatS | kOOTextureNoShrink)
      57             : 
      58             : 
      59             : enum
      60             : {
      61             :         kRandomBufferSize               = 128
      62             : };
      63             : 
      64             : 
      65             : /*      The planet generator actually generates two textures when shaders are
      66             :         active, but the texture loader interface assumes we only load/generate
      67             :         one texture per loader. Rather than complicate that, we use a mock
      68             :         generator for the normal/light map.
      69             : */
      70             : @interface OOPlanetNormalMapGenerator: OOTextureGenerator
      71             : {
      72             : @private
      73             :         BOOL                                    _enqueued;
      74             :         NSString                                *_cacheKey;
      75             :         RANROTSeed                              _seed;
      76             : }
      77             : 
      78             : - (id) initWithCacheKey:(NSString *)cacheKey seed:(RANROTSeed)seed;
      79             : 
      80             : - (void) completeWithData:(void *)data width:(unsigned)width height:(unsigned)height;
      81             : 
      82             : - (BOOL) enqueued;
      83             : 
      84             : @end
      85             : 
      86             : 
      87             : //      Doing the same as above for the atmosphere.
      88             : @interface OOPlanetAtmosphereGenerator: OOTextureGenerator
      89             : {
      90             : @private
      91             :         BOOL                            _enqueued;
      92             :         NSString                                *_cacheKey;
      93             :         RANROTSeed                              _seed;
      94             :         OOPlanetTextureGenerator        *_parent;
      95             : }
      96             : 
      97             : - (id) initWithCacheKey:(NSString *)cacheKey seed:(RANROTSeed)seed andParent:(OOPlanetTextureGenerator *)parent;
      98             : 
      99             : - (void) completeWithData:(void *)data width:(unsigned)width height:(unsigned)height;
     100             : 
     101             : - (BOOL) enqueued;
     102             : 
     103             : @end
     104             : 
     105             : 
     106             : @interface OOPlanetTextureGenerator (Private)
     107             : 
     108             : - (NSString *) cacheKeyForType:(NSString *)type;
     109             : - (OOPlanetNormalMapGenerator *) normalMapGenerator;    // Must be called before generator is enqueued for rendering.
     110             : - (OOPlanetAtmosphereGenerator *) atmosphereGenerator;  // Must be called before generator is enqueued for rendering.
     111             : 
     112             : #if DEBUG_DUMP_RAW
     113             : - (void) dumpNoiseBuffer:(float *)noise;
     114             : #endif
     115             : 
     116             : @end
     117             : 
     118             : 
     119             : static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key);
     120             : 
     121             : static BOOL FillFBMBuffer(OOPlanetTextureGeneratorInfo *info);
     122             : 
     123             : static float QFactor(float *accbuffer, int x, int y, unsigned width, float polar_y_value, float bias, float polar_y);
     124             : static float GetQ(float *qbuffer, int x, int y, unsigned width, unsigned height, unsigned widthMask, unsigned heightMask);
     125             : 
     126             : static FloatRGB Blend(float fraction, FloatRGB a, FloatRGB b);
     127             : static float BlendAlpha(float fraction, float a, float b);
     128             : static void SetMixConstants(OOPlanetTextureGeneratorInfo *info, float temperatureFraction);
     129             : static FloatRGBA CloudMix(OOPlanetTextureGeneratorInfo *info, float q, float nearPole);
     130             : static FloatRGBA PlanetMix(OOPlanetTextureGeneratorInfo *info, float q, float nearPole);
     131             : 
     132             : 
     133             : enum
     134             : {
     135             :         kPlanetScale256x256                     = 1,
     136             :         kPlanetScale512x512,
     137             :         kPlanetScale1024x1024,
     138             :         kPlanetScale2048x2048,
     139             :         kPlanetScale4096x4096,
     140             :         
     141             :         kPlanetScaleReducedDetail       = kPlanetScale512x512,
     142             :         kPlanetScaleFullDetail          = kPlanetScale1024x1024,
     143             :         kPlanetScaleExtraDetail         = kPlanetScale2048x2048
     144             : };
     145             : 
     146             : 
     147             : @implementation OOPlanetTextureGenerator
     148             : 
     149             : - (id) initWithPlanetInfo:(NSDictionary *)planetInfo
     150             : {
     151             :         OOLog(@"texture.planet.generate", @"%@", @"Initialising planetary generator");
     152             : 
     153             :         // AllowCubeMap not used yet but might be in future
     154             :         if ((self = [super initWithPath:[NSString stringWithFormat:@"OOPlanetTexture@%p", self] options:kOOTextureAllowCubeMap]))
     155             :         {
     156             :                 OOLog(@"texture.planet.generate", @"Extracting parameters for generator %@",self);
     157             : 
     158             :                 _info.landFraction = OOClamp_0_1_f([planetInfo oo_floatForKey:@"land_fraction" defaultValue:0.3]);
     159             :                 _info.polarFraction = OOClamp_0_1_f([planetInfo oo_floatForKey:@"polar_fraction" defaultValue:0.05]);
     160             :                 _info.landColor = FloatRGBFromDictColor(planetInfo, @"land_color");
     161             :                 _info.seaColor = FloatRGBFromDictColor(planetInfo, @"sea_color");
     162             :                 _info.paleLandColor = FloatRGBFromDictColor(planetInfo, @"polar_land_color");
     163             :                 _info.polarSeaColor = FloatRGBFromDictColor(planetInfo, @"polar_sea_color");
     164             :                 [[planetInfo objectForKey:@"noise_map_seed"] getValue:&_info.seed];
     165             :                 if ([planetInfo objectForKey:@"cloud_alpha"])
     166             :                 {
     167             :                         OOLog(@"texture.planet.generate", @"%@", @"Extracting atmosphere parameters");
     168             :                         // we have an atmosphere:
     169             :                         _info.cloudAlpha = [planetInfo oo_floatForKey:@"cloud_alpha" defaultValue:1.0f];
     170             :                         _info.cloudFraction = OOClamp_0_1_f([planetInfo oo_floatForKey:@"cloud_fraction" defaultValue:0.3]);
     171             :                         _info.cloudColor = FloatRGBFromDictColor(planetInfo, @"cloud_color");
     172             :                         _info.paleCloudColor = FloatRGBFromDictColor(planetInfo, @"polar_cloud_color");
     173             :                 }
     174             :                 
     175             :                 OOGraphicsDetail detailLevel = [UNIVERSE detailLevel];
     176             :                 
     177             : #ifndef TEXGEN_TEST_RIG
     178             :                 if (detailLevel < DETAIL_LEVEL_SHADERS || [planetInfo oo_boolForKey:@"isMiniature" defaultValue:NO])
     179             :                 {
     180             :                         _planetScale = kPlanetScaleReducedDetail;
     181             :                 }
     182             :                 else if (detailLevel == DETAIL_LEVEL_SHADERS)
     183             :                 {
     184             :                         _planetScale = kPlanetScaleFullDetail;
     185             :                 }
     186             :                 else
     187             :                 {
     188             :                         _planetScale = kPlanetScaleExtraDetail;
     189             :                 }
     190             : #else
     191             :                 _planetScale = kPlanetScale4096x4096;
     192             : #endif
     193             :                 _info.perlin3d = [planetInfo oo_boolForKey:@"perlin_3d" defaultValue:detailLevel > DETAIL_LEVEL_SHADERS];
     194             :                 _info.planetAspectRatio = _info.perlin3d ? 2 : 1;
     195             :                 _info.planetScaleOffset = 8 - _info.planetAspectRatio;
     196             :         }
     197             :         
     198             :         return self;
     199             : }
     200             : 
     201             : 
     202             : + (OOTexture *) planetTextureWithInfo:(NSDictionary *)planetInfo
     203             : {
     204             :         OOTexture *result = nil;
     205             :         OOPlanetTextureGenerator *generator = [[self alloc] initWithPlanetInfo:planetInfo];
     206             :         if (generator != nil)
     207             :         {
     208             :                 result = [OOTexture textureWithGenerator:generator];
     209             :                 [generator release];
     210             :         }
     211             :         
     212             :         return result;
     213             : }
     214             : 
     215             : 
     216             : + (BOOL) generatePlanetTexture:(OOTexture **)texture andAtmosphere:(OOTexture **)atmosphere withInfo:(NSDictionary *)planetInfo
     217             : {
     218             :         NSParameterAssert(texture != NULL);
     219             :         
     220             :         OOPlanetTextureGenerator *diffuseGen = [[[self alloc] initWithPlanetInfo:planetInfo] autorelease];
     221             :         if (diffuseGen == nil)  return NO;
     222             :         
     223             :         OOPlanetAtmosphereGenerator *atmoGen = [diffuseGen atmosphereGenerator];
     224             :         if (atmoGen == nil)  return NO;
     225             :         
     226             :         *atmosphere = [OOTexture textureWithGenerator:atmoGen];
     227             :         if (*atmosphere == nil)  return NO;
     228             :         
     229             :         *texture = [OOTexture textureWithGenerator:diffuseGen enqueue: [atmoGen enqueued]];
     230             :         
     231             :         return *texture != nil;
     232             : }
     233             : 
     234             : 
     235             : + (BOOL) generatePlanetTexture:(OOTexture **)texture secondaryTexture:(OOTexture **)secondaryTexture withInfo:(NSDictionary *)planetInfo
     236             : {
     237             :         NSParameterAssert(texture != NULL);
     238             : 
     239             :         BOOL enqueue = NO;
     240             :         
     241             :         OOPlanetTextureGenerator *diffuseGen = [[[self alloc] initWithPlanetInfo:planetInfo] autorelease];
     242             :         if (diffuseGen == nil)  return NO;
     243             :         
     244             :         if (secondaryTexture != NULL)
     245             :         {
     246             :                 OOPlanetNormalMapGenerator *normalGen = [diffuseGen normalMapGenerator];
     247             :                 if (normalGen == nil)  return NO;
     248             :                 
     249             :                 *secondaryTexture = [OOTexture textureWithGenerator:normalGen];
     250             :                 if (*secondaryTexture == nil)  return NO;
     251             :                 enqueue = [normalGen enqueued];
     252             :         }
     253             :         
     254             :         *texture = [OOTexture textureWithGenerator:diffuseGen enqueue: enqueue];
     255             :         
     256             :         return *texture != nil;
     257             : }
     258             : 
     259             : 
     260             : + (BOOL) generatePlanetTexture:(OOTexture **)texture secondaryTexture:(OOTexture **)secondaryTexture andAtmosphere:(OOTexture **)atmosphere withInfo:(NSDictionary *)planetInfo
     261             : {
     262             :         NSParameterAssert(texture != NULL);
     263             :         
     264             :         BOOL enqueue = NO;
     265             : 
     266             :         OOPlanetTextureGenerator *diffuseGen = [[[self alloc] initWithPlanetInfo:planetInfo] autorelease];
     267             :         if (diffuseGen == nil)  return NO;
     268             :         
     269             :         if (secondaryTexture != NULL)
     270             :         {
     271             :                 OOPlanetNormalMapGenerator *normalGen = [diffuseGen normalMapGenerator];
     272             :                 if (normalGen == nil)  return NO;
     273             :                 
     274             :                 *secondaryTexture = [OOTexture textureWithGenerator:normalGen];
     275             :                 if (*secondaryTexture == nil)  return NO;
     276             :                 enqueue = [normalGen enqueued];
     277             :         }
     278             :         
     279             :         OOPlanetAtmosphereGenerator *atmoGen = [diffuseGen atmosphereGenerator];
     280             :         if (atmoGen == nil)  return NO;
     281             :         
     282             :         *atmosphere = [OOTexture textureWithGenerator:atmoGen];
     283             :         if (*atmosphere == nil)
     284             :         {
     285             :                 if (secondaryTexture != NULL) {
     286             :                         *secondaryTexture = nil;
     287             :                 }
     288             :                 return NO;
     289             :         }
     290             :         enqueue = enqueue || [atmoGen enqueued];
     291             : 
     292             :         OOLog(@"texture.planet.generate",@"Generator %@ has atmosphere %@",diffuseGen,*atmosphere);
     293             :         
     294             :         *texture = [OOTexture textureWithGenerator:diffuseGen enqueue: enqueue];
     295             :         return *texture != nil;
     296             : }
     297             : 
     298             : 
     299             : - (void) dealloc
     300             : {
     301             :         DESTROY(_nMapGenerator);
     302             :         DESTROY(_atmoGenerator);
     303             :         
     304             :         [super dealloc];
     305             : }
     306             : 
     307             : 
     308             : - (NSString *) descriptionComponents
     309             : {
     310             :         return [NSString stringWithFormat:@"seed: %u,%u land: %g", _info.seed.high, _info.seed.low, _info.landFraction];
     311             : }
     312             : 
     313             : 
     314             : - (uint32_t) textureOptions
     315             : {
     316             :         return PLANET_TEXTURE_OPTIONS;
     317             : }
     318             : 
     319             : 
     320             : - (NSString *) cacheKey
     321             : {
     322             :         NSString *type =(_nMapGenerator == nil) ? @"diffuse-baked" : @"diffuse-raw";
     323             :         if (_atmoGenerator != nil) type = [NSString stringWithFormat:@"%@-atmo", type];
     324             :         return [self cacheKeyForType:type];
     325             : }
     326             : 
     327             : 
     328             : - (NSString *) cacheKeyForType:(NSString *)type
     329             : {
     330             :         return [NSString stringWithFormat:@"OOPlanetTextureGenerator-%@@%u\n%u,%u/%g/%u,%u/%f,%f,%f/%f,%f,%f/%f,%f,%f/%f,%f,%f",
     331             :                         type, _planetScale,
     332             :                         _info.width, _info.height, _info.landFraction, _info.seed.high, _info.seed.low,
     333             :                         _info.landColor.r, _info.landColor.g, _info.landColor.b,
     334             :                         _info.seaColor.r, _info.seaColor.g, _info.seaColor.b,
     335             :                         _info.paleLandColor.r, _info.paleLandColor.g, _info.paleLandColor.b,
     336             :                         _info.polarSeaColor.r, _info.polarSeaColor.g, _info.polarSeaColor.b];
     337             : }
     338             : 
     339             : 
     340             : - (OOPlanetNormalMapGenerator *) normalMapGenerator
     341             : {
     342             :         if (_nMapGenerator == nil)
     343             :         {
     344             :                 _nMapGenerator = [[OOPlanetNormalMapGenerator alloc] initWithCacheKey:[self cacheKeyForType:@"normal"] seed:_info.seed];
     345             :         }
     346             :         return _nMapGenerator;
     347             : }
     348             : 
     349             : 
     350             : - (OOPlanetAtmosphereGenerator *) atmosphereGenerator
     351             : {
     352             :         if (_atmoGenerator == nil)
     353             :         {
     354             :                 _atmoGenerator = [[OOPlanetAtmosphereGenerator alloc] initWithCacheKey:[self cacheKeyForType:@"atmo"] seed:_info.seed andParent:self];
     355             :         }
     356             :         return _atmoGenerator;
     357             : }
     358             : 
     359             : 
     360             : - (BOOL)getResult:(OOPixMap *)outData
     361             :                    format:(OOTextureDataFormat *)outFormat
     362             :                         width:(uint32_t *)outWidth
     363             :                    height:(uint32_t *)outHeight
     364             : {
     365             :         BOOL waiting = NO;
     366             :         if (![self isReady])
     367             :         {
     368             :                 waiting = true;
     369             :                 OOLog(@"texture.planet.generate.wait", @"%s generator %@", "Waiting for", self);
     370             :         }
     371             :         
     372             :         BOOL result = [super getResult:outData format:outFormat originalWidth:outWidth originalHeight:outHeight];
     373             :         
     374             :         if (waiting)
     375             :         {
     376             :                 OOLog(@"texture.planet.generate.dequeue", @"%s generator %@", result ? "Dequeued" : "Failed to dequeue", self);
     377             :         }
     378             :         else
     379             :         {
     380             :                 OOLog(@"texture.planet.generate.dequeue", @"%s generator %@ without waiting.", result ? "Dequeued" : "Failed to dequeue", self);
     381             :         }
     382             :         
     383             :         return result;
     384             : }
     385             : 
     386             : 
     387             : - (void) loadTexture
     388             : {
     389             :         OOLog(@"texture.planet.generate.begin", @"Started generator %@", self);
     390             :         
     391             :         BOOL success = NO;
     392             :         BOOL generateNormalMap = (_nMapGenerator != nil);
     393             :         BOOL generateAtmosphere = (_atmoGenerator != nil);
     394             :         
     395             :         uint8_t         *buffer = NULL, *px = NULL;
     396             :         uint8_t         *nBuffer = NULL, *npx = NULL;
     397             :         uint8_t         *aBuffer = NULL, *apx = NULL;
     398             :         float           *randomBuffer = NULL;
     399             :         
     400             :         _height = _info.height = 1 << (_planetScale + _info.planetScaleOffset);
     401             :         _width = _info.width = _height * _info.planetAspectRatio;
     402             :         
     403             : #define FAIL_IF(cond)  do { if (EXPECT_NOT(cond))  goto END; } while (0)
     404             : #define FAIL_IF_NULL(x)  FAIL_IF((x) == NULL)
     405             :         
     406             :         buffer = malloc(4 * _width * _height);
     407             :         FAIL_IF_NULL(buffer);
     408             :         px = buffer;
     409             :         
     410             :         if (generateNormalMap)
     411             :         {
     412             :                 nBuffer = malloc(4 * _width * _height);
     413             :                 FAIL_IF_NULL(nBuffer);
     414             :                 npx = nBuffer;
     415             :         }
     416             :         
     417             :         if (generateAtmosphere)
     418             :         {
     419             :                 aBuffer = malloc(4 * _width * _height);
     420             :                 FAIL_IF_NULL(aBuffer);
     421             :                 apx = aBuffer;
     422             :         }
     423             :         
     424             :         FAIL_IF(!FillFBMBuffer(&_info));
     425             : #if DEBUG_DUMP_RAW
     426             :         [self dumpNoiseBuffer:_info.fbmBuffer];
     427             : #endif
     428             :         
     429             :         float paleClouds = (_info.cloudFraction * _info.fbmBuffer[0] < 1.0f - _info.cloudFraction) ? 0.0f : 1.0f;
     430             :         float poleValue = (_info.landFraction > 0.5f) ? 0.5f * _info.landFraction : 0.0f;
     431             :         float seaBias = _info.landFraction - 1.0f;
     432             :         
     433             :         _info.paleSeaColor = Blend(0.35f, _info.polarSeaColor, Blend(0.7f, _info.seaColor, _info.landColor));
     434             :         float normalScale = (1 << _planetScale)
     435             : #ifndef NDEBUG
     436             :                                                 // test-release only, make normalScale adjustable from within user defaults
     437             :                                                 * [[NSUserDefaults standardUserDefaults] oo_floatForKey:@"p3dnsf" defaultValue:1.0f]
     438             : #endif
     439             :                                                 ; // float normalScale = ...
     440             :         if (!generateNormalMap)  normalScale *= 3.0f;
     441             :         
     442             :         // Deep sea colour: sea darker past the continental shelf.
     443             :         _info.deepSeaColor = Blend(0.85f, _info.seaColor, (FloatRGB){ 0, 0, 0 });
     444             :         
     445             :         int x, y;
     446             :         FloatRGBA color;
     447             :         Vector norm;
     448             :         float q, yN, yS, yW, yE, nearPole;
     449             :         GLfloat shade;
     450             :         float rHeight = 1.0f / _height;
     451             :         float fy, fHeight = _height;
     452             :         // The second parameter is the temperature fraction. Most favourable: 1.0f,  little ice. Most unfavourable: 0.0f, frozen planet. TODO: make it dependent on ranrot / planetinfo key...
     453             :         SetMixConstants(&_info, 1.0f-_info.polarFraction);  // no need to recalculate them inside each loop!
     454             :         
     455             :         // first pass, calculate q.
     456             :         _info.qBuffer = malloc(_width * _height * sizeof (float));
     457             :         FAIL_IF_NULL(_info.qBuffer);
     458             :         
     459             :         for (y = (int)_height - 1, fy = (float)y; y >=0; y--, fy--)
     460             :         {
     461             :                 nearPole = (2.0f * fy - fHeight) * rHeight;
     462             :                 nearPole *= nearPole;
     463             :                 
     464             :                 for (x = (int)_width - 1; x >=0; x--)
     465             :                 {
     466             :                         _info.qBuffer[y * _width + x] = QFactor(_info.fbmBuffer, x, y, _width, poleValue, seaBias, nearPole);
     467             :                 }
     468             :         }
     469             :         
     470             :         // second pass, use q.
     471             :         float cloudFraction = _info.cloudFraction;
     472             :         unsigned widthMask = _width - 1;
     473             :         unsigned heightMask = _height - 1;
     474             :         
     475             :         for (y = (int)_height - 1, fy = (float)y; y >= 0; y--, fy--)
     476             :         {
     477             :                 nearPole = (2.0f * fy - fHeight) * rHeight;
     478             :                 nearPole *= nearPole;
     479             :                 
     480             :                 for (x = (int)_width - 1; x >= 0; x--)
     481             :                 {
     482             :                         q = _info.qBuffer[y * _width + x];      // no need to use GetQ, x and y are always within bounds.
     483             :                         yN = GetQ(_info.qBuffer, x, y - 1, _width, _height, widthMask, heightMask);     // recalculates x & y if they go out of bounds.
     484             :                         yS = GetQ(_info.qBuffer, x, y + 1, _width, _height, widthMask, heightMask);
     485             :                         yW = GetQ(_info.qBuffer, x - 1, y, _width, _height, widthMask, heightMask);
     486             :                         yE = GetQ(_info.qBuffer, x + 1, y, _width, _height, widthMask, heightMask);
     487             :                         
     488             :                         color = PlanetMix(&_info, q, nearPole);
     489             :                         
     490             :                         norm = vector_normal(make_vector(normalScale * (yE - yW), normalScale * (yN - yS), 1.0f));
     491             :                         if (generateNormalMap)
     492             :                         {
     493             :                                 shade = 1.0f;
     494             :                                 
     495             :                                 // Flatten the sea.
     496             :                                 norm = OOVectorInterpolate(norm, kBasisZVector, color.a);
     497             :                                 
     498             :                                 // Put norm in normal map, scaled from [-1..1] to [0..255].
     499             :                                 *npx++ = 127.5f * (norm.y + 1.0f);
     500             :                                 *npx++ = 127.5f * (-norm.x + 1.0f);
     501             :                                 *npx++ = 127.5f * (norm.z + 1.0f);
     502             :                                 
     503             :                                 *npx++ = 255.0f * color.a;      // Specular channel.
     504             :                         }
     505             :                         else
     506             :                         {
     507             :                                 //      Terrain shading - lambertian lighting from straight above.
     508             :                                 shade = norm.z;
     509             :                                 
     510             :                                 /*      We don't want terrain shading in the sea. The alpha channel
     511             :                                         of color is a measure of "seaishness" for the specular map,
     512             :                                         so we can recycle that to avoid branching.
     513             :                                         -- Ahruman
     514             :                                 */
     515             :                                 shade += color.a - color.a * shade;     // equivalent to - but slightly faster than - previous implementation.
     516             :                         }
     517             :                         
     518             :                         *px++ = 255.0f * color.r * shade;
     519             :                         *px++ = 255.0f * color.g * shade;
     520             :                         *px++ = 255.0f * color.b * shade;
     521             :                         
     522             :                         *px++ = 0;      // FIXME: light map goes here.
     523             :                         
     524             :                         if (generateAtmosphere)
     525             :                         {
     526             :                                 q = QFactor(_info.fbmBuffer, x, y, _width, paleClouds, cloudFraction, nearPole);
     527             :                                 color = CloudMix(&_info, q, nearPole);
     528             :                                 *apx++ = 255.0f * color.r;
     529             :                                 *apx++ = 255.0f * color.g;
     530             :                                 *apx++ = 255.0f * color.b;
     531             :                                 *apx++ = 255.0f * color.a * _info.cloudAlpha;
     532             :                         }
     533             :                 }
     534             :         }
     535             :         
     536             :         success = YES;
     537             :         _format = kOOTextureDataRGBA;
     538             :         
     539             : END:
     540             :         FREE(_info.fbmBuffer);
     541             :         FREE(_info.qBuffer);
     542             :         FREE(randomBuffer);
     543             :         if (success)
     544             :         {
     545             :                 _data = buffer;
     546             :                 if (generateNormalMap) [_nMapGenerator completeWithData:nBuffer width:_width height:_height];
     547             :                 if (generateAtmosphere) [_atmoGenerator completeWithData:aBuffer width:_width height:_height];
     548             :         }
     549             :         else
     550             :         {
     551             :                 FREE(buffer);
     552             :                 FREE(nBuffer);
     553             :                 FREE(aBuffer);
     554             :         }
     555             :         DESTROY(_nMapGenerator);
     556             :         DESTROY(_atmoGenerator);
     557             :         
     558             :         OOLog(@"texture.planet.generate.complete", @"Completed generator %@ %@successfully", self, success ? @"" : @"un");
     559             :         
     560             : #if DEBUG_DUMP
     561             :         if (success)
     562             :         {
     563             :                 NSString *diffuseName = [NSString stringWithFormat:@"planet-%u-%u-diffuse-new", _info.seed.high, _info.seed.low];
     564             :                 NSString *lightsName = [NSString stringWithFormat:@"planet-%u-%u-lights-new", _info.seed.high, _info.seed.low];
     565             :                 
     566             :                 [[UNIVERSE gameView] dumpRGBAToRGBFileNamed:diffuseName
     567             :                                                                    andGrayFileNamed:lightsName
     568             :                                                                                           bytes:buffer
     569             :                                                                                           width:_width
     570             :                                                                                          height:_height
     571             :                                                                                    rowBytes:_width * 4];
     572             :         }
     573             : #endif
     574             : }
     575             : 
     576             : 
     577             : #if DEBUG_DUMP_RAW
     578             : 
     579             : - (void) dumpNoiseBuffer:(float *)noise
     580             : {
     581             :         NSString *noiseName = [NSString stringWithFormat:@"planet-%u-%u-noise-new", _info.seed.high, _info.seed.low];
     582             :         
     583             :         uint8_t *noisePx = malloc(_width * _height);
     584             :         unsigned x, y;
     585             :         for (y = 0; y < _height; y++)
     586             :         {
     587             :                 for (x = 0; x < _width; x++)
     588             :                 {
     589             :                         noisePx[y * _width + x] = 255.0f * noise[y * _width + x];
     590             :                 }
     591             :         }
     592             :         
     593             :         [[UNIVERSE gameView] dumpGrayToFileNamed:noiseName
     594             :                                                                            bytes:noisePx
     595             :                                                                            width:_width
     596             :                                                                           height:_height
     597             :                                                                         rowBytes:_width];
     598             :         FREE(noisePx);
     599             : }
     600             : 
     601             : #endif
     602             : 
     603             : @end
     604             : 
     605             : 
     606             : OOINLINE float Lerp(float v0, float v1, float fraction)
     607             : {
     608             :         // Linear interpolation - equivalent to v0 * (1.0f - fraction) + v1 * fraction.
     609             :         return v0 + fraction * (v1 - v0);
     610             : }
     611             : 
     612             : 
     613             : static FloatRGB Blend(float fraction, FloatRGB a, FloatRGB b)
     614             : {
     615             :         return (FloatRGB)
     616             :         {
     617             :                 Lerp(b.r, a.r, fraction),
     618             :                 Lerp(b.g, a.g, fraction),
     619             :                 Lerp(b.b, a.b, fraction)
     620             :         };
     621             : }
     622             : 
     623             : 
     624             : static float BlendAlpha(float fraction, float a, float b)
     625             : {
     626             :         return Lerp(b, a, fraction);
     627             : }
     628             : 
     629             : 
     630             : static FloatRGBA CloudMix(OOPlanetTextureGeneratorInfo *info, float q, float nearPole)
     631             : {
     632             : //#define AIR_ALPHA                             (0.15f)
     633             : //#define CLOUD_ALPHA                           (1.0f)
     634             : // CIM: make distinction between cloud and not-cloud bigger
     635             : #define AIR_ALPHA                               (0.05f)
     636             : #define CLOUD_ALPHA                             (2.0f)
     637             : 
     638             : #define POLAR_BOUNDARY                  (0.33f)
     639             : #define CLOUD_BOUNDARY                  (0.5f)
     640             : #define RECIP_CLOUD_BOUNDARY    (1.0f / CLOUD_BOUNDARY)
     641             : 
     642             :         FloatRGB cloudColor = info->cloudColor;
     643             :         float alpha = info->cloudAlpha, portion = 0.0f;
     644             :         
     645             :         q -= CLOUD_BOUNDARY * 0.5f;
     646             :         
     647             :         if (nearPole > POLAR_BOUNDARY)
     648             :         {
     649             :                 portion = nearPole > POLAR_BOUNDARY + 0.2f ? 1.0f : (nearPole - POLAR_BOUNDARY) * 5.0f;
     650             :                 cloudColor = Blend(portion, info->paleCloudColor, cloudColor);
     651             :                  
     652             :                 portion = nearPole > POLAR_BOUNDARY + 0.625f ? 1.0f : (nearPole - POLAR_BOUNDARY) * 1.6f;
     653             :         }
     654             :         
     655             :         if (q <= 0.0f)
     656             :         {
     657             :                 if (q >= -CLOUD_BOUNDARY)
     658             :                 {
     659             :                         alpha *= BlendAlpha(-q * 0.5f * RECIP_CLOUD_BOUNDARY + 0.5f, CLOUD_ALPHA, AIR_ALPHA);
     660             :                 }
     661             :                 else
     662             :                 {
     663             :                         alpha *= CLOUD_ALPHA;
     664             :                 }
     665             :         }
     666             :         else
     667             :         {
     668             :                 if (q < CLOUD_BOUNDARY)
     669             :                 {
     670             :                         alpha *= BlendAlpha( q * 0.5f * RECIP_CLOUD_BOUNDARY + 0.5f,  AIR_ALPHA,CLOUD_ALPHA);
     671             :                 }
     672             :                 else
     673             :                 {
     674             :                         alpha *= AIR_ALPHA;
     675             :                 }
     676             :         }
     677             :         // magic numbers! at the poles we have fairly thin air.
     678             :         alpha *= BlendAlpha(portion, 0.6f, 1.0f);
     679             :         if (alpha > 1.0)
     680             :         {
     681             :                 alpha = 1.0;
     682             :         }
     683             : 
     684             :         return (FloatRGBA){ cloudColor.r, cloudColor.g, cloudColor.b, alpha };
     685             : }
     686             : 
     687             : 
     688             : static FloatRGBA PlanetMix(OOPlanetTextureGeneratorInfo *info, float q, float nearPole)
     689             : {
     690             : #define RECIP_COASTLINE_PORTION         (160.0f)
     691             : #define COASTLINE_PORTION                       (1.0f / RECIP_COASTLINE_PORTION)
     692             : #define SHALLOWS                                        (2.0f * COASTLINE_PORTION)      // increased shallows area.
     693             : #define RECIP_SHALLOWS                          (1.0f / SHALLOWS)
     694             : // N.B.: DEEPS can't be more than RECIP_COASTLINE_PORTION * COASTLINE_PORTION!
     695             : #define DEEPS                                           (40.0f * COASTLINE_PORTION) 
     696             : #define RECIP_DEEPS                                     (1.0f / DEEPS)
     697             :         
     698             :         const FloatRGB white = { 1.0f, 1.0f, 1.0f };
     699             :         FloatRGB diffuse;
     700             :         // windows specular 'fix': 0 was showing pitch black continents when on the dark side, 0.01 shows the same shading as on Macs.
     701             :         // TODO: a less hack-like fix.
     702             :         float specular = 0.01f;
     703             :         
     704             :         if (q <= 0.0f)
     705             :         {
     706             :                 // Below datum - sea.
     707             :                 if (q > -SHALLOWS)
     708             :                 {
     709             :                         // Coastal waters.
     710             :                         diffuse = Blend(-q * RECIP_SHALLOWS, info->seaColor, info->paleSeaColor);
     711             :                         specular = 1.0f;
     712             :                 }
     713             :                 else
     714             :                 {
     715             :                         // Open sea.
     716             :                         if (q > -DEEPS)  diffuse = Blend(-q * RECIP_DEEPS, info->deepSeaColor, info->seaColor);
     717             :                         else  diffuse = info->deepSeaColor;
     718             :                         specular = Lerp(1.0f, 0.85f, -q);
     719             :                 }
     720             :         }
     721             :         else if (q < COASTLINE_PORTION)
     722             :         {
     723             :                 // Coastline.
     724             :                 specular = q * RECIP_COASTLINE_PORTION;
     725             :                 diffuse = Blend(specular, info->landColor, info->paleSeaColor);
     726             :                 specular = 1.0f - specular;
     727             :         }
     728             :         else if (q > 1.0f)
     729             :         {
     730             :                 // High up - snow-capped peaks. With overrides q can range between -2 to +2.
     731             :                 diffuse = white;
     732             :         }
     733             :         else if (q > info->mix_hi)
     734             :         {
     735             :                 diffuse = Blend((q - info->mix_hi) * info->mix_ih, white, info->paleLandColor);        // Snowline.
     736             :         }
     737             :         else
     738             :         {
     739             :                 // Normal land.
     740             :                 diffuse = Blend((info->mix_hi - q) * info->mix_oh, info->landColor, info->paleLandColor);
     741             :         }
     742             :         
     743             : #if POLAR_CAPS
     744             :         // (q > mix_polarCap + mix_polarCap - nearPole) ==  ((nearPole + q) / 2 > mix_polarCap)
     745             :         float phi = info->mix_polarCap + info->mix_polarCap - nearPole;
     746             :         if (q > phi)  // (nearPole + q) / 2 > pole
     747             :         {
     748             :                 // thinner to thicker ice.
     749             :                 specular = q > phi + 0.02f ? 1.0f : 0.2f + (q - phi) * 40.0f;        // (q - phi) * 40 == ((q-phi) / 0.02) * 0.8
     750             :                 //diffuse = info->polarSeaColor;
     751             :                 diffuse = Blend(specular, info->polarSeaColor, diffuse);
     752             :                 specular = specular * 0.5f; // softer contours under ice, but still contours.
     753             :         }
     754             : #endif
     755             :         
     756             :         return (FloatRGBA){ diffuse.r, diffuse.g, diffuse.b, specular };
     757             : }
     758             : 
     759             : 
     760             : static FloatRGB FloatRGBFromDictColor(NSDictionary *dictionary, NSString *key)
     761             : {
     762             :         OOColor *color = [dictionary objectForKey:key];
     763             :         NSCAssert1([color isKindOfClass:[OOColor class]], @"Expected OOColor, got %@", [color class]);
     764             :         
     765             :         return (FloatRGB){ [color redComponent] * ALBEDO_FACTOR, [color greenComponent] * ALBEDO_FACTOR, [color blueComponent] * ALBEDO_FACTOR };
     766             : }
     767             : 
     768             : 
     769             : OOINLINE float Hermite(float q)
     770             : {
     771             :         return 3.0f * q * q - 2.0f * q * q * q;
     772             : }
     773             : 
     774             : 
     775             : #if __BIG_ENDIAN__
     776             : #define iman_ 1
     777             : #else
     778             : #define iman_ 0
     779             : #endif
     780             : 
     781             :  // (same behaviour as, but faster than, FLOAT->INT)
     782             :  //Works OK for -32728 to 32727.99999236688
     783             : OOINLINE int32_t fast_floor(double val)
     784             : {
     785             :    val += 68719476736.0 * 1.5;
     786             :    return (((int32_t*)&val)[iman_] >> 16);
     787             : }
     788             : 
     789             : 
     790             : static BOOL GenerateFBMNoise(OOPlanetTextureGeneratorInfo *info);
     791             : static BOOL GenerateFBMNoise3D(OOPlanetTextureGeneratorInfo *info);
     792             : 
     793             : 
     794             : static BOOL FillFBMBuffer(OOPlanetTextureGeneratorInfo *info)
     795             : {
     796             :         NSCParameterAssert(info != NULL);
     797             :         
     798             :         // Allocate result buffer.
     799             :         info->fbmBuffer = calloc(info->width * info->height, sizeof (float));
     800             :         if (info->fbmBuffer != NULL)
     801             :         {
     802             :                 if (!info->perlin3d)
     803             :                 {
     804             :                         GenerateFBMNoise(info);
     805             :                 }
     806             :                 else
     807             :                 {
     808             :                         GenerateFBMNoise3D(info);
     809             :                 }
     810             :         
     811             :                 return YES;
     812             :         }
     813             :         return NO;
     814             : }
     815             : 
     816             : 
     817             : 
     818             : enum
     819             : {
     820             :         //      Size of permutation buffer used to map integer coordinates to gradients. Must be power of two.
     821             :         kPermutationCount               = 1 << 10,
     822             :         kPermutationMask                = kPermutationCount - 1,
     823             :         
     824             :         // Number of different gradient vectors used. The most important thing is that the gradients are evenly distributed and sum to 0.
     825             :         kGradientCount                  = 12
     826             : };
     827             : 
     828             : 
     829             : #define LUT_DOT 1
     830             : 
     831             : 
     832             : #if !LUT_DOT
     833             : static const Vector kGradients[kGradientCount] =
     834             : {
     835             :         {  1,  1,  0 },
     836             :         { -1,  1,  0 },
     837             :         {  1, -1,  0 },
     838             :         { -1, -1,  0 },
     839             :         {  1,  0,  1 },
     840             :         { -1,  0,  1 },
     841             :         {  1,  0, -1 },
     842             :         { -1,  0, -1 },
     843             :         {  0,  1,  1 },
     844             :         {  0, -1,  1 },
     845             :         {  0,  1, -1 },
     846             :         {  0, -1, -1 }
     847             : };
     848             : #else
     849             : static const uint8_t kGradients[kGradientCount][3] =
     850             : {
     851             :         {  2,  2,  1 },
     852             :         {  0,  2,  1 },
     853             :         {  2,  0,  1 },
     854             :         {  0,  0,  1 },
     855             :         {  2,  1,  2 },
     856             :         {  0,  1,  2 },
     857             :         {  2,  1,  0 },
     858             :         {  0,  1,  0 },
     859             :         {  1,  2,  2 },
     860             :         {  1,  0,  2 },
     861             :         {  1,  2,  0 },
     862             :         {  1,  0,  0 }
     863             : };
     864             : 
     865             : 
     866             : /*      Attempted speedup that didn't pan out, but might inspire something better.
     867             :         Since our gradient vectors' components are all -1, 0 or 1, we should be
     868             :         able to calculate the dot product without any multiplication at all, by
     869             :         simply summing the right combination of (x, y, z), (0, 0, 0) and (-x, -y, -z).
     870             :         
     871             :         This turns out to be slightly slower than using multiplication, even if
     872             :         the negations are precalculated.
     873             : */
     874             : OOINLINE float TDot3(const uint8_t grad[3], float x, float y, float z)
     875             : {
     876             :         float xt[3] = { -x, 0.0f, x };
     877             :         float yt[3] = { -y, 0.0f, y };
     878             :         float zt[3] = { -z, 0.0f, z };
     879             :         
     880             :         return xt[grad[0]] + yt[grad[1]] + zt[grad[2]];
     881             : }
     882             : #endif
     883             : 
     884             : 
     885             : // Sample 3D noise function defined by kGradients and permutation table at point p.
     886             : static float SampleNoise3D(OOPlanetTextureGeneratorInfo *info, Vector p)
     887             : {
     888             :         uint16_t        *permutations = info->permutations;
     889             :         
     890             :         // Split coordinates into integer and fractional parts.
     891             :         float           fx = floor(p.x);
     892             :         float           fy = floor(p.y);
     893             :         float           fz = floor(p.z);
     894             :         int                     X = fx;
     895             :         int                     Y = fy;
     896             :         int                     Z = fz;
     897             :         float           x = p.x - fx;
     898             :         float           y = p.y - fy;
     899             :         float           z = p.z - fz;
     900             :         
     901             :         // Select gradient for each corner.
     902             : #define PERM(v) permutations[(v) & kPermutationMask]
     903             :         
     904             :         unsigned PZ0 = PERM(Z);
     905             :         unsigned PZ1 = PERM(Z + 1);
     906             :         
     907             :         unsigned PY0Z0 = PERM(Y + PZ0);
     908             :         unsigned PY1Z0 = PERM(Y + 1 + PZ0);
     909             :         unsigned PY0Z1 = PERM(Y + PZ1);
     910             :         unsigned PY1Z1 = PERM(Y + 1 + PZ1);
     911             :         
     912             :         unsigned gi000 = PERM(X     + PY0Z0);
     913             :         unsigned gi010 = PERM(X     + PY1Z0);
     914             :         unsigned gi100 = PERM(X + 1 + PY0Z0);
     915             :         unsigned gi110 = PERM(X + 1 + PY1Z0);
     916             :         unsigned gi001 = PERM(X     + PY0Z1);
     917             :         unsigned gi011 = PERM(X     + PY1Z1);
     918             :         unsigned gi101 = PERM(X + 1 + PY0Z1);
     919             :         unsigned gi111 = PERM(X + 1 + PY1Z1);
     920             :         
     921             : #undef PERM
     922             :         
     923             :         //      Calculate noise contributions from each of the eight corners.
     924             : #if !LUT_DOT
     925             : #define DOT3(idx, x_, y_, z_)  dot_product(kGradients[(idx) % kGradientCount], (Vector){ (x_), (y_), (z_) })
     926             : #else
     927             : #define DOT3(idx, x_, y_, z_)  TDot3(kGradients[(idx) % kGradientCount], (x_), (y_), (z_))
     928             : #endif
     929             :         
     930             :         float x1 = x - 1.0f;
     931             :         float y1 = y - 1.0f;
     932             :         float z1 = z - 1.0f;
     933             :         float n000 = DOT3(gi000, x , y , z );
     934             :         float n010 = DOT3(gi010, x , y1, z );
     935             :         float n100 = DOT3(gi100, x1, y , z );
     936             :         float n110 = DOT3(gi110, x1, y1, z );
     937             :         float n001 = DOT3(gi001, x , y , z1);
     938             :         float n011 = DOT3(gi011, x , y1, z1);
     939             :         float n101 = DOT3(gi101, x1, y , z1);
     940             :         float n111 = DOT3(gi111, x1, y1, z1);
     941             :         
     942             : #undef DOT3
     943             :         
     944             :         // Compute the fade curve value for each of x, y, z
     945             :         float u = Hermite(x);
     946             :         float v = Hermite(y);
     947             :         float w = Hermite(z);
     948             :         
     949             :         // Interpolate along the contributions from each of the corners.
     950             :         float nx00 = Lerp(n000, n100, u);
     951             :         float nx01 = Lerp(n001, n101, u);
     952             :         float nx10 = Lerp(n010, n110, u);
     953             :         float nx11 = Lerp(n011, n111, u);
     954             :         
     955             :         float nxy0 = Lerp(nx00, nx10, v);
     956             :         float nxy1 = Lerp(nx01, nx11, v);
     957             :         
     958             :         float nxyz = Lerp(nxy0, nxy1, w);
     959             :         
     960             :         return nxyz;
     961             : }
     962             : 
     963             : 
     964             : //      Noise map generator
     965             : static BOOL MakePermutationTable(OOPlanetTextureGeneratorInfo *info)
     966             : {
     967             :         uint16_t *perms = malloc(sizeof *info->permutations * kPermutationCount);
     968             :         if (EXPECT_NOT(perms == NULL))  return NO;
     969             :         
     970             :         perms[0] = 0;
     971             :         uint16_t n;
     972             :         for (n = 1; n < kPermutationCount; n++)
     973             :         {
     974             :                 perms[n] = RanrotWithSeed(&info->seed) & kPermutationMask;
     975             :         }
     976             :         
     977             :         info->permutations = perms;
     978             :         return YES;
     979             : }
     980             : 
     981             : 
     982             : static BOOL GenerateFBMNoise3D(OOPlanetTextureGeneratorInfo *info)
     983             : {
     984             :         BOOL OK = NO;
     985             :         
     986             :         FAIL_IF(!MakePermutationTable(info));
     987             :         
     988             :         unsigned x, y, width = info->width, height = info->height;
     989             :         float lon, lat; // Longitude and latitude in radians.
     990             :         float dlon = 2.0f * M_PI / width;
     991             :         float dlat = M_PI / height;
     992             :         float *px = info->fbmBuffer;
     993             :         
     994             :         for (y = 0, lat = -M_PI_2; y < height; y++, lat += dlat)
     995             :         {
     996             :                 float las = sin(lat);
     997             :                 float lac = cos(lat);
     998             :                 
     999             :                 for (x = 0, lon = -M_PI; x < width; x++, lon += dlon)
    1000             :                 {
    1001             :                         // FIXME: in real life, we really don't want sin and cos per pixel.
    1002             :                         // Convert spherical coordinates to vector.
    1003             :                         float los = sin(lon);
    1004             :                         float loc = cos(lon);
    1005             :                         
    1006             :                         Vector p =
    1007             :                         {
    1008             :                                 los * lac,
    1009             :                                 las,
    1010             :                                 loc * lac
    1011             :                         };
    1012             :                         
    1013             : #if 1
    1014             :                         // fBM
    1015             :                         unsigned octaveMask = 4;
    1016             :                         float octave = octaveMask;
    1017             :                         octaveMask -= 1;
    1018             :                         float scale = 0.4f;
    1019             :                         float sum = 0;
    1020             :                         
    1021             :                         while ((octaveMask + 1) < height)
    1022             :                         {
    1023             :                                 Vector ps = vector_multiply_scalar(p, octave);
    1024             :                                 sum += scale * SampleNoise3D(info, ps);
    1025             :                                 
    1026             :                                 octave *= 2.0f;
    1027             :                                 octaveMask = (octaveMask << 1) | 1;
    1028             :                                 scale *= 0.5f;
    1029             :                         }
    1030             : #else
    1031             :                         // Single octave
    1032             :                         p = vector_multiply_scalar(p, 4.0f);
    1033             :                         float sum = 0.5f * SampleNoise3D(info, p);
    1034             : #endif
    1035             :                         
    1036             :                         *px++ = sum + 0.5f;
    1037             :                 }
    1038             :         }
    1039             :         
    1040             : END:
    1041             :         FREE(info->permutations);
    1042             :         return OK;
    1043             : }
    1044             : 
    1045             : 
    1046             : // Old 2D value noise.
    1047             : 
    1048             : static void FillRandomBuffer(float *randomBuffer, RANROTSeed seed)
    1049             : {
    1050             :         unsigned i, len = kRandomBufferSize * kRandomBufferSize;
    1051             :         for (i = 0; i < len; i++)
    1052             :         {
    1053             :                 randomBuffer[i] = randfWithSeed(&seed);
    1054             :         }
    1055             : }
    1056             : 
    1057             : 
    1058             : static void AddNoise(OOPlanetTextureGeneratorInfo *info, float *randomBuffer, float octave, unsigned octaveMask, float scale, float *qxBuffer, int *ixBuffer)
    1059             : {
    1060             :         unsigned        x, y;
    1061             :         unsigned        width = info->width, height = info->height;
    1062             :         int                     ix, jx, iy, jy;
    1063             :         float           rr = octave / width;
    1064             :         float           fx, fy, qx, qy, rix, rjx, rfinal;
    1065             :         float           *dst = info->fbmBuffer;
    1066             :         
    1067             :         for (fy = 0, y = 0; y < height; fy++, y++)
    1068             :         {
    1069             :                 qy = fy * rr;
    1070             :                 iy = fast_floor(qy);
    1071             :                 jy = (iy + 1) & octaveMask;
    1072             :                 qy = Hermite(qy - iy);
    1073             :                 iy &= (kRandomBufferSize - 1);
    1074             :                 jy &= (kRandomBufferSize - 1);
    1075             :                 
    1076             :                 for (fx = 0, x = 0; x < width; fx++, x++)
    1077             :                 {
    1078             :                         if (y == 0)
    1079             :                         {
    1080             :                                 // first pass: initialise buffers.
    1081             :                                 qx = fx * rr;
    1082             :                                 ix = fast_floor(qx);
    1083             :                                 qx -= ix;
    1084             :                                 ix &= (kRandomBufferSize - 1);
    1085             :                                 ixBuffer[x] = ix;
    1086             :                                 qxBuffer[x] = Hermite(qx);
    1087             :                         }
    1088             :                         else
    1089             :                         {
    1090             :                                 // later passes: grab the stored values.
    1091             :                                 ix = ixBuffer[x];
    1092             :                                 qx = qxBuffer[x];
    1093             :                         }
    1094             :                         
    1095             :                         jx = (ix + 1) & octaveMask;
    1096             :                         jx &= (kRandomBufferSize - 1);
    1097             :                         
    1098             :                         rix = Lerp(randomBuffer[iy * kRandomBufferSize + ix], randomBuffer[iy * kRandomBufferSize + jx], qx);
    1099             :                         rjx = Lerp(randomBuffer[jy * kRandomBufferSize + ix], randomBuffer[jy * kRandomBufferSize + jx], qx);
    1100             :                         rfinal = Lerp(rix, rjx, qy);
    1101             :                         
    1102             :                         *dst++ += scale * rfinal;
    1103             :                 }
    1104             :         }
    1105             : }
    1106             : 
    1107             : 
    1108             : static BOOL GenerateFBMNoise(OOPlanetTextureGeneratorInfo *info)
    1109             : {
    1110             :         // Allocate the temporary buffers we need in one fell swoop, to avoid administrative overhead.
    1111             :         size_t randomBufferSize = kRandomBufferSize * kRandomBufferSize * sizeof (float);
    1112             :         size_t qxBufferSize = info->width * sizeof (float);
    1113             :         size_t ixBufferSize = info->width * sizeof (int);
    1114             :         char *sharedBuffer = malloc(randomBufferSize + qxBufferSize + ixBufferSize);
    1115             :         if (sharedBuffer == NULL)  return NO;
    1116             :         
    1117             :         float *randomBuffer = (float *)sharedBuffer;
    1118             :         float *qxBuffer = (float *)(sharedBuffer + randomBufferSize);
    1119             :         int *ixBuffer = (int *)(sharedBuffer + randomBufferSize + qxBufferSize);
    1120             :         
    1121             :         // Get us some value noise.
    1122             :         FillRandomBuffer(randomBuffer, info->seed);
    1123             :         
    1124             :         // Generate basic fBM noise.
    1125             :         unsigned height = info->height;
    1126             :         unsigned octaveMask = 8 * info->planetAspectRatio;
    1127             :         float octave = octaveMask;
    1128             :         octaveMask -= 1;
    1129             :         float scale = 0.5f;
    1130             :         
    1131             :         while ((octaveMask + 1) < height)
    1132             :         {
    1133             :                 AddNoise(info, randomBuffer, octave, octaveMask, scale, qxBuffer, ixBuffer);
    1134             :                 octave *= 2.0f;
    1135             :                 octaveMask = (octaveMask << 1) | 1;
    1136             :                 scale *= 0.5f;
    1137             :         }
    1138             :         
    1139             :         FREE(sharedBuffer);
    1140             :         return YES;
    1141             : }
    1142             : 
    1143             : 
    1144             : 
    1145             : static float QFactor(float *accbuffer, int x, int y, unsigned width, float polar_y_value, float bias, float polar_y)
    1146             : {
    1147             :         float q = accbuffer[y * width + x];     // 0.0 -> 1.0
    1148             :         q += bias;
    1149             :         
    1150             :         // Polar Y smooth.
    1151             :         q = q * (1.0f - polar_y) + polar_y * polar_y_value;
    1152             : 
    1153             :         return q;
    1154             : }
    1155             : 
    1156             : 
    1157             : static float GetQ(float *qbuffer, int x, int y, unsigned width, unsigned height, unsigned widthMask, unsigned heightMask)
    1158             : {
    1159             :         // Correct Y wrapping mode, unoptimised.
    1160             :         //if (y < 0) { y = -y - 1; x += width / 2; }
    1161             :         //else if (y >= height) { y = height - (y - height)  - 1; x += width / 2; }
    1162             :         // now let's wrap x.
    1163             :         //x = x % width;
    1164             : 
    1165             :         // Correct Y wrapping mode, faster method. In the following lines of code, both
    1166             :         // width and height are assumed to be powers of 2: 512, 1024, 2048, etc...
    1167             :         if (y & height) { y = (y ^ heightMask) & heightMask; x += width >> 1; }
    1168             :         // x wrapping.
    1169             :         x &= widthMask;
    1170             :         return  qbuffer[y * width + x];
    1171             : }
    1172             : 
    1173             : 
    1174             : static void SetMixConstants(OOPlanetTextureGeneratorInfo *info, float temperatureFraction)
    1175             : {
    1176             :         info->mix_hi = 0.66667f * info->landFraction;
    1177             :         info->mix_oh = 1.0f / info->mix_hi;
    1178             :         info->mix_ih = 1.0f / (1.0f - info->mix_hi);
    1179             :         info->mix_polarCap = temperatureFraction * (0.28f + 0.24f * info->landFraction);  // landmasses make the polar cap proportionally bigger, but not too much bigger.
    1180             : }
    1181             : 
    1182             : 
    1183             : @implementation OOPlanetNormalMapGenerator
    1184             : 
    1185             : - (id) initWithCacheKey:(NSString *)cacheKey seed:(RANROTSeed)seed
    1186             : {
    1187             :         // AllowCubeMap not used yet but might be in future
    1188             :         if ((self = [super initWithPath:[NSString stringWithFormat:@"OOPlanetNormalTexture@%p", self] options:kOOTextureAllowCubeMap]))
    1189             :         {
    1190             :                 _enqueued = NO;
    1191             :                 _cacheKey = [cacheKey copy];
    1192             :                 _seed = seed;
    1193             :         }
    1194             :         return self;
    1195             : }
    1196             : 
    1197             : 
    1198             : - (void) dealloc
    1199             : {
    1200             :         DESTROY(_cacheKey);
    1201             :         
    1202             :         [super dealloc];
    1203             : }
    1204             : 
    1205             : 
    1206             : - (NSString *) cacheKey
    1207             : {
    1208             :         return _cacheKey;
    1209             : }
    1210             : 
    1211             : 
    1212             : - (uint32_t) textureOptions
    1213             : {
    1214             :         return PLANET_TEXTURE_OPTIONS;
    1215             : }
    1216             : 
    1217             : 
    1218             : - (BOOL) enqueue
    1219             : {
    1220             :         /*      This generator doesn't do any work, so it doesn't need to be queued
    1221             :                 at the normal time.
    1222             :                 (The alternative would be for it to block a work thread waiting for
    1223             :                 the real generator to complete, which seemed silly.)
    1224             :         */
    1225             :         _enqueued = YES;
    1226             :         return YES;
    1227             : }
    1228             : 
    1229             : 
    1230             : - (BOOL) enqueued
    1231             : {
    1232             :         return _enqueued;
    1233             : }
    1234             : 
    1235             : 
    1236             : - (void) loadTexture
    1237             : {
    1238             :         // Do nothing.
    1239             : }
    1240             : 
    1241             : 
    1242             : - (void) completeWithData:(void *)data_ width:(unsigned)width_ height:(unsigned)height_
    1243             : {
    1244             :         _data = data_;
    1245             :         _width = width_;
    1246             :         _height = height_;
    1247             :         _format = kOOTextureDataRGBA;
    1248             :         
    1249             :         // Enqueue so superclass can apply texture options and so forth.
    1250             :         [super enqueue];
    1251             :         
    1252             : #if DEBUG_DUMP
    1253             :         NSString *normalName = [NSString stringWithFormat:@"planet-%u-%u-normal-new", _seed.high, _seed.low];
    1254             :         NSString *specularName = [NSString stringWithFormat:@"planet-%u-%u-specular-new", _seed.high, _seed.low];
    1255             :         
    1256             :         [[UNIVERSE gameView] dumpRGBAToRGBFileNamed:normalName
    1257             :                                                            andGrayFileNamed:specularName
    1258             :                                                                                   bytes:_data
    1259             :                                                                                   width:_width
    1260             :                                                                                  height:_height
    1261             :                                                                            rowBytes:_width * 4];
    1262             : #endif
    1263             : }
    1264             : 
    1265             : @end
    1266             : 
    1267             : 
    1268             : @implementation OOPlanetAtmosphereGenerator
    1269             : 
    1270             : - (id) initWithCacheKey:(NSString *)cacheKey seed:(RANROTSeed)seed andParent:(OOPlanetTextureGenerator *)parent
    1271             : {
    1272             :         OOLog(@"texture.planet.generate",@"Initialising atmosphere generator %@",cacheKey);
    1273             :         // AllowCubeMap not used yet but might be in future
    1274             :         if ((self = [super initWithPath:[NSString stringWithFormat:@"OOPlanetAtmoTexture@%p", self] options:kOOTextureAllowCubeMap]))
    1275             :         {
    1276             :                 _cacheKey = [cacheKey copy];
    1277             :                 _seed = seed;
    1278             :                 _parent = [parent retain];
    1279             :                 _enqueued = NO;
    1280             :         }
    1281             :         return self;
    1282             : }
    1283             : 
    1284             : 
    1285             : - (void) dealloc
    1286             : {
    1287             :         DESTROY(_cacheKey);
    1288             :         DESTROY(_parent);
    1289             :         [super dealloc];
    1290             : }
    1291             : 
    1292             : 
    1293             : - (NSString *) cacheKey
    1294             : {
    1295             :         return _cacheKey;
    1296             : }
    1297             : 
    1298             : 
    1299             : - (uint32_t) textureOptions
    1300             : {
    1301             :         return PLANET_TEXTURE_OPTIONS;
    1302             : }
    1303             : 
    1304             : 
    1305             : - (BOOL) enqueue
    1306             : {
    1307             :         _enqueued = YES;
    1308             :         return YES;
    1309             : }
    1310             : 
    1311             : 
    1312             : - (BOOL) enqueued
    1313             : {
    1314             :         return _enqueued;
    1315             : }
    1316             : 
    1317             : 
    1318             : - (void) loadTexture
    1319             : {
    1320             :         // Do nothing.
    1321             : }
    1322             : 
    1323             : 
    1324             : /* Because of the hack to avoid making texture loaders need to know
    1325             :  * about multiple textures, when we call the atmosphere generator on a
    1326             :  * planet with a from-file texture, things can occasionally go wrong
    1327             :  * because this isn't queued until after it's ready, but might be
    1328             :  * requested from the queue before then. So, when getResult is called,
    1329             :  * it waits for its parent task to complete if it's not itself ready -
    1330             :  * by the time the parent task is complete, this texture loader will
    1331             :  * have been enqueued, and [super getResult] will then work
    1332             :  * properly. - CIM 2014-04-05 */
    1333             : - (BOOL) getResult:(OOPixMap *)result
    1334             :                         format:(OOTextureDataFormat *)outFormat
    1335             :          originalWidth:(uint32_t *)outWidth
    1336             :         originalHeight:(uint32_t *)outHeight
    1337             : {
    1338             :         if (!_ready)
    1339             :         {
    1340             :                 [[OOAsyncWorkManager sharedAsyncWorkManager] waitForTaskToComplete:_parent];
    1341             :         }
    1342             : 
    1343             :         return [super getResult:result
    1344             :                                          format:outFormat
    1345             :                           originalWidth:outWidth
    1346             :                          originalHeight:outHeight];
    1347             : }
    1348             : 
    1349             : 
    1350             : - (void) completeWithData:(void *)data_ width:(unsigned)width_ height:(unsigned)height_
    1351             : {
    1352             :         OOLog(@"texture.planet.generate", @"%@", @"Completing atmosphere generator");
    1353             : 
    1354             :         _data = data_;
    1355             :         _width = width_;
    1356             :         _height = height_;
    1357             :         _format = kOOTextureDataRGBA;
    1358             :         
    1359             :         // Enqueue so superclass can apply texture options and so forth.
    1360             :         [super enqueue];
    1361             :         
    1362             : #if DEBUG_DUMP
    1363             :         NSString *rgbName = [NSString stringWithFormat:@"planet-%u-%u-atmosphere-rgb-new", _seed.high, _seed.low];
    1364             :         NSString *alphaName = [NSString stringWithFormat:@"planet-%u-%u-atmosphere-alpha-new", _seed.high, _seed.low];
    1365             :         
    1366             :         [[UNIVERSE gameView] dumpRGBAToRGBFileNamed:rgbName
    1367             :                                                            andGrayFileNamed:alphaName
    1368             :                                                                                   bytes:_data
    1369             :                                                                                   width:_width
    1370             :                                                                                  height:_height
    1371             :                                                                            rowBytes:_width * 4];
    1372             : #endif
    1373             : }
    1374             : 
    1375             : @end
    1376             : 
    1377             : #endif  // NEW_PLANETS

Generated by: LCOV version 1.14