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;
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 : }
430 :
431 : /* The above code generates less than _nebulaCount quads, because i is
432 : incremented once in the outer loop as well as in the inner loop. To
433 : keep skies looking the same, we leave the bug in and fill in the
434 : actual generated count here.
435 : */
436 : _nebulaCount = actualCount;
437 :
438 : [self addQuads:quads count:_nebulaCount];
439 : free(quads);
440 : }
441 :
442 :
443 : - (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count
444 : {
445 : if (_quadSets == nil) _quadSets = [[NSMutableArray alloc] init];
446 :
447 : [OOSkyQuadSet addQuads:quads count:count toArray:_quadSets];
448 : }
449 :
450 :
451 : - (void)loadStarTextures
452 : {
453 : if (sStarTextures == nil)
454 : {
455 : sStarTextures = [[OOProbabilisticTextureManager alloc]
456 : initWithPListName:@"startextures.plist"
457 : options:kOOTextureMinFilterMipMap | kOOTextureMagFilterLinear | kOOTextureAlphaMask
458 : anisotropy:0.0f
459 : lodBias:-0.0f];
460 : if (sStarTextures == nil)
461 : {
462 : [NSException raise:OOLITE_EXCEPTION_DATA_NOT_FOUND format:@"No star textures could be loaded."];
463 : }
464 : }
465 :
466 : [sStarTextures setSeed:RANROTGetFullSeed()];
467 :
468 : }
469 :
470 :
471 : - (void)loadNebulaTextures
472 : {
473 : if (sNebulaTextures == nil)
474 : {
475 : sNebulaTextures = [[OOProbabilisticTextureManager alloc]
476 : initWithPListName:@"nebulatextures.plist"
477 : options:kOOTextureDefaultOptions | kOOTextureAlphaMask
478 : anisotropy:0.0f
479 : lodBias:0.0f];
480 : if (sNebulaTextures == nil)
481 : {
482 : [NSException raise:OOLITE_EXCEPTION_DATA_NOT_FOUND format:@"No nebula textures could be loaded."];
483 : }
484 : }
485 :
486 : [sNebulaTextures setSeed:RANROTGetFullSeed()];
487 :
488 : }
489 :
490 :
491 : - (void)ensureTexturesLoaded
492 : {
493 : [sStarTextures ensureTexturesLoaded];
494 : [sNebulaTextures ensureTexturesLoaded];
495 : }
496 :
497 :
498 0 : - (void)resetGraphicsState
499 : {
500 : OO_ENTER_OPENGL();
501 :
502 : if (_displayListName != 0)
503 : {
504 : glDeleteLists(_displayListName, 1);
505 : _displayListName = 0;
506 : }
507 : }
508 :
509 : @end
510 :
511 :
512 : @implementation OOSkyQuadSet
513 :
514 : + (void)addQuads:(OOSkyQuadDesc *)quads count:(unsigned)count toArray:(NSMutableArray *)ioArray
515 : {
516 : NSMutableSet *seenTextures = nil;
517 : OOTexture *texture = nil;
518 : OOSkyQuadSet *quadSet = nil;
519 : unsigned i;
520 :
521 : // Iterate over all quads.
522 : seenTextures = [NSMutableSet set];
523 : for (i = 0; i != count; ++i)
524 : {
525 : texture = quads[i].texture;
526 :
527 : // If we haven't seen this quad's texture before...
528 : if (![seenTextures containsObject:texture])
529 : {
530 : [seenTextures addObject:texture];
531 :
532 : // ...create a quad set for this texture.
533 : quadSet = [[self alloc] initWithQuadsWithTexture:texture
534 : inArray:quads
535 : count:count];
536 : if (quadSet != nil)
537 : {
538 : [ioArray addObject:quadSet];
539 : [quadSet release];
540 : }
541 : }
542 : }
543 : }
544 :
545 :
546 : - (id)initWithQuadsWithTexture:(OOTexture *)texture inArray:(OOSkyQuadDesc *)array count:(unsigned)totalCount
547 : {
548 : BOOL OK = YES;
549 : unsigned i, j, vertexCount;
550 : GLfloat *pos;
551 : GLfloat *tc;
552 : GLfloat *col;
553 : GLfloat r, g, b, x;
554 : size_t posSize, tcSize, colSize;
555 : unsigned count = 0;
556 : int skyColorCorrection = [[NSUserDefaults standardUserDefaults] oo_integerForKey:@"sky-color-correction" defaultValue:0];
557 :
558 : // Hejl / Burgess-Dawson filmic tone mapping
559 : // this algorithm has gamma correction already embedded
560 0 : #define SKYCOLOR_TONEMAP_COMPONENT(skyColorComponent) \
561 : do { \
562 : x = MAX(0.0, skyColorComponent - 0.004); \
563 : *col++ = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); \
564 : } while (0)
565 :
566 : self = [super init];
567 : if (self == nil) OK = NO;
568 :
569 : if (OK)
570 : {
571 : // Count the quads in the array using this texture.
572 : for (i = 0; i != totalCount; ++i)
573 : {
574 : if (array[i].texture == texture) ++_count;
575 : }
576 : if (_count == 0) OK = NO;
577 : }
578 :
579 : if (OK)
580 : {
581 : // Allocate arrays.
582 : vertexCount = _count * 4;
583 : posSize = sizeof *_positions * vertexCount * kSkyQuadSetPositionEntriesPerVertex;
584 : tcSize = sizeof *_texCoords * vertexCount * kSkyQuadSetTexCoordEntriesPerVertex;
585 : colSize = sizeof *_colors * vertexCount * kSkyQuadSetColorEntriesPerVertex;
586 :
587 : _positions = malloc(posSize);
588 : _texCoords = malloc(tcSize);
589 : _colors = malloc(colSize);
590 :
591 : if (_positions == NULL || _texCoords == NULL || _colors == NULL) OK = NO;
592 :
593 : pos = _positions;
594 : tc = _texCoords;
595 : col = _colors;
596 : }
597 :
598 : if (OK)
599 : {
600 : // Find the matching quads again, and convert them to renderable representation.
601 : for (i = 0; i != totalCount; ++i)
602 : {
603 : if (array[i].texture == texture)
604 : {
605 : r = [array[i].color redComponent];
606 : g = [array[i].color greenComponent];
607 : b = [array[i].color blueComponent];
608 :
609 : // Loop over vertices
610 : for (j = 0; j != 4; ++j)
611 : {
612 : *pos++ = array[i].corners[j].x;
613 : *pos++ = array[i].corners[j].y;
614 : *pos++ = array[i].corners[j].z;
615 :
616 : // Colour is the same for each vertex
617 : if (skyColorCorrection == 0) // no color correction
618 : {
619 : *col++ = r;
620 : *col++ = g;
621 : *col++ = b;
622 : }
623 : else if (skyColorCorrection == 1) // gamma correction only
624 : {
625 : *col++ = pow(r, 1.0/2.2);
626 : *col++ = pow(g, 1.0/2.2);
627 : *col++ = pow(b, 1.0/2.2);
628 : }
629 : else // gamma correctioin + filmic tone mapping
630 : {
631 : SKYCOLOR_TONEMAP_COMPONENT(r);
632 : SKYCOLOR_TONEMAP_COMPONENT(g);
633 : SKYCOLOR_TONEMAP_COMPONENT(b);
634 : }
635 : *col++ = 1.0f; // Alpha is unused but needs to be there
636 : }
637 :
638 : // Texture co-ordinates are the same for each quad.
639 : *tc++ = sMinTexCoord;
640 : *tc++ = sMinTexCoord;
641 :
642 : *tc++ = sMaxTexCoord;
643 : *tc++ = sMinTexCoord;
644 :
645 : *tc++ = sMaxTexCoord;
646 : *tc++ = sMaxTexCoord;
647 :
648 : *tc++ = sMinTexCoord;
649 : *tc++ = sMaxTexCoord;
650 :
651 : count++;
652 : }
653 : }
654 :
655 : _texture = [texture retain];
656 : OOLog(@"sky.setup", @"Generated quadset with %u quads for texture %@", count, _texture);
657 : }
658 :
659 : if (!OK)
660 : {
661 : [self release];
662 : self = nil;
663 : }
664 :
665 : return self;
666 : }
667 :
668 :
669 0 : - (void)dealloc
670 : {
671 : [_texture release];
672 :
673 : if (_positions != NULL) free(_positions);
674 : if (_texCoords != NULL) free(_texCoords);
675 : if (_colors != NULL) free(_colors);
676 :
677 : [super dealloc];
678 : }
679 :
680 :
681 0 : - (NSString *)description
682 : {
683 : return [NSString stringWithFormat:@"<%@ %p>{%u quads, texture: %@}", [self class], self, _count, _texture];
684 : }
685 :
686 :
687 : - (void)render
688 : {
689 : OO_ENTER_OPENGL();
690 :
691 : [_texture apply];
692 :
693 : OOGL(glVertexPointer(kSkyQuadSetPositionEntriesPerVertex, GL_FLOAT, 0, _positions));
694 : OOGL(glTexCoordPointer(kSkyQuadSetTexCoordEntriesPerVertex, GL_FLOAT, 0, _texCoords));
695 : OOGL(glColorPointer(kSkyQuadSetColorEntriesPerVertex, GL_FLOAT, 0, _colors));
696 :
697 : OOGL(glDrawArrays(GL_QUADS, 0, 4 * _count));
698 : }
699 :
700 :
701 : #ifndef NDEBUG
702 : - (size_t) totalSize
703 : {
704 : return [self oo_objectSize] + _count * 4 * (sizeof *_positions + sizeof *_texCoords + sizeof *_colors);
705 : }
706 :
707 :
708 : - (OOTexture *) texture
709 : {
710 : return _texture;
711 : }
712 : #endif
713 :
714 : @end
715 :
716 :
717 0 : static OOColor *SaturatedColorInRange(OOColor *color1, OOColor *color2, BOOL hueFix)
718 : {
719 : OOColor *color = nil;
720 : float hue, saturation, brightness, alpha;
721 :
722 : color = [color1 blendedColorWithFraction:randf() ofColor:color2];
723 : [color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];
724 :
725 : saturation = 0.5 * saturation + 0.5; // move saturation up a notch!
726 :
727 : /* NOTE: this changes the hue, because getHue:... produces hue values
728 : in [0, 360], but colorWithCalibratedHue:... takes hue values in
729 : [0, 1].
730 : */
731 : if (hueFix)
732 : {
733 : /* now nebula colours can be independently set, correct the
734 : * range so they behave expectedly if they have actually been
735 : * set in planetinfo */
736 : hue /= 360.0;
737 : }
738 : /* else keep it how it was before so the nebula hues are clearer */
739 :
740 : return [OOColor colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
741 : }
|