Line data Source code
1 0 : /*
2 :
3 : OOSunEntity.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOSunEntity.h"
26 : #import "OOOpenGLExtensionManager.h"
27 : #import "OOMacroOpenGL.h"
28 :
29 : #import "Universe.h"
30 : #import "AI.h"
31 : #import "MyOpenGLView.h"
32 : #import "ShipEntityAI.h"
33 : #import "OOColor.h"
34 : #import "OOCharacter.h"
35 : #import "OOStringParsing.h"
36 : #import "PlayerEntity.h"
37 : #import "OOCollectionExtractors.h"
38 : #import "OODebugFlags.h"
39 : #import "OOStringExpander.h"
40 :
41 : @interface OOSunEntity (Private)
42 :
43 0 : - (void) calculateGLArrays:(GLfloat)inner_radius width:(GLfloat)width zDistance:(GLfloat)z_distance;
44 0 : - (void) drawOpaqueParts;
45 0 : - (void) drawTranslucentParts;
46 :
47 : @end
48 :
49 :
50 : @implementation OOSunEntity
51 :
52 : #ifndef NDEBUG
53 0 : - (id) init
54 : {
55 : assert(0);
56 : return nil;
57 : }
58 : #endif
59 :
60 :
61 : - (BOOL) setSunColor:(OOColor*)sun_color
62 : {
63 : if (sun_color == nil) return NO;
64 :
65 : OO_ENTER_OPENGL();
66 :
67 : float hue, sat, bri, alf;
68 : OOColor *color = nil;
69 :
70 : // blend some white into the sun color to brighten it up
71 : sun_color = [sun_color blendedColorWithFraction:0.3 ofColor:[OOColor whiteColor]];
72 :
73 : [sun_color getHue:&hue saturation:&sat brightness:&bri alpha:&alf];
74 : hue /= 360;
75 :
76 : /*
77 : // FIXME: do away with hue_drift altogether?
78 : // The following two lines are functionally identical to 1.73:
79 : randf();randf(); // avoid ranrot dirft!
80 : float hue_drift = 0.0f;
81 : */
82 :
83 : // anything more than a minimal hue drift will wipe out the original colour.
84 : float hue_drift = 0.0038f * fabsf(randf() - randf());
85 :
86 : // set the lighting color for the sun
87 : GLfloat r,g,b,a;
88 : [sun_color getRed:&r green:&g blue:&b alpha:&a];
89 :
90 : GLfloat sun_ambient[] = { 0.0, 0.0, 0.0, 1.0}; // real ambient light inside gl_LightModel.ambient
91 : sun_diffuse[0] = 0.5f * (1.0f + r); // paler
92 : sun_diffuse[1] = 0.5f * (1.0f + g); // paler
93 : sun_diffuse[2] = 0.5f * (1.0f + b); // paler
94 : sun_diffuse[3] = 1.0;
95 : sun_specular[0] = r;
96 : sun_specular[1] = g;
97 : sun_specular[2] = b;
98 : sun_specular[3] = 1.0;
99 :
100 : OOGL(glLightfv(GL_LIGHT1, GL_AMBIENT, sun_ambient));
101 : OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_diffuse));
102 : OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, sun_specular));
103 :
104 : // main disc less saturation (partially taken care of by the ACES tonemapper) more brightness
105 : color = [OOColor colorWithHue:hue saturation:sat * 0.75f brightness:1.0f alpha:1.0f];
106 : // our OpenGL color values are unclamped, so we can multiply the color components by
107 : // any value we want, in order to make the sun a truly bright object in the sky
108 : color = [OOColor colorWithRed:[color redComponent] * _sunBrightnessFactor green:[color greenComponent] * _sunBrightnessFactor blue:[color blueComponent] * _sunBrightnessFactor alpha:[color alphaComponent]];
109 : [color getRed:&discColor[0] green:&discColor[1] blue:&discColor[2] alpha:&discColor[3]];
110 :
111 : /* Two inner corona layers with low alpha and saturation are additively
112 : blended with main corona. This produces something vaguely like a bloom
113 : effect.
114 : */
115 : hue += hue_drift * 3;
116 : // saturation = 1 would shift white to red
117 : color = [OOColor colorWithHue:hue saturation:OOClamp_0_1_f(sat*1.0f) brightness:bri * 0.75f alpha:0.45f];
118 : color = [OOColor colorWithRed:[color redComponent] * _sunBrightnessFactor green:[color greenComponent] * _sunBrightnessFactor blue:[color blueComponent] * _sunBrightnessFactor alpha:[color alphaComponent]];
119 : [color getRed:&outerCoronaColor[0] green:&outerCoronaColor[1] blue:&outerCoronaColor[2] alpha:&outerCoronaColor[3]];
120 :
121 : return YES;
122 : }
123 :
124 :
125 : - (id) initSunWithColor:(OOColor *)sun_color andDictionary:(NSDictionary *) dict
126 : {
127 : int i;
128 :
129 : self = [super init];
130 :
131 : collision_radius = 100000.0; // 100km across
132 :
133 : scanClass = CLASS_NO_DRAW;
134 :
135 : _sunBrightnessFactor = [[NSUserDefaults standardUserDefaults] oo_floatForKey:@"sbf" defaultValue:80.0f];
136 : _sunCoronaAlphaFactor = [[NSUserDefaults standardUserDefaults] oo_floatForKey:@"scaf" defaultValue:0.005f];
137 :
138 : [self setSunColor:sun_color];
139 :
140 : [self setName:OOExpand([dict oo_stringForKey:KEY_SUNNAME defaultValue:@"[oolite-default-star-name]"])];
141 :
142 : corona_blending=OOClamp_0_1_f([dict oo_floatForKey:@"corona_hues" defaultValue:1.0f]);
143 : corona_speed_factor=[dict oo_floatForKey:@"corona_shimmer" defaultValue:-1.0];
144 : if(corona_speed_factor<0)
145 : {
146 : // from .22222 to 2
147 : corona_speed_factor = 1.0 / (0.5 + 2.0 * (randf() + randf()));
148 : }
149 : else
150 : {
151 : //on average: 0 = .25 , 1 = 2.25 - the same sun should give the same random component
152 : corona_speed_factor=OOClamp_0_1_f(corona_speed_factor) * 2.0 + randf() * randf();
153 : }
154 : #ifdef OO_DUMP_PLANETINFO
155 : OOLog(@"planetinfo.record",@"corona_shimmer = %f",corona_speed_factor);
156 : #endif
157 :
158 : corona_stage = 0.0;
159 : for (i = 0; i < SUN_CORONA_SAMPLES; i++)
160 : rvalue[i] = randf();
161 :
162 : // set up the radius properties
163 : [self changeSunProperty:@"sun_radius" withDictionary:dict];
164 :
165 : unsigned k = 0;
166 : for (unsigned i=0 ; i < 360 ; i++)
167 : {
168 : unsigned j = (i+1)%360;
169 : // disc
170 : sunTriangles[k++] = 0;
171 : sunTriangles[k++] = 1+i;
172 : sunTriangles[k++] = 1+j;
173 : }
174 : for (unsigned i=0 ; i < 360 ; i++)
175 : {
176 : unsigned j = (i+1)%360;
177 : // ring 1
178 : sunTriangles[k++] = 1+i;
179 : sunTriangles[k++] = 1+j;
180 : sunTriangles[k++] = 361+i;
181 : sunTriangles[k++] = 1+j;
182 : sunTriangles[k++] = 361+i;
183 : sunTriangles[k++] = 361+j;
184 : // ring 2
185 : sunTriangles[k++] = 361+i;
186 : sunTriangles[k++] = 361+j;
187 : sunTriangles[k++] = 721+i;
188 : sunTriangles[k++] = 361+j;
189 : sunTriangles[k++] = 721+i;
190 : sunTriangles[k++] = 721+j;
191 : // ring 3
192 : sunTriangles[k++] = 721+i;
193 : sunTriangles[k++] = 721+j;
194 : sunTriangles[k++] = 1081+i;
195 : sunTriangles[k++] = 721+j;
196 : sunTriangles[k++] = 1081+i;
197 : sunTriangles[k++] = 1081+j;
198 : // ring 4
199 : sunTriangles[k++] = 1081+i;
200 : sunTriangles[k++] = 1081+j;
201 : sunTriangles[k++] = 1441+i;
202 : sunTriangles[k++] = 1081+j;
203 : sunTriangles[k++] = 1441+i;
204 : sunTriangles[k++] = 1441+j;
205 : }
206 :
207 : return self;
208 : }
209 :
210 :
211 0 : - (void) dealloc
212 : {
213 : DESTROY(_name);
214 : [super dealloc];
215 : }
216 :
217 :
218 0 : - (NSString*) descriptionComponents
219 : {
220 : NSString *result = [NSString stringWithFormat:@"ID: %u position: %@ radius: %.3fkm", [self universalID], HPVectorDescription([self position]), 0.001 * [self radius]];
221 : if ([self goneNova])
222 : {
223 : result = [result stringByAppendingString:@" (gone nova)"];
224 : }
225 : else if ([self willGoNova])
226 : {
227 : result = [result stringByAppendingString:@" (will go nova)"];
228 : }
229 :
230 : return result;
231 : }
232 :
233 :
234 0 : - (BOOL) canCollide
235 : {
236 : return YES;
237 : }
238 :
239 :
240 : #ifndef NDEBUG
241 0 : - (BOOL) checkCloseCollisionWith:(Entity *)other
242 : {
243 : if (gDebugFlags & DEBUG_COLLISIONS)
244 : {
245 : OOLog(@"sun.collide", @"%@", @"SUN Collision!");
246 : }
247 :
248 : return [super checkCloseCollisionWith:other];
249 : }
250 : #endif
251 :
252 :
253 0 : - (void) update:(OOTimeDelta) delta_t
254 : {
255 : [super update:delta_t];
256 :
257 : PlayerEntity *player = PLAYER;
258 : assert(player != nil);
259 : rotMatrix = OOMatrixForBillboard(position, [player viewpointPosition]);
260 :
261 : if (throw_sparks && _novaExpansionRate > 0.0f) // going NOVA!
262 : {
263 : if (_novaCountdown >= 0.0) // countdown
264 : {
265 : _novaCountdown -= delta_t;
266 : if (corona_speed_factor < 5.0)
267 : {
268 : corona_speed_factor += 0.75 * delta_t;
269 : }
270 : }
271 : else
272 : {
273 : if (_novaExpansionTimer <= 60.0) // expand for a minute
274 : {
275 : double sky_bri = 1.0 - 1.5 * _novaExpansionTimer;
276 : if (sky_bri < 0)
277 : {
278 : [UNIVERSE setSkyColorRed:0.0f // back to black
279 : green:0.0f
280 : blue:0.0f
281 : alpha:0.0f];
282 : }
283 : else
284 : {
285 : [UNIVERSE setSkyColorRed:sky_bri // whiteout
286 : green:sky_bri
287 : blue:sky_bri
288 : alpha:1.0f];
289 : }
290 : if (sky_bri == 1.0)
291 : {
292 : // This sun has now gone nova!
293 : [UNIVERSE setSystemDataKey:@"sun_gone_nova" value:[NSNumber numberWithBool:YES] fromManifest:@"org.oolite.oolite"];
294 : [UNIVERSE setSystemDataKey:@"corona_flare" value:[NSNumber numberWithFloat:0.3] fromManifest:@"org.oolite.oolite"];
295 : [UNIVERSE setSystemDataKey:@"corona_hues" value:[NSNumber numberWithFloat:0.05] fromManifest:@"org.oolite.oolite"];
296 : // Novas are stored under the core manifest if the
297 : // player was there at the time. Default layer 2
298 : // is fine.
299 : OOLog(@"sun.nova.start", @"DEBUG: NOVA original radius %.1f", collision_radius);
300 : }
301 : discColor[0] = 1.0 * _sunBrightnessFactor; discColor[1] = 1.0 * _sunBrightnessFactor; discColor[2] = 1.0 * _sunBrightnessFactor;
302 : _novaExpansionTimer += delta_t;
303 : [UNIVERSE setSystemDataKey:@"sun_radius" value:[NSNumber numberWithFloat:collision_radius + delta_t * _novaExpansionRate] fromManifest:@"org.oolite.oolite"];
304 : }
305 : else
306 : {
307 : OOLog(@"sun.nova.end", @"DEBUG: NOVA final radius %.1f", collision_radius);
308 :
309 : // reset at the new size
310 : [self resetNova];
311 : throw_sparks = YES; // keep throw_sparks at YES to indicate the higher temperature
312 : }
313 : }
314 : }
315 :
316 : // update corona
317 : if (![UNIVERSE reducedDetail])
318 : {
319 : corona_stage += corona_speed_factor * delta_t;
320 : while (corona_stage > 1.0)
321 : {
322 : int i;
323 : corona_stage -= 1.0;
324 : for (i = 0; i < 360; i++)
325 : {
326 : rvalue[i] = rvalue[360 + i];
327 : rvalue[360 + i] = randf();
328 : }
329 : }
330 : }
331 :
332 : }
333 :
334 :
335 :
336 0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
337 : {
338 : if (![UNIVERSE breakPatternHide])
339 : {
340 : if (translucent)
341 : {
342 : // nothing...
343 : }
344 : else
345 : {
346 : [self drawOpaqueParts];
347 : /* Despite the side effects, we have to draw the translucent
348 : * parts on the opaque pass. Planets, at long range, aren't
349 : * depth-buffered. So if the translucent parts are drawn on the
350 : * translucent pass, they appear in front of planets they are
351 : * actually behind. Telabe in G3 is a good one to test with if
352 : * you have any clever ideas.
353 : *
354 : * - CIM 8/7/2013 */
355 : [self drawTranslucentParts];
356 : }
357 : }
358 : }
359 :
360 :
361 0 : - (void) updateCameraRelativePosition
362 : {
363 : HPVector cr_temp = HPvector_subtract([self absolutePositionForSubentity],[PLAYER viewpointPosition]);
364 : /* Special calculation as suns viewed over ~1E9 - and the bigger
365 : * ones are still just about visible at this range - get floating
366 : * point errors messing up the display */
367 : if (EXPECT_NOT(HPmagnitude2(cr_temp) > 1E18))
368 : {
369 : cr_temp = HPvector_multiply_scalar(cr_temp,1E9/HPmagnitude(cr_temp));
370 : }
371 : cameraRelativePosition = HPVectorToVector(cr_temp);
372 : }
373 :
374 :
375 0 : - (void) drawOpaqueParts
376 : {
377 : float sqrt_zero_distance = sqrt(cam_zero_distance);
378 : float effective_radius = collision_radius;
379 : float effective_cor16k = cor16k;
380 :
381 : /* At very long ranges the floating point inaccuracies make a
382 : * complete mess of the calculations, so if the sun is more than
383 : * 1E9 away, draw it closer but smaller. Painter's algorithm
384 : * should stop oddities with planets transiting it */
385 : float large_distance_compensator = sqrt_zero_distance / 1000000000.0f; //1E9
386 : if (large_distance_compensator > 1.0f)
387 : {
388 : sqrt_zero_distance /= large_distance_compensator;
389 : effective_radius /= large_distance_compensator;
390 : effective_cor16k /= large_distance_compensator;
391 : }
392 :
393 : OO_ENTER_OPENGL();
394 :
395 : OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
396 :
397 : if ([UNIVERSE reducedDetail])
398 : {
399 : int subdivideLevel = 2; // 4 is probably the maximum!
400 : float drawFactor = [[UNIVERSE gameView] viewSize].width / 100.0;
401 : float drawRatio2 = drawFactor * effective_radius / sqrt_zero_distance; // equivalent to size on screen in pixels
402 :
403 : if (cam_zero_distance > 0.0f)
404 : {
405 : subdivideLevel = 2 + floorf(drawRatio2);
406 : if (subdivideLevel > 4)
407 : subdivideLevel = 4;
408 : }
409 :
410 : /*
411 :
412 : The depth test gets disabled in parts of this and instead
413 : we rely on the painters algorithm instead.
414 :
415 : The depth buffer isn't granular enough to cope with huge objects at vast
416 : distances.
417 :
418 : */
419 : BOOL ignoreDepthBuffer = cam_zero_distance > effective_radius * effective_radius * 25;
420 :
421 : int steps = 2 * (MAX_SUBDIVIDE - subdivideLevel);
422 :
423 : // Close enough not to draw flat?
424 : if (ignoreDepthBuffer) OOGL(glDisable(GL_DEPTH_TEST));
425 :
426 : OOGL(glColor3fv(discColor));
427 : // FIXME: use vertex arrays
428 : OOGL(glDisable(GL_BLEND));
429 : OOGLBEGIN(GL_TRIANGLE_FAN);
430 : GLDrawBallBillboard(effective_radius, steps, sqrt_zero_distance);
431 : OOGLEND();
432 : OOGL(glEnable(GL_BLEND));
433 :
434 : if (ignoreDepthBuffer) OOGL(glEnable(GL_DEPTH_TEST));
435 :
436 : }
437 : else
438 : {
439 : [self calculateGLArrays:effective_radius
440 : width:effective_cor16k
441 : zDistance:sqrt_zero_distance];
442 : OOGL(glDisable(GL_BLEND));
443 : OOGL(glVertexPointer(3, GL_FLOAT, 0, sunVertices));
444 :
445 : OOGL(glEnableClientState(GL_COLOR_ARRAY));
446 : OOGL(glColorPointer(4, GL_FLOAT, 0, sunColors));
447 :
448 : OOGL(glDrawElements(GL_TRIANGLES, 3*360, GL_UNSIGNED_INT, sunTriangles));
449 :
450 : OOGL(glDisableClientState(GL_COLOR_ARRAY));
451 : OOGL(glEnable(GL_BLEND));
452 :
453 :
454 : }
455 :
456 : OOVerifyOpenGLState();
457 : OOCheckOpenGLErrors(@"SunEntity after drawing %@", self);
458 : }
459 :
460 :
461 0 : - (void) drawTranslucentParts
462 : {
463 : if ([UNIVERSE reducedDetail])
464 : {
465 : return;
466 : }
467 :
468 : OO_ENTER_OPENGL();
469 :
470 : OOSetOpenGLState(OPENGL_STATE_ADDITIVE_BLENDING);
471 :
472 : OOGL(glVertexPointer(3, GL_FLOAT, 0, sunVertices));
473 :
474 : OOGL(glEnableClientState(GL_COLOR_ARRAY));
475 : OOGL(glColorPointer(4, GL_FLOAT, 0, sunColors));
476 : OOGL(glDrawElements(GL_TRIANGLES, 24*360, GL_UNSIGNED_INT, sunTriangles+(3*360)));
477 :
478 : OOGL(glDisableClientState(GL_COLOR_ARRAY));
479 :
480 :
481 : }
482 :
483 0 : - (void) calculateGLArrays:(GLfloat)inner_radius width:(GLfloat)width zDistance:(GLfloat)z_distance
484 : {
485 : // if (EXPECT_NOT(inner_radius >= z_distance)) return; // inside the sphere
486 :
487 : GLfloat activity[8] = {0.84, 0.74, 0.64, 0.54,
488 : 0.3 , 0.4 , 0.7 , 0.8};
489 :
490 : GLfloat si, ci;
491 : GLfloat rv0, rv1, rv2, c0, c1, c2;
492 : GLfloat pt0, pt1;
493 :
494 : unsigned short i, j, k;
495 : GLfloat theta = 0.0f, delta;
496 : delta = M_PI / 180.0f; // Convert step from degrees to radians
497 : pt0=(1.0 - corona_stage) * corona_blending;
498 : pt1=corona_stage * corona_blending;
499 :
500 : sunVertices[0] = 0.0;
501 : sunVertices[1] = 0.0;
502 : sunVertices[2] = 0.0;
503 : k = 3;
504 : for (j = 0 ; j <= 4 ; j++)
505 : {
506 : GLfloat r = inner_radius;
507 : switch (j) {
508 : case 4:
509 : r += width;
510 : break;
511 : case 3:
512 : r += width/1.5f;
513 : break;
514 : case 2:
515 : r += width/3.0f;
516 : break;
517 : case 1:
518 : r += width/15.0f;
519 : break;
520 : }
521 : theta = 0.0;
522 : for (i = 0 ; i < 360 ; i++)
523 : {
524 : GLfloat rm = 1.0;
525 : if (j >= 1 && j < 4)
526 : {
527 : rm = 1.0 + ((0.04/j)*(pt0 * (rvalue[i]+rvalue[i+1]+rvalue[i+2]) + pt1 * (rvalue[i+360]+rvalue[i+361]+rvalue[i+362])))/3;
528 : }
529 : GLfloat z = r * r * rm * rm / z_distance;
530 : si = sinf(theta);
531 : ci = cosf(theta);
532 : theta += delta;
533 : sunVertices[k++] = si * r * rm;
534 : sunVertices[k++] = ci * r * rm;
535 : sunVertices[k++] = -z;
536 : }
537 : }
538 :
539 : GLfloat blackColor[4] = {0.0,0.0,0.0,0.0};
540 : GLfloat *color = blackColor;
541 : GLfloat alpha = 0.0;
542 :
543 : k=0;
544 : sunColors[k++] = discColor[0];
545 : sunColors[k++] = discColor[1];
546 : sunColors[k++] = discColor[2];
547 : sunColors[k++] = discColor[3] * _sunCoronaAlphaFactor;
548 :
549 : for (j = 0 ; j <= 4 ; j++)
550 : {
551 : switch (j) {
552 : case 4:
553 : color = blackColor;
554 : alpha = 0.0;
555 : break;
556 : case 3:
557 : color = outerCoronaColor;
558 : alpha = 0.1;
559 : break;
560 : case 2:
561 : color = outerCoronaColor;
562 : alpha = 0.6;
563 : break;
564 : case 1:
565 : color = discColor;
566 : alpha = 0.95;
567 : break;
568 : case 0:
569 : color = discColor;
570 : alpha = 1.0;
571 : break;
572 : }
573 : for (i = 0 ; i < 360 ; i++)
574 : {
575 : if (j == 0)
576 : {
577 : sunColors[k++] = color[0];
578 : sunColors[k++] = color[1];
579 : sunColors[k++] = color[2];
580 : sunColors[k++] = alpha * _sunCoronaAlphaFactor;
581 : }
582 : else
583 : {
584 : rv0 = pt0 * rvalue[i] + pt1 * rvalue[i + 360];
585 : rv1 = pt0 * rvalue[i + 1] + pt1 * rvalue[i + 361];
586 : rv2 = pt0 * rvalue[i + 2] + pt1 * rvalue[i + 362];
587 : c0 = color[0] * (activity[j-1] + rv0*activity[j+3]);
588 : c1 = color[1] * (activity[j-1] + rv1*activity[j+3]);
589 : c2 = color[2] * (activity[j-1] + rv2*activity[j+3]);
590 : if (c1 > c2 && c1 > c0)
591 : {
592 : c1 = fmaxf(c0,c2);
593 : }
594 :
595 : sunColors[k++] = c0;
596 : sunColors[k++] = c1;
597 : sunColors[k++] = c2;
598 : sunColors[k++] = alpha * _sunCoronaAlphaFactor;
599 : }
600 : }
601 : }
602 : }
603 :
604 :
605 : - (void) drawDirectVisionSunGlare
606 : {
607 : #if SUN_DIRECT_VISION_GLARE
608 : OO_ENTER_OPENGL();
609 :
610 : OOSetOpenGLState(OPENGL_STATE_OVERLAY);
611 :
612 : GLfloat sunGlareAngularSize = atan([self radius]/HPdistance([PLAYER viewpointPosition], [self position])) * SUN_GLARE_MULT_FACTOR + (SUN_GLARE_ADD_FACTOR);
613 :
614 : GLfloat directVisionSunGlare = [PLAYER lookingAtSunWithThresholdAngleCos:cosf(sunGlareAngularSize)];
615 : if (directVisionSunGlare)
616 : {
617 : NSSize siz = [[UNIVERSE gui] size];
618 : MyOpenGLView *gameView = [UNIVERSE gameView];
619 : GLfloat aspectRatio = ([gameView viewSize].width / [gameView viewSize].height);
620 : GLfloat z = [gameView display_z] / (aspectRatio > 4.0f/3.0f ? aspectRatio : 1.0f / aspectRatio);
621 : GLfloat atmosphericReductionFactor = 1.0f - [PLAYER insideAtmosphereFraction];
622 : // 182: square of ratio of radius to sun-witchpoint distance
623 : // in default Lave
624 : GLfloat distanceReductionFactor = OOClamp_0_1_f(([self radius] * [self radius] * 182.0) / HPdistance2([PLAYER position], [self position]));
625 : GLfloat sunGlareFilterMultiplierLocal = [PLAYER sunGlareFilter];
626 : GLfloat directVisionSunGlareColor[4] = {discColor[0], discColor[1], discColor[2], directVisionSunGlare *
627 : atmosphericReductionFactor * distanceReductionFactor *
628 : (1.0f - sunGlareFilterMultiplierLocal) * 0.0085f};
629 :
630 : OOGL(glColor4fv(directVisionSunGlareColor));
631 :
632 : OOGLBEGIN(GL_QUADS);
633 : glVertex3f(siz.width, siz.height, z);
634 : glVertex3f(siz.width, -siz.height, z);
635 : glVertex3f(-siz.width, -siz.height, z);
636 : glVertex3f(-siz.width, siz.height, z);
637 : OOGLEND();
638 : }
639 : #endif
640 : }
641 :
642 :
643 : - (void) drawStarGlare
644 : {
645 : OO_ENTER_OPENGL();
646 :
647 : OOSetOpenGLState(OPENGL_STATE_OVERLAY);
648 :
649 : float sqrt_zero_distance = sqrtf(cam_zero_distance);
650 : float alt = sqrt_zero_distance - collision_radius;
651 : if (EXPECT_NOT(alt < 0))
652 : {
653 : return;
654 : }
655 : float corona = cor16k/SUN_GLARE_CORONA_FACTOR;
656 : if (corona > alt)
657 : {
658 : float alpha = (1-(alt/corona));
659 : float alphaMult = _sunCoronaAlphaFactor * alpha;
660 : alpha *= alphaMult;
661 : GLfloat glareColor[4] = {discColor[0], discColor[1], discColor[2], alpha};
662 : NSSize siz = [[UNIVERSE gui] size];
663 : MyOpenGLView *gameView = [UNIVERSE gameView];
664 : GLfloat aspectRatio = ([gameView viewSize].width / [gameView viewSize].height);
665 : GLfloat z = [gameView display_z] / (aspectRatio > 4.0f/3.0f ? aspectRatio : 1.0f / aspectRatio);
666 : OOGL(glColor4fv(glareColor));
667 :
668 : OOGLBEGIN(GL_QUADS);
669 : glVertex3f(siz.width, siz.height, z);
670 : glVertex3f(siz.width, -siz.height, z);
671 : glVertex3f(-siz.width, -siz.height, z);
672 : glVertex3f(-siz.width, siz.height, z);
673 : OOGLEND();
674 :
675 : }
676 : }
677 :
678 :
679 :
680 : - (BOOL) changeSunProperty:(NSString *)key withDictionary:(NSDictionary*) dict
681 : {
682 : id object = [dict objectForKey:key];
683 : static GLfloat oldRadius = 0.0;
684 : if ([key isEqualToString:@"sun_radius"])
685 : {
686 : oldRadius = [object doubleValue]; // clamp corona_flare in case planetinfo.plist / savegame contains the wrong value
687 : [self setRadius:oldRadius andCorona:[dict oo_floatForKey:@"corona_flare" defaultValue:0.0f]];
688 : }
689 : else if ([key isEqualToString:KEY_SUNNAME])
690 : {
691 : [self setName:[dict oo_stringForKey:KEY_SUNNAME]];
692 : }
693 : else if ([key isEqualToString:@"corona_flare"])
694 : {
695 : [self setRadius:collision_radius andCorona:[object floatValue]];
696 : }
697 : else if ([key isEqualToString:@"corona_shimmer"])
698 : {
699 : corona_speed_factor=OOClamp_0_1_f([object floatValue]) * 2.0 + randf() * randf();
700 : }
701 : else if ([key isEqualToString:@"corona_hues"])
702 : {
703 : corona_blending=OOClamp_0_1_f([object floatValue]);
704 : }
705 : else if ([key isEqualToString:@"sun_gone_nova"])
706 : {
707 :
708 : if ([dict oo_boolForKey:key])
709 : {
710 : [self setGoingNova:YES inTime:0];
711 : }
712 : else
713 : {
714 : [self setGoingNova:NO inTime:0];
715 : // oldRadius is always the radius we had before going nova...
716 : [self setRadius: oldRadius andCorona:[dict oo_floatForKey:@"corona_flare" defaultValue:0.0f]];
717 :
718 : }
719 : }
720 : else
721 : {
722 : OOLogWARN(@"script.warning", @"Change to property '%@' not applied, will apply only after leaving this system.",key);
723 : return NO;
724 : }
725 : return YES;
726 : }
727 :
728 :
729 : - (OOStellarBodyType) planetType
730 : {
731 : return STELLAR_TYPE_SUN;
732 : }
733 :
734 :
735 : - (void) getDiffuseComponents:(GLfloat[4])components
736 : {
737 : NSParameterAssert(components != NULL);
738 : memcpy(components, sun_diffuse, sizeof sun_diffuse);
739 : }
740 :
741 :
742 : - (void) getSpecularComponents:(GLfloat[4])components
743 : {
744 : NSParameterAssert(components != NULL);
745 : memcpy(components, sun_specular, sizeof sun_specular);
746 : }
747 :
748 :
749 0 : - (double) radius
750 : {
751 : return collision_radius;
752 : }
753 :
754 :
755 : - (void) setRadius:(GLfloat) rad andCorona:(GLfloat)corona
756 : {
757 : collision_radius = rad;
758 : if (corona < 0.01f) {
759 : corona = 0.01f;
760 : }
761 : cor16k = rad * 8 * corona;
762 :
763 : GLfloat corouter = rad * (1+(8*corona));
764 :
765 : lim16k = corouter * corouter * NO_DRAW_DISTANCE_FACTOR*NO_DRAW_DISTANCE_FACTOR;
766 : }
767 :
768 :
769 0 : - (void) setPosition:(HPVector) posn
770 : {
771 : [super setPosition: posn];
772 : [UNIVERSE setMainLightPosition: HPVectorToVector(posn)];
773 : }
774 :
775 :
776 : - (BOOL) willGoNova
777 : {
778 : return throw_sparks;
779 : }
780 :
781 :
782 : - (BOOL) goneNova
783 : {
784 : return throw_sparks && _novaCountdown <= 0;
785 : }
786 :
787 :
788 : - (void) setGoingNova:(BOOL) yesno inTime:(double)interval
789 : {
790 : throw_sparks = yesno;
791 : if (throw_sparks)
792 : {
793 : _novaCountdown = fmax(interval, 0.0);
794 : OOLog(@"script.debug.setSunNovaIn", @"NOVA activated! time until Nova : %.1f s", _novaCountdown);
795 : }
796 :
797 : _novaExpansionTimer = 0;
798 : _novaExpansionRate = 10000;
799 : }
800 :
801 :
802 : - (void) resetNova
803 : {
804 : _novaExpansionTimer = 0.0;
805 : _novaExpansionRate = 0.0f;
806 : _novaCountdown = 0.0;
807 : }
808 :
809 :
810 0 : - (BOOL) isSun
811 : {
812 : return YES;
813 : }
814 :
815 :
816 0 : - (BOOL) isVisible
817 : {
818 : return YES;
819 : }
820 :
821 :
822 0 : - (NSString *) name
823 : {
824 : return _name;
825 : }
826 :
827 :
828 0 : - (void) setName:(NSString *)name
829 : {
830 : [_name release];
831 : _name = [name retain];
832 : }
833 :
834 :
835 : @end
|