1 // dome.cxx -- model sky with an upside down "bowl"
3 // Written by Curtis Olson, started December 1997.
4 // SSG-ified by Curtis Olson, February 2000.
6 // Copyright (C) 1997-2000 Curtis L. Olson - curt@flightgear.org
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library 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 GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the
20 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 // Boston, MA 02111-1307, USA.
27 # include <simgear_config.h>
40 #include <simgear/debug/logstream.hxx>
41 #include <simgear/sky/sunsky/sunsky.hxx>
47 # pragma global_optimizer off
51 // in meters of course
52 static const float center_elev = 0.3125;
54 static const float upper_radius = 0.6250;
55 static const float upper_elev = 0.2500;
57 static const float middle_radius = 0.8750;
58 static const float middle_elev = 0.1000;
60 static const float lower_radius = 0.8750;
61 static const float lower_elev = 0.0000;
63 static const float bottom_radius = 0.6250;
64 static const float bottom_elev = -0.0250;
67 // Set up dome rendering callbacks
68 static int sgSkyDomePreDraw( ssgEntity *e ) {
69 /* cout << endl << "Dome Pre Draw" << endl << "----------------"
72 ssgLeaf *f = (ssgLeaf *)e;
73 if ( f -> hasState () ) f->getState()->apply() ;
75 glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_FOG_BIT );
76 // cout << "push error = " << glGetError() << endl;
78 glDisable( GL_DEPTH_TEST );
84 static int sgSkyDomePostDraw( ssgEntity *e ) {
85 /* cout << endl << "Dome Post Draw" << endl << "----------------"
89 // cout << "pop error = " << glGetError() << endl;
96 SGSkyDome::SGSkyDome( void ) {
101 SGSkyDome::~SGSkyDome( void ) {
105 // initialize the sky object and connect it into our scene graph
106 ssgBranch * SGSkyDome::build( double hscale, double vscale ) {
113 dome_state = new ssgSimpleState();
114 dome_state->setShadeModel( GL_SMOOTH );
115 dome_state->disable( GL_LIGHTING );
116 dome_state->disable( GL_CULL_FACE );
117 dome_state->enable( GL_TEXTURE_2D );
118 dome_state->disable( GL_COLOR_MATERIAL );
119 dome_state->setColourMaterial( GL_AMBIENT_AND_DIFFUSE );
120 dome_state->setMaterial( GL_EMISSION, 0, 0, 0, 1 );
121 dome_state->setMaterial( GL_SPECULAR, 0, 0, 0, 1 );
122 dome_state->disable( GL_BLEND );
123 dome_state->disable( GL_ALPHA_TEST );
126 center_disk_vl = new ssgVertexArray( 14 );
127 center_disk_cl = new ssgColourArray( 14 );
129 upper_ring_vl = new ssgVertexArray( 26 );
130 upper_ring_cl = new ssgColourArray( 26 );
132 middle_ring_vl = new ssgVertexArray( 26 );
133 middle_ring_cl = new ssgColourArray( 26 );
135 lower_ring_vl = new ssgVertexArray( 26 );
136 lower_ring_cl = new ssgColourArray( 26 );
138 // initially seed to all blue
139 // sgSetVec4( color, 0.0, 0.0, 1.0, 1.0 );
141 // generate the raw vertex data
142 sgVec3 center_vertex;
143 sgVec3 upper_vertex[12];
144 sgVec3 middle_vertex[12];
145 sgVec3 lower_vertex[12];
146 sgVec3 bottom_vertex[12];
148 sgSetVec3( center_vertex, 0.0, 0.0, center_elev * vscale );
150 for ( i = 0; i < 12; i++ ) {
151 theta = (i * 30.0) * SGD_DEGREES_TO_RADIANS;
153 sgSetVec3( upper_vertex[i],
154 cos(theta) * upper_radius * hscale,
155 sin(theta) * upper_radius * hscale,
156 upper_elev * vscale );
158 sgSetVec3( middle_vertex[i],
159 cos((double)theta) * middle_radius * hscale,
160 sin((double)theta) * middle_radius * hscale,
161 middle_elev * vscale );
163 sgSetVec3( lower_vertex[i],
164 cos((double)theta) * lower_radius * hscale,
165 sin((double)theta) * lower_radius * hscale,
166 lower_elev * vscale );
168 sgSetVec3( bottom_vertex[i],
169 cos((double)theta) * bottom_radius * hscale,
170 sin((double)theta) * bottom_radius * hscale,
171 bottom_elev * vscale );
174 // generate the center disk vertex/color arrays
175 center_disk_vl->add( center_vertex );
176 // center_disk_cl->add( color );
177 for ( i = 11; i >= 0; i-- ) {
178 center_disk_vl->add( upper_vertex[i] );
179 // center_disk_cl->add( color );
181 center_disk_vl->add( upper_vertex[11] );
182 // center_disk_cl->add( color );
184 // generate the upper ring
185 for ( i = 0; i < 12; i++ ) {
186 upper_ring_vl->add( middle_vertex[i] );
187 // upper_ring_cl->add( color );
189 upper_ring_vl->add( upper_vertex[i] );
190 // upper_ring_cl->add( color );
192 upper_ring_vl->add( middle_vertex[0] );
193 // upper_ring_cl->add( color );
195 upper_ring_vl->add( upper_vertex[0] );
196 // upper_ring_cl->add( color );
198 // generate middle ring
199 for ( i = 0; i < 12; i++ ) {
200 middle_ring_vl->add( lower_vertex[i] );
201 // middle_ring_cl->add( color );
203 middle_ring_vl->add( middle_vertex[i] );
204 // middle_ring_cl->add( color );
206 middle_ring_vl->add( lower_vertex[0] );
207 // middle_ring_cl->add( color );
209 middle_ring_vl->add( middle_vertex[0] );
210 // middle_ring_cl->add( color );
212 // generate lower ring
213 for ( i = 0; i < 12; i++ ) {
214 lower_ring_vl->add( bottom_vertex[i] );
215 // lower_ring_cl->add( color );
217 lower_ring_vl->add( lower_vertex[i] );
218 // lower_ring_cl->add( color );
220 lower_ring_vl->add( bottom_vertex[0] );
221 // lower_ring_cl->add( color );
223 lower_ring_vl->add( lower_vertex[0] );
224 // lower_ring_cl->add( color );
226 // force a repaint of the sky colors with ugly defaults
228 // sgSetVec4( fog_color, 1.0, 1.0, 1.0, 1.0 );
229 // repaint( color, fog_color, 0.0, 5000.0 );
231 // build the ssg scene graph sub tree for the sky and connected
232 // into the provide scene graph branch
233 ssgVtxTable *center_disk, *upper_ring, *middle_ring, *lower_ring;
235 center_disk = new ssgVtxTable( GL_TRIANGLE_FAN,
236 center_disk_vl, NULL, NULL, center_disk_cl );
238 upper_ring = new ssgVtxTable( GL_TRIANGLE_STRIP,
239 upper_ring_vl, NULL, NULL, upper_ring_cl );
241 middle_ring = new ssgVtxTable( GL_TRIANGLE_STRIP,
242 middle_ring_vl, NULL, NULL, middle_ring_cl );
244 lower_ring = new ssgVtxTable( GL_TRIANGLE_STRIP,
245 lower_ring_vl, NULL, NULL, lower_ring_cl );
247 center_disk->setState( dome_state );
248 upper_ring->setState( dome_state );
249 middle_ring->setState( dome_state );
250 lower_ring->setState( dome_state );
252 dome_transform = new ssgTransform;
253 dome_transform->addKid( center_disk );
254 dome_transform->addKid( upper_ring );
255 dome_transform->addKid( middle_ring );
256 dome_transform->addKid( lower_ring );
258 // not entirely satisfying. We are depending here that the first
259 // thing we add to a parent is the first drawn
260 center_disk->setCallback( SSG_CALLBACK_PREDRAW, sgSkyDomePreDraw );
261 center_disk->setCallback( SSG_CALLBACK_POSTDRAW, sgSkyDomePostDraw );
263 upper_ring->setCallback( SSG_CALLBACK_PREDRAW, sgSkyDomePreDraw );
264 upper_ring->setCallback( SSG_CALLBACK_POSTDRAW, sgSkyDomePostDraw );
266 middle_ring->setCallback( SSG_CALLBACK_PREDRAW, sgSkyDomePreDraw );
267 middle_ring->setCallback( SSG_CALLBACK_POSTDRAW, sgSkyDomePostDraw );
269 lower_ring->setCallback( SSG_CALLBACK_PREDRAW, sgSkyDomePreDraw );
270 lower_ring->setCallback( SSG_CALLBACK_POSTDRAW, sgSkyDomePostDraw );
272 return dome_transform;
277 * regenerate the sky texture based on the current position and time
279 * lat: the current latitude (0 ... 360)
280 * lon: the current longitude (-90 ... 90) south to north
281 * zone: standard meredian
282 * julianDay: julian day (1 ... 365)
283 * time: time of day (0.0 ... 23.99 - 14.25 = 2:15pm)
284 * turbidity: (1.0 ... 30+) 2-6 are most useful for clear days.
285 * atmEffects: if atm effects are not initialized, bad things will
286 * happen if you try to use them....
288 #define SG_SKYTEXTURE_WIDTH 128
289 #define SG_SKYTEXTURE_HEIGHT 128
291 // produce theta-distorted map suitable for texture mapping
292 static const bool thetaMap = false;
294 bool SGSkyDome::repaint( float lat, float lon, int zone, int julianDay,
295 int time, float turbidity, bool atmEffects )
297 SGSunSky sunSky(lat, lon, zone, julianDay, time, turbidity, atmEffects);
299 float sunAngle = sqrt(sunSky.GetSunSolidAngle() / SG_PI);
300 sgVec3 *sunColour = sunSky.GetSunSpectralRadiance().To_XYZ();
301 sgVec3 *sunDir = sunSky.GetSunPosition();
303 for (unsigned int i = 0; i < 128; i++ ) {
304 for (unsigned int j = 0; j < 128; j++ ) {
306 sgVec2 hemiPos, normVec2;
310 sgSetVec2( normVec2, 1.0, 1.0 );
311 sgSetVec2( hemiPos, (j + 0.5)/SG_SKYTEXTURE_WIDTH,
312 (i + 0.5)/SG_SKYTEXTURE_WIDTH );
314 sgScaleVec2(hemiPos, 2.0);
315 sgAddVec2(hemiPos, normVec2);
317 if (sgDistanceSquaredVec2(hemiPos, normVec2) <= 1.0)
319 // North = Up, East = left, so hemisphere is 'above' viewer:
320 // imagine lying on your back looking up at the sky,
321 // head oriented towards north
325 // remap to theta-based coords
326 float r = sgLengthVec2(hemiPos);
327 sgScaleVec2(hemiPos, cos(SG_PI * (1 - r) / 2.0) / r);
330 sgSetVec3( hemiDir, -hemiPos[1], -hemiPos[0],
331 sqrt(1.0-sgDistanceSquaredVec2(hemiPos, normVec2)) );
333 if (acos(sgScalarProductVec2(hemiDir, *sunDir)) < sunAngle)
335 // this is actually a little beside the point: as
336 // the sun subtends about 0.5 degrees, at an image
337 // size of 400x400 pixels, the sun will only cover a
339 hemiColour = sunColour;
343 hemiColour = sunSky.GetSkyXYZRadiance(&hemiDir);
344 // hemiColour = csDisplay.ToGamut(hemiColour);
347 radImage.SetPixel(j, i, hemiColour);
350 radImage.SetPixel(j, i, cBlack);
356 // repaint the sky colors based on current value of sun_angle, sky,
357 // and fog colors. This updates the color arrays for ssgVtxTable.
358 // sun angle in degrees relative to verticle
359 // 0 degrees = high noon
360 // 90 degrees = sun rise/set
361 // 180 degrees = darkest midnight
362 bool SGSkyDome::repaint( sgVec4 sky_color, sgVec4 fog_color, double sun_angle,
367 sgVec3 outer_param, outer_amt, outer_diff;
368 sgVec3 middle_param, middle_amt, middle_diff;
371 // Check for sunrise/sunset condition
372 if ( (sun_angle > 80.0) && (sun_angle < 100.0) ) {
374 sgSetVec3( outer_param,
375 (10.0 - fabs(90.0 - sun_angle)) / 20.0,
376 (10.0 - fabs(90.0 - sun_angle)) / 40.0,
377 -(10.0 - fabs(90.0 - sun_angle)) / 30.0 );
379 sgSetVec3( middle_param,
380 (10.0 - fabs(90.0 - sun_angle)) / 40.0,
381 (10.0 - fabs(90.0 - sun_angle)) / 80.0,
384 sgScaleVec3( outer_diff, outer_param, 1.0 / 6.0 );
386 sgScaleVec3( middle_diff, middle_param, 1.0 / 6.0 );
388 sgSetVec3( outer_param, 0.0, 0.0, 0.0 );
389 sgSetVec3( middle_param, 0.0, 0.0, 0.0 );
391 sgSetVec3( outer_diff, 0.0, 0.0, 0.0 );
392 sgSetVec3( middle_diff, 0.0, 0.0, 0.0 );
394 // printf(" outer_red_param = %.2f outer_red_diff = %.2f\n",
395 // outer_red_param, outer_red_diff);
397 // calculate transition colors between sky and fog
398 sgCopyVec3( outer_amt, outer_param );
399 sgCopyVec3( middle_amt, middle_param );
402 // First, recalulate the basic colors
406 sgVec4 upper_color[12];
407 sgVec4 middle_color[12];
408 sgVec4 lower_color[12];
409 sgVec4 bottom_color[12];
413 if ( vis < 3000.0 ) {
414 vis_factor = (vis - 1000.0) / 2000.0;
415 if ( vis_factor < 0.0 ) {
422 for ( j = 0; j < 3; j++ ) {
423 diff = sky_color[j] - fog_color[j];
424 center_color[j] = sky_color[j] - diff * ( 1.0 - vis_factor );
426 center_color[3] = 1.0;
428 for ( i = 0; i < 6; i++ ) {
429 for ( j = 0; j < 3; j++ ) {
430 diff = sky_color[j] - fog_color[j];
432 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
433 // l->sky_color[j], l->fog_color[j], diff);
435 upper_color[i][j] = sky_color[j] - diff * ( 1.0 - vis_factor * 0.7);
436 middle_color[i][j] = sky_color[j] - diff * ( 1.0 - vis_factor * 0.1)
438 lower_color[i][j] = fog_color[j] + outer_amt[j];
440 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
441 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
442 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
443 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
444 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
445 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
447 upper_color[i][3] = middle_color[i][3] = lower_color[i][3] = 1.0;
449 for ( j = 0; j < 3; j++ ) {
450 outer_amt[j] -= outer_diff[j];
451 middle_amt[j] -= middle_diff[j];
455 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
456 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
457 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
458 middle_color[i][0], middle_color[i][1], middle_color[i][2],
460 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
461 lower_color[i][0], lower_color[i][1], lower_color[i][2],
466 sgSetVec3( outer_amt, 0.0, 0.0, 0.0 );
467 sgSetVec3( middle_amt, 0.0, 0.0, 0.0 );
469 for ( i = 6; i < 12; i++ ) {
470 for ( j = 0; j < 3; j++ ) {
471 diff = sky_color[j] - fog_color[j];
473 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
474 // sky_color[j], fog_color[j], diff);
476 upper_color[i][j] = sky_color[j] - diff * ( 1.0 - vis_factor * 0.7);
477 middle_color[i][j] = sky_color[j] - diff * ( 1.0 - vis_factor * 0.1)
479 lower_color[i][j] = fog_color[j] + outer_amt[j];
481 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
482 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
483 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
484 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
485 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
486 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
488 upper_color[i][3] = middle_color[i][3] = lower_color[i][3] = 1.0;
490 for ( j = 0; j < 3; j++ ) {
491 outer_amt[j] += outer_diff[j];
492 middle_amt[j] += middle_diff[j];
496 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
497 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
498 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
499 middle_color[i][0], middle_color[i][1], middle_color[i][2],
501 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
502 lower_color[i][0], lower_color[i][1], lower_color[i][2],
507 for ( i = 0; i < 12; i++ ) {
508 sgCopyVec4( bottom_color[i], fog_color );
512 // Second, assign the basic colors to the object color arrays
518 // update the center disk color arrays
520 slot = center_disk_cl->get( counter++ );
522 // sgSetVec4( red, 1.0, 0.0, 0.0, 1.0 );
523 sgCopyVec4( slot, center_color );
524 for ( i = 11; i >= 0; i-- ) {
525 slot = center_disk_cl->get( counter++ );
526 sgCopyVec4( slot, upper_color[i] );
528 slot = center_disk_cl->get( counter++ );
529 sgCopyVec4( slot, upper_color[11] );
531 // generate the upper ring
533 for ( i = 0; i < 12; i++ ) {
534 slot = upper_ring_cl->get( counter++ );
535 sgCopyVec4( slot, middle_color[i] );
537 slot = upper_ring_cl->get( counter++ );
538 sgCopyVec4( slot, upper_color[i] );
540 slot = upper_ring_cl->get( counter++ );
541 sgCopyVec4( slot, middle_color[0] );
543 slot = upper_ring_cl->get( counter++ );
544 sgCopyVec4( slot, upper_color[0] );
546 // generate middle ring
548 for ( i = 0; i < 12; i++ ) {
549 slot = middle_ring_cl->get( counter++ );
550 sgCopyVec4( slot, lower_color[i] );
552 slot = middle_ring_cl->get( counter++ );
553 sgCopyVec4( slot, middle_color[i] );
555 slot = middle_ring_cl->get( counter++ );
556 sgCopyVec4( slot, lower_color[0] );
558 slot = middle_ring_cl->get( counter++ );
559 sgCopyVec4( slot, middle_color[0] );
561 // generate lower ring
563 for ( i = 0; i < 12; i++ ) {
564 slot = lower_ring_cl->get( counter++ );
565 sgCopyVec4( slot, bottom_color[i] );
567 slot = lower_ring_cl->get( counter++ );
568 sgCopyVec4( slot, lower_color[i] );
570 slot = lower_ring_cl->get( counter++ );
571 sgCopyVec4( slot, bottom_color[0] );
573 slot = lower_ring_cl->get( counter++ );
574 sgCopyVec4( slot, lower_color[0] );
580 // reposition the sky at the specified origin and orientation
581 // lon specifies a rotation about the Z axis
582 // lat specifies a rotation about the new Y axis
583 // spin specifies a rotation about the new Z axis (and orients the
584 // sunrise/set effects
585 bool SGSkyDome::reposition( sgVec3 p, double lon, double lat, double spin ) {
587 sgMat4 T, LON, LAT, SPIN;
590 // Translate to view position
591 // Point3D zero_elev = current_view.get_cur_zero_elev();
592 // xglTranslatef( zero_elev.x(), zero_elev.y(), zero_elev.z() );
593 sgMakeTransMat4( T, p );
595 // printf(" Translated to %.2f %.2f %.2f\n",
596 // zero_elev.x, zero_elev.y, zero_elev.z );
598 // Rotate to proper orientation
599 // printf(" lon = %.2f lat = %.2f\n",
600 // lon * SGD_RADIANS_TO_DEGREES,
601 // lat * SGD_RADIANS_TO_DEGREES);
602 // xglRotatef( lon * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
603 sgSetVec3( axis, 0.0, 0.0, 1.0 );
604 sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
606 // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
608 sgSetVec3( axis, 0.0, 1.0, 0.0 );
609 sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
611 // xglRotatef( l->sun_rotation * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
612 sgSetVec3( axis, 0.0, 0.0, 1.0 );
613 sgMakeRotMat4( SPIN, spin * SGD_RADIANS_TO_DEGREES, axis );
617 sgCopyMat4( TRANSFORM, T );
618 sgPreMultMat4( TRANSFORM, LON );
619 sgPreMultMat4( TRANSFORM, LAT );
620 sgPreMultMat4( TRANSFORM, SPIN );
623 sgSetCoord( &skypos, TRANSFORM );
625 dome_transform->setTransform( &skypos );