Line data Source code
1 0 : /*
2 :
3 : OOSkyDrawable.m
4 :
5 :
6 : Copyright (C) 2007-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #import "OOSkyDrawable.h"
29 : #import "ResourceManager.h"
30 : #import "OOTexture.h"
31 : #import "GameController.h"
32 : #import "OOColor.h"
33 : #import "OOProbabilisticTextureManager.h"
34 : #import "OOGraphicsResetManager.h"
35 : #import "Universe.h"
36 : #import "OOMacroOpenGL.h"
37 : #import "NSObjectOOExtensions.h"
38 : #import "OOCollectionExtractors.h"
39 :
40 :
41 0 : #define SKY_ELEMENT_SCALE_FACTOR (BILLBOARD_DEPTH / 500.0f)
42 0 : #define NEBULA_SHUFFLE_FACTOR 0.005f
43 0 : #define DEBUG_COLORS 0 // If set, rgb = xyz (offset to range from 0.1 to 1.0).
44 :
45 :
46 : /* Min and max coords are 0 and 1 normally, but the default
47 : sky-render-inset-coords can be used to modify them slightly as an attempted
48 : work-around for artefacts on buggy S3/Via renderers.
49 : */
50 0 : static float sMinTexCoord = 0.0f, sMaxTexCoord = 1.0f;
51 0 : static BOOL sInited = NO;
52 :
53 :
54 : /* Struct used to describe quads initially. This form is optimized for
55 : reasoning about.
56 : */
57 0 : typedef struct OOSkyQuadDesc
58 : {
59 0 : Vector corners[4];
60 0 : OOColor *color;
61 0 : OOTexture *texture;
62 0 : } OOSkyQuadDesc;
63 :
64 :
65 0 : enum
66 : {
67 : kSkyQuadSetPositionEntriesPerVertex = 3,
68 : kSkyQuadSetTexCoordEntriesPerVertex = 2,
69 : kSkyQuadSetColorEntriesPerVertex = 4
70 : };
71 :
72 :
73 : /* Class containing a set of quads with the same texture. This form is
74 : optimized for rendering.
75 : */
76 0 : @interface OOSkyQuadSet: NSObject
77 : {
78 : @private
79 0 : OOTexture *_texture;
80 0 : unsigned _count;
81 0 : GLfloat *_positions; // 3 entries per vertex, 12 per quad
82 0 : GLfloat *_texCoords; // 2 entries per vertex, 8 per quad
83 0 : GLfloat *_colors; // 4 entries per vertex, 16 per quad
84 : }
85 :
86 0 : + (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count toArray:(NSMutableArray *)ioArray;
87 :
88 0 : - (id)initWithQuadsWithTexture:(OOTexture *)texture inArray:(OOSkyQuadDesc *)array count:(unsigned)totalCount;
89 :
90 0 : - (void)render;
91 :
92 : #ifndef NDEBUG
93 0 : - (size_t) totalSize;
94 0 : - (OOTexture *) texture;
95 : #endif
96 :
97 : @end
98 :
99 :
100 : /* Textures are global because there is always a sky, but the sky isn't
101 : replaced very often, so the textures are likely to fall out of the cache.
102 : */
103 0 : static OOProbabilisticTextureManager *sStarTextures;
104 0 : static OOProbabilisticTextureManager *sNebulaTextures;
105 :
106 :
107 : static OOColor *SaturatedColorInRange(OOColor *color1, OOColor *color2, BOOL hueFix);
108 :
109 :
110 : @interface OOSkyDrawable (OOPrivate) <OOGraphicsResetClient>
111 :
112 0 : - (void)setUpStarsWithColor1:(OOColor *)color1 color2:(OOColor *)color2;
113 0 : - (void)setUpNebulaeWithColor1:(OOColor *)color1
114 : color2:(OOColor *)color2
115 : clusterFactor:(float)nebulaClusterFactor
116 : nebulaHueFix:(BOOL)nebulaHueFix
117 : alpha:(float)nebulaAlpha
118 : scale:(float)nebulaScale;
119 :
120 :
121 0 : - (void)loadStarTextures;
122 0 : - (void)loadNebulaTextures;
123 :
124 0 : - (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count;
125 :
126 0 : - (void)ensureTexturesLoaded;
127 :
128 : @end
129 :
130 :
131 : @implementation OOSkyDrawable
132 :
133 : - (id)initWithColor1:(OOColor *)color1
134 : Color2:(OOColor *)color2
135 : Color3:(OOColor *)color3
136 : Color4:(OOColor *)color4
137 : starCount:(unsigned)starCount
138 : nebulaCount:(unsigned)nebulaCount
139 : nebulaHueFix:(BOOL)nebulaHueFix
140 : clusterFactor:(float)nebulaClusterFactor
141 : alpha:(float)nebulaAlpha
142 : scale:(float)nebulaScale
143 : {
144 : NSAutoreleasePool *pool = nil;
145 :
146 : if (!sInited)
147 : {
148 : sInited = YES;
149 : if ([[NSUserDefaults standardUserDefaults] boolForKey:@"sky-render-inset-coords"])
150 : {
151 : sMinTexCoord += 1.0f/128.0f;
152 : sMaxTexCoord -= 1.0f/128.0f;
153 : }
154 : }
155 :
156 : self = [super init];
157 : if (self == nil) return nil;
158 :
159 : _starCount = starCount;
160 : _nebulaCount = nebulaCount;
161 :
162 : pool = [[NSAutoreleasePool alloc] init];
163 : [self setUpStarsWithColor1:color1 color2:color2];
164 :
165 : if (![UNIVERSE reducedDetail])
166 : {
167 : [self setUpNebulaeWithColor1:color3
168 : color2:color4
169 : clusterFactor:nebulaClusterFactor
170 : nebulaHueFix:nebulaHueFix
171 : alpha:nebulaAlpha
172 : scale:nebulaScale];
173 : }
174 : [pool release];
175 :
176 : [[OOGraphicsResetManager sharedManager] registerClient:self];
177 :
178 : return self;
179 : }
180 :
181 :
182 0 : - (void)dealloc
183 : {
184 : OO_ENTER_OPENGL();
185 :
186 : [_quadSets release];
187 : [[OOGraphicsResetManager sharedManager] unregisterClient:self];
188 : if (_displayListName != 0) glDeleteLists(_displayListName, 1);
189 :
190 : [super dealloc];
191 : }
192 :
193 :
194 0 : - (void)renderOpaqueParts
195 : {
196 : // While technically translucent, the sky doesn't need to be depth-sorted
197 : // since it'll be behind everything else anyway.
198 :
199 : OO_ENTER_OPENGL();
200 : OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
201 :
202 : OOGL(glDisable(GL_DEPTH_TEST)); // don't read the depth buffer
203 : OOGL(glEnable(GL_TEXTURE_2D));
204 :
205 : // Make stars dim in atmosphere. Note: works OK on night side because sky is dark blue, not black.
206 : GLfloat fogColor[4] = {0.02, 0.02, 0.02, 1.0};
207 : OOGL(glFogfv(GL_FOG_COLOR, fogColor));
208 :
209 : if (_displayListName != 0)
210 : {
211 : OOGL(glCallList(_displayListName));
212 : }
213 : else
214 : {
215 : // Set up display list
216 : [self ensureTexturesLoaded];
217 : _displayListName = glGenLists(1);
218 :
219 : OOGL(glNewList(_displayListName, GL_COMPILE));
220 :
221 : OOGL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
222 : OOGL(glEnableClientState(GL_COLOR_ARRAY));
223 :
224 : [_quadSets makeObjectsPerformSelector:@selector(render)];
225 :
226 : OOGL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
227 : OOGL(glDisableClientState(GL_COLOR_ARRAY));
228 :
229 : OOGL(glEndList());
230 : }
231 :
232 : // Restore state
233 : OOGL(glEnable(GL_DEPTH_TEST));
234 : OOGL(glDisable(GL_TEXTURE_2D));
235 :
236 : // Resetting fog is draw loop's responsibility.
237 :
238 : OOVerifyOpenGLState();
239 : OOCheckOpenGLErrors(@"OOSkyDrawable after rendering");
240 : }
241 :
242 :
243 0 : - (BOOL)hasOpaqueParts
244 : {
245 : return YES;
246 : }
247 :
248 :
249 0 : - (GLfloat)maxDrawDistance
250 : {
251 : return INFINITY;
252 : }
253 :
254 : #ifndef NDEBUG
255 0 : - (NSSet *) allTextures
256 : {
257 : NSMutableSet *result = [NSMutableSet setWithCapacity:[_quadSets count]];
258 :
259 : NSEnumerator *quadSetEnum = nil;
260 : OOSkyQuadSet *quadSet = nil;
261 : for (quadSetEnum = [_quadSets objectEnumerator]; (quadSet = [quadSetEnum nextObject]); )
262 : {
263 : [result addObject:[quadSet texture]];
264 : }
265 :
266 : return result;
267 : }
268 :
269 :
270 0 : - (size_t) totalSize
271 : {
272 : size_t result = [super totalSize];
273 :
274 : NSEnumerator *quadSetEnum = nil;
275 : OOSkyQuadSet *quadSet = nil;
276 : for (quadSetEnum = [_quadSets objectEnumerator]; (quadSet = [quadSetEnum nextObject]); )
277 : {
278 : result += [quadSet totalSize];
279 : }
280 :
281 : return result;
282 : }
283 : #endif
284 :
285 : @end
286 :
287 :
288 : #if DEBUG_COLORS
289 : static OOColor *DebugColor(Vector orientation)
290 : {
291 : Vector color = vector_add(make_vector(0.55, 0.55, 0.55), vector_multiply_scalar(vector_normal(orientation), 0.45));
292 : return [OOColor colorWithCalibratedRed:color.x green:color.y blue:color.z alpha:1.0];
293 : }
294 : #endif
295 :
296 :
297 : @implementation OOSkyDrawable (OOPrivate)
298 :
299 : - (void)setUpStarsWithColor1:(OOColor *)color1 color2:(OOColor *)color2
300 : {
301 : OOSkyQuadDesc *quads = NULL, *currQuad = NULL;
302 : unsigned i;
303 : Quaternion q;
304 : Vector vi, vj, vk;
305 : float size;
306 : Vector middle, offset;
307 :
308 : [self loadStarTextures];
309 :
310 : quads = malloc(sizeof *quads * _starCount);
311 : if (quads == NULL) return;
312 :
313 : currQuad = quads;
314 : for (i = 0; i != _starCount; ++i)
315 : {
316 : // Select a direction and rotation.
317 : q = OORandomQuaternion();
318 : basis_vectors_from_quaternion(q, &vi, &vj, &vk);
319 :
320 : // Select colour and texture.
321 : #if DEBUG_COLORS
322 : currQuad->color = DebugColor(vk);
323 : #else
324 : currQuad->color = [color1 blendedColorWithFraction:randf() ofColor:color2];
325 : #endif
326 : currQuad->texture = [sStarTextures selectTexture]; // Not retained, since sStarTextures is never released.
327 :
328 : // Select scale; calculate centre position and offset to first corner.
329 : size = (1 + (ranrot_rand() % 6)) * SKY_ELEMENT_SCALE_FACTOR;
330 : middle = vector_multiply_scalar(vk, BILLBOARD_DEPTH);
331 : offset = vector_multiply_scalar(vector_add(vi, vj), 0.5f * size);
332 :
333 : // Scale the "side" vectors.
334 : Vector vj2 = vector_multiply_scalar(vj, size);
335 : Vector vi2 = vector_multiply_scalar(vi, size);
336 :
337 : // Set up corners.
338 : currQuad->corners[0] = vector_subtract(middle, offset);
339 : currQuad->corners[1] = vector_add(currQuad->corners[0], vj2);
340 : currQuad->corners[2] = vector_add(currQuad->corners[1], vi2);
341 : currQuad->corners[3] = vector_add(currQuad->corners[0], vi2);
342 :
343 : ++currQuad;
344 : }
345 :
346 : [self addQuads:quads count:_starCount];
347 : free(quads);
348 : }
349 :
350 :
351 : - (void)setUpNebulaeWithColor1:(OOColor *)color1
352 : color2:(OOColor *)color2
353 : clusterFactor:(float)nebulaClusterFactor
354 : nebulaHueFix:(BOOL)nebulaHueFix
355 : alpha:(float)nebulaAlpha
356 : scale:(float)nebulaScale
357 : {
358 : OOSkyQuadDesc *quads = NULL, *currQuad = NULL;
359 : unsigned i, actualCount = 0, clusters = 0;
360 : OOColor *color;
361 : Quaternion q;
362 : Vector vi, vj, vk;
363 : double size, r2;
364 : Vector middle, offset;
365 : int r1;
366 :
367 : [self loadNebulaTextures];
368 :
369 : quads = malloc(sizeof *quads * _nebulaCount);
370 : if (quads == NULL) return;
371 :
372 : currQuad = quads;
373 : for (i = 0; i < _nebulaCount; ++i)
374 : {
375 : color = SaturatedColorInRange(color1, color2, nebulaHueFix);
376 :
377 : // Select a direction and rotation.
378 : q = OORandomQuaternion();
379 :
380 : // Create a cluster of nebula quads.
381 : while ((i < _nebulaCount) && (randf() < nebulaClusterFactor))
382 : {
383 : // Select size.
384 : r1 = 1 + (ranrot_rand() & 15);
385 : size = nebulaScale * r1 * SKY_ELEMENT_SCALE_FACTOR;
386 :
387 : // Calculate centre position and offset to first corner.
388 : basis_vectors_from_quaternion(q, &vi, &vj, &vk);
389 :
390 : // Select colour and texture. Smaller nebula quads are dimmer.
391 : #if DEBUG_COLORS
392 : currQuad->color = DebugColor(vk);
393 : #else
394 : currQuad->color = [color colorWithBrightnessFactor:nebulaAlpha * (0.5f + (float)r1 / 32.0f)];
395 : #endif
396 : currQuad->texture = [sNebulaTextures selectTexture]; // Not retained, since sStarTextures is never released.
397 :
398 : middle = vector_multiply_scalar(vk, BILLBOARD_DEPTH);
399 : offset = vector_multiply_scalar(vector_add(vi, vj), 0.5f * size);
400 :
401 : // Rotate vi and vj by a random angle
402 : r2 = randf() * M_PI * 2.0;
403 : quaternion_rotate_about_axis(&q, vk, r2);
404 : vi = vector_right_from_quaternion(q);
405 : vj = vector_up_from_quaternion(q);
406 :
407 : // Scale the "side" vectors.
408 : vj = vector_multiply_scalar(vj, size);
409 : vi = vector_multiply_scalar(vi, size);
410 :
411 : // Set up corners.
412 : currQuad->corners[0] = vector_subtract(middle, offset);
413 : currQuad->corners[1] = vector_add(currQuad->corners[0], vj);
414 : currQuad->corners[2] = vector_add(currQuad->corners[1], vi);
415 : currQuad->corners[3] = vector_add(currQuad->corners[0], vi);
416 :
417 : // Shuffle direction quat around a bit to spread the cluster out.
418 : size = NEBULA_SHUFFLE_FACTOR / (nebulaScale * SKY_ELEMENT_SCALE_FACTOR);
419 : q.x += size * (randf() - 0.5);
420 : q.y += size * (randf() - 0.5);
421 : q.z += size * (randf() - 0.5);
422 : q.w += size * (randf() - 0.5);
423 : quaternion_normalize(&q);
424 :
425 : ++i;
426 : ++currQuad;
427 : ++actualCount;
428 : }
429 : ++clusters;
430 : }
431 :
432 : /* The above code generates less than _nebulaCount quads, because i is
433 : incremented once in the outer loop as well as in the inner loop. To
434 : keep skies looking the same, we leave the bug in and fill in the
435 : actual generated count here.
436 : */
437 : _nebulaCount = actualCount;
438 :
439 : [self addQuads:quads count:_nebulaCount];
440 : free(quads);
441 : }
442 :
443 :
444 : - (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count
445 : {
446 : if (_quadSets == nil) _quadSets = [[NSMutableArray alloc] init];
447 :
448 : [OOSkyQuadSet addQuads:quads count:count toArray:_quadSets];
449 : }
450 :
451 :
452 : - (void)loadStarTextures
453 : {
454 : if (sStarTextures == nil)
455 : {
456 : sStarTextures = [[OOProbabilisticTextureManager alloc]
457 : initWithPListName:@"startextures.plist"
458 : options:kOOTextureMinFilterMipMap | kOOTextureMagFilterLinear | kOOTextureAlphaMask
459 : anisotropy:0.0f
460 : lodBias:-0.0f];
461 : if (sStarTextures == nil)
462 : {
463 : [NSException raise:OOLITE_EXCEPTION_DATA_NOT_FOUND format:@"No star textures could be loaded."];
464 : }
465 : }
466 :
467 : [sStarTextures setSeed:RANROTGetFullSeed()];
468 :
469 : }
470 :
471 :
472 : - (void)loadNebulaTextures
473 : {
474 : if (sNebulaTextures == nil)
475 : {
476 : sNebulaTextures = [[OOProbabilisticTextureManager alloc]
477 : initWithPListName:@"nebulatextures.plist"
478 : options:kOOTextureDefaultOptions | kOOTextureAlphaMask
479 : anisotropy:0.0f
480 : lodBias:0.0f];
481 : if (sNebulaTextures == nil)
482 : {
483 : [NSException raise:OOLITE_EXCEPTION_DATA_NOT_FOUND format:@"No nebula textures could be loaded."];
484 : }
485 : }
486 :
487 : [sNebulaTextures setSeed:RANROTGetFullSeed()];
488 :
489 : }
490 :
491 :
492 : - (void)ensureTexturesLoaded
493 : {
494 : [sStarTextures ensureTexturesLoaded];
495 : [sNebulaTextures ensureTexturesLoaded];
496 : }
497 :
498 :
499 0 : - (void)resetGraphicsState
500 : {
501 : OO_ENTER_OPENGL();
502 :
503 : if (_displayListName != 0)
504 : {
505 : glDeleteLists(_displayListName, 1);
506 : _displayListName = 0;
507 : }
508 : }
509 :
510 : @end
511 :
512 :
513 : @implementation OOSkyQuadSet
514 :
515 : + (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count toArray:(NSMutableArray *)ioArray
516 : {
517 : NSMutableSet *seenTextures = nil;
518 : OOTexture *texture = nil;
519 : OOSkyQuadSet *quadSet = nil;
520 : unsigned i;
521 :
522 : // Iterate over all quads.
523 : seenTextures = [NSMutableSet set];
524 : for (i = 0; i != count; ++i)
525 : {
526 : texture = quads[i].texture;
527 :
528 : // If we haven't seen this quad's texture before...
529 : if (![seenTextures containsObject:texture])
530 : {
531 : [seenTextures addObject:texture];
532 :
533 : // ...create a quad set for this texture.
534 : quadSet = [[self alloc] initWithQuadsWithTexture:texture
535 : inArray:quads
536 : count:count];
537 : if (quadSet != nil)
538 : {
539 : [ioArray addObject:quadSet];
540 : [quadSet release];
541 : }
542 : }
543 : }
544 : }
545 :
546 :
547 : - (id)initWithQuadsWithTexture:(OOTexture *)texture inArray:(OOSkyQuadDesc *)array count:(unsigned)totalCount
548 : {
549 : BOOL OK = YES;
550 : unsigned i, j, vertexCount;
551 : GLfloat *pos;
552 : GLfloat *tc;
553 : GLfloat *col;
554 : GLfloat r, g, b, x;
555 : size_t posSize, tcSize, colSize;
556 : unsigned count = 0;
557 : int skyColorCorrection = [[NSUserDefaults standardUserDefaults] oo_integerForKey:@"sky-color-correction" defaultValue:0];
558 :
559 : // Hejl / Burgess-Dawson filmic tone mapping
560 : // this algorithm has gamma correction already embedded
561 0 : #define SKYCOLOR_TONEMAP_COMPONENT(skyColorComponent) \
562 : do { \
563 : x = MAX(0.0, skyColorComponent - 0.004); \
564 : *col++ = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); \
565 : } while (0)
566 :
567 : self = [super init];
568 : if (self == nil) OK = NO;
569 :
570 : if (OK)
571 : {
572 : // Count the quads in the array using this texture.
573 : for (i = 0; i != totalCount; ++i)
574 : {
575 : if (array[i].texture == texture) ++_count;
576 : }
577 : if (_count == 0) OK = NO;
578 : }
579 :
580 : if (OK)
581 : {
582 : // Allocate arrays.
583 : vertexCount = _count * 4;
584 : posSize = sizeof *_positions * vertexCount * kSkyQuadSetPositionEntriesPerVertex;
585 : tcSize = sizeof *_texCoords * vertexCount * kSkyQuadSetTexCoordEntriesPerVertex;
586 : colSize = sizeof *_colors * vertexCount * kSkyQuadSetColorEntriesPerVertex;
587 :
588 : _positions = malloc(posSize);
589 : _texCoords = malloc(tcSize);
590 : _colors = malloc(colSize);
591 :
592 : if (_positions == NULL || _texCoords == NULL || _colors == NULL) OK = NO;
593 :
594 : pos = _positions;
595 : tc = _texCoords;
596 : col = _colors;
597 : }
598 :
599 : if (OK)
600 : {
601 : // Find the matching quads again, and convert them to renderable representation.
602 : for (i = 0; i != totalCount; ++i)
603 : {
604 : if (array[i].texture == texture)
605 : {
606 : r = [array[i].color redComponent];
607 : g = [array[i].color greenComponent];
608 : b = [array[i].color blueComponent];
609 :
610 : // Loop over vertices
611 : for (j = 0; j != 4; ++j)
612 : {
613 : *pos++ = array[i].corners[j].x;
614 : *pos++ = array[i].corners[j].y;
615 : *pos++ = array[i].corners[j].z;
616 :
617 : // Colour is the same for each vertex
618 : if (skyColorCorrection == 0) // no color correction
619 : {
620 : *col++ = r;
621 : *col++ = g;
622 : *col++ = b;
623 : }
624 : else if (skyColorCorrection == 1) // gamma correction only
625 : {
626 : *col++ = pow(r, 1.0/2.2);
627 : *col++ = pow(g, 1.0/2.2);
628 : *col++ = pow(b, 1.0/2.2);
629 : }
630 : else // gamma correctioin + filmic tone mapping
631 : {
632 : SKYCOLOR_TONEMAP_COMPONENT(r);
633 : SKYCOLOR_TONEMAP_COMPONENT(g);
634 : SKYCOLOR_TONEMAP_COMPONENT(b);
635 : }
636 : *col++ = 1.0f; // Alpha is unused but needs to be there
637 : }
638 :
639 : // Texture co-ordinates are the same for each quad.
640 : *tc++ = sMinTexCoord;
641 : *tc++ = sMinTexCoord;
642 :
643 : *tc++ = sMaxTexCoord;
644 : *tc++ = sMinTexCoord;
645 :
646 : *tc++ = sMaxTexCoord;
647 : *tc++ = sMaxTexCoord;
648 :
649 : *tc++ = sMinTexCoord;
650 : *tc++ = sMaxTexCoord;
651 :
652 : count++;
653 : }
654 : }
655 :
656 : _texture = [texture retain];
657 : OOLog(@"sky.setup", @"Generated quadset with %u quads for texture %@", count, _texture);
658 : }
659 :
660 : if (!OK)
661 : {
662 : [self release];
663 : self = nil;
664 : }
665 :
666 : return self;
667 : }
668 :
669 :
670 0 : - (void)dealloc
671 : {
672 : [_texture release];
673 :
674 : if (_positions != NULL) free(_positions);
675 : if (_texCoords != NULL) free(_texCoords);
676 : if (_colors != NULL) free(_colors);
677 :
678 : [super dealloc];
679 : }
680 :
681 :
682 0 : - (NSString *)description
683 : {
684 : return [NSString stringWithFormat:@"<%@ %p>{%u quads, texture: %@}", [self class], self, _count, _texture];
685 : }
686 :
687 :
688 : - (void)render
689 : {
690 : OO_ENTER_OPENGL();
691 :
692 : [_texture apply];
693 :
694 : OOGL(glVertexPointer(kSkyQuadSetPositionEntriesPerVertex, GL_FLOAT, 0, _positions));
695 : OOGL(glTexCoordPointer(kSkyQuadSetTexCoordEntriesPerVertex, GL_FLOAT, 0, _texCoords));
696 : OOGL(glColorPointer(kSkyQuadSetColorEntriesPerVertex, GL_FLOAT, 0, _colors));
697 :
698 : OOGL(glDrawArrays(GL_QUADS, 0, 4 * _count));
699 : }
700 :
701 :
702 : #ifndef NDEBUG
703 : - (size_t) totalSize
704 : {
705 : return [self oo_objectSize] + _count * 4 * (sizeof *_positions + sizeof *_texCoords + sizeof *_colors);
706 : }
707 :
708 :
709 : - (OOTexture *) texture
710 : {
711 : return _texture;
712 : }
713 : #endif
714 :
715 : @end
716 :
717 :
718 0 : static OOColor *SaturatedColorInRange(OOColor *color1, OOColor *color2, BOOL hueFix)
719 : {
720 : OOColor *color = nil;
721 : float hue, saturation, brightness, alpha;
722 :
723 : color = [color1 blendedColorWithFraction:randf() ofColor:color2];
724 : [color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
725 :
726 : saturation = 0.5 * saturation + 0.5; // move saturation up a notch!
727 :
728 : /* NOTE: this changes the hue, because getHue:... produces hue values
729 : in [0, 360], but colorWithCalibratedHue:... takes hue values in
730 : [0, 1].
731 : */
732 : if (hueFix)
733 : {
734 : /* now nebula colours can be independently set, correct the
735 : * range so they behave expectedly if they have actually been
736 : * set in planetinfo */
737 : hue /= 360.0;
738 : }
739 : /* else keep it how it was before so the nebula hues are clearer */
740 :
741 : return [OOColor colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
742 : }
|