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
|