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 - http://www.flightgear.org/~curt
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 General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 # include <simgear_config.h>
35 #include <simgear/compiler.h>
39 #include <osg/Geometry>
41 #include <osg/MatrixTransform>
42 #include <osg/Material>
43 #include <osg/ShadeModel>
45 #include <simgear/debug/logstream.hxx>
51 # pragma global_optimizer off
55 // proportions of max dimensions fed to the build() routine
56 static const float center_elev = 1.0;
58 static const float upper_radius = 0.6;
59 static const float upper_elev = 0.15;
61 static const float middle_radius = 0.9;
62 static const float middle_elev = 0.08;
64 static const float lower_radius = 1.0;
65 static const float lower_elev = 0.0;
67 static const float bottom_radius = 0.8;
68 static const float bottom_elev = -0.1;
72 SGSkyDome::SGSkyDome( void ) {
78 SGSkyDome::~SGSkyDome( void ) {
82 // initialize the sky object and connect it into our scene graph
84 SGSkyDome::build( double hscale, double vscale ) {
86 osg::Geode* geode = new osg::Geode;
89 osg::StateSet* stateSet = geode->getOrCreateStateSet();
90 stateSet->setRenderBinDetails(-10, "RenderBin");
92 osg::ShadeModel* shadeModel = new osg::ShadeModel;
93 shadeModel->setMode(osg::ShadeModel::SMOOTH);
94 stateSet->setAttributeAndModes(shadeModel);
95 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
96 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
97 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
98 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
99 stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
100 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
101 osg::Material* material = new osg::Material;
102 // material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
103 // material->setEmission(osg::Material::FRONT_AND_BACK,
104 // osg::Vec4(0, 0, 0, 1));
105 // material->setSpecular(osg::Material::FRONT_AND_BACK,
106 // osg::Vec4(0, 0, 0, 1));
107 // material->setShininess(osg::Material::FRONT_AND_BACK, 0);
108 stateSet->setAttribute(material);
111 // initially seed to all blue
112 center_disk_vl = new osg::Vec3Array;
113 center_disk_cl = new osg::Vec3Array;
114 center_disk_cl->assign(14, osg::Vec3(0, 0, 1));
116 upper_ring_vl = new osg::Vec3Array;
117 upper_ring_cl = new osg::Vec3Array;
118 upper_ring_cl->assign(26, osg::Vec3(0, 0, 1));
120 middle_ring_vl = new osg::Vec3Array;
121 middle_ring_cl = new osg::Vec3Array;
122 middle_ring_cl->assign(26, osg::Vec3(0, 0, 1));
124 lower_ring_vl = new osg::Vec3Array;
125 lower_ring_cl = new osg::Vec3Array;
126 lower_ring_cl->assign(26, osg::Vec3(0, 0, 1));
129 // generate the raw vertex data
130 osg::Vec3 center_vertex(0.0, 0.0, center_elev*vscale);
131 osg::Vec3 upper_vertex[12];
132 osg::Vec3 middle_vertex[12];
133 osg::Vec3 lower_vertex[12];
134 osg::Vec3 bottom_vertex[12];
136 for ( int i = 0; i < 12; ++i ) {
137 double theta = (i * 30) * SGD_DEGREES_TO_RADIANS;
138 double sTheta = hscale*sin(theta);
139 double cTheta = hscale*cos(theta);
141 upper_vertex[i] = osg::Vec3(cTheta * upper_radius,
142 sTheta * upper_radius,
143 upper_elev * vscale);
145 middle_vertex[i] = osg::Vec3(cTheta * middle_radius,
146 sTheta * middle_radius,
147 middle_elev * vscale);
149 lower_vertex[i] = osg::Vec3(cTheta * lower_radius,
150 sTheta * lower_radius,
151 lower_elev * vscale);
153 bottom_vertex[i] = osg::Vec3(cTheta * bottom_radius,
154 sTheta * bottom_radius,
155 bottom_elev * vscale);
158 // generate the center disk vertex/color arrays
159 center_disk_vl->push_back(center_vertex);
160 for ( int i = 11; i >= 0; --i )
161 center_disk_vl->push_back(upper_vertex[i]);
162 center_disk_vl->push_back(upper_vertex[11]);
164 // generate the upper ring
165 for ( int i = 0; i < 12; ++i ) {
166 upper_ring_vl->push_back( middle_vertex[i] );
167 upper_ring_vl->push_back( upper_vertex[i] );
169 upper_ring_vl->push_back( middle_vertex[0] );
170 upper_ring_vl->push_back( upper_vertex[0] );
172 // generate middle ring
173 for ( int i = 0; i < 12; i++ ) {
174 middle_ring_vl->push_back( lower_vertex[i] );
175 middle_ring_vl->push_back( middle_vertex[i] );
177 middle_ring_vl->push_back( lower_vertex[0] );
178 middle_ring_vl->push_back( middle_vertex[0] );
180 // generate lower ring
181 for ( int i = 0; i < 12; i++ ) {
182 lower_ring_vl->push_back( bottom_vertex[i] );
183 lower_ring_vl->push_back( lower_vertex[i] );
185 lower_ring_vl->push_back( bottom_vertex[0] );
186 lower_ring_vl->push_back( lower_vertex[0] );
188 // force a repaint of the sky colors with ugly defaults
189 repaint(SGVec3f(1, 1, 1), SGVec3f(1, 1, 1), 0.0, 5000.0 );
191 // build the ssg scene graph sub tree for the sky and connected
192 // into the provide scene graph branch
193 osg::Geometry* geometry = new osg::Geometry;
194 geometry->setName("Dome Center");
195 geometry->setUseDisplayList(false);
196 geometry->setVertexArray(center_disk_vl.get());
197 geometry->setColorArray(center_disk_cl.get());
198 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
199 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
200 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_FAN, 0, 14));
201 geode->addDrawable(geometry);
203 geometry = new osg::Geometry;
204 geometry->setName("Dome Upper Ring");
205 geometry->setUseDisplayList(false);
206 geometry->setVertexArray(upper_ring_vl.get());
207 geometry->setColorArray(upper_ring_cl.get());
208 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
209 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
210 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
211 geode->addDrawable(geometry);
213 geometry = new osg::Geometry;
214 geometry->setName("Dome Middle Ring");
215 geometry->setUseDisplayList(false);
216 geometry->setVertexArray(middle_ring_vl.get());
217 geometry->setColorArray(middle_ring_cl.get());
218 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
219 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
220 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
221 geode->addDrawable(geometry);
223 geometry = new osg::Geometry;
224 geometry->setName("Dome Lower Ring");
225 geometry->setUseDisplayList(false);
226 geometry->setVertexArray(lower_ring_vl.get());
227 geometry->setColorArray(lower_ring_cl.get());
228 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
229 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
230 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
231 geode->addDrawable(geometry);
233 dome_transform = new osg::MatrixTransform;
234 dome_transform->addChild(geode);
236 return dome_transform.get();
239 static void fade_to_black(osg::Vec3 sky_color[], float asl, int count) {
240 const float ref_asl = 10000.0f;
241 float d = exp( - asl / ref_asl );
242 for(int i = 0; i < count ; i++) {
244 sky_color[i][0] = sky_color[i][0] - f * sky_color[i][0] ;
245 sky_color[i][1] = sky_color[i][1] - f * sky_color[i][1] ;
246 sky_color[i][2] = sky_color[i][2] - f * sky_color[i][2] ;
250 // repaint the sky colors based on current value of sun_angle, sky,
251 // and fog colors. This updates the color arrays for ssgVtxTable.
252 // sun angle in degrees relative to verticle
253 // 0 degrees = high noon
254 // 90 degrees = sun rise/set
255 // 180 degrees = darkest midnight
257 SGSkyDome::repaint( const SGVec3f& sky_color, const SGVec3f& fog_color,
258 double sun_angle, double vis )
260 SGVec3f outer_param, outer_diff;
261 SGVec3f middle_param, middle_diff;
263 // Check for sunrise/sunset condition
267 outer_param[0] = (10.0 - fabs(90.0 - sun_angle)) / 20.0;
268 outer_param[1] = (10.0 - fabs(90.0 - sun_angle)) / 40.0;
269 outer_param[2] = -(10.0 - fabs(90.0 - sun_angle)) / 30.0;
271 middle_param[0] = (10.0 - fabs(90.0 - sun_angle)) / 40.0;
272 middle_param[1] = (10.0 - fabs(90.0 - sun_angle)) / 80.0;
273 middle_param[2] = 0.0;
275 outer_diff = (1.0 / 6.0) * outer_param;
276 middle_diff = (1.0 / 6.0) * middle_param;
278 outer_param = SGVec3f(0, 0, 0);
279 middle_param = SGVec3f(0, 0, 0);
281 outer_diff = SGVec3f(0, 0, 0);
282 middle_diff = SGVec3f(0, 0, 0);
284 // printf(" outer_red_param = %.2f outer_red_diff = %.2f\n",
285 // outer_red_param, outer_red_diff);
287 // calculate transition colors between sky and fog
288 SGVec3f outer_amt = outer_param;
289 SGVec3f middle_amt = middle_param;
292 // First, recalulate the basic colors
295 osg::Vec3 center_color;
296 osg::Vec3 upper_color[12];
297 osg::Vec3 middle_color[12];
298 osg::Vec3 lower_color[12];
299 osg::Vec3 bottom_color[12];
301 double vis_factor, cvf = vis;
306 vis_factor = (vis - 1000.0) / 2000.0;
307 if ( vis_factor < 0.0 ) {
309 } else if ( vis_factor > 1.0) {
313 center_color = sky_color.osg();
315 for ( int i = 0; i < 6; i++ ) {
316 for ( int j = 0; j < 3; j++ ) {
317 double saif = sun_angle/SG_PI;
318 double diff = (sky_color[j] - fog_color[j])
319 * (0.8 + j * 0.2) * (0.8 + saif - ((6-i)/10));
321 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
322 // l->sky_color[j], l->fog_color[j], diff);
324 upper_color[i][j] = sky_color[j] - diff *
325 ( 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000) );
326 middle_color[i][j] = sky_color[j] - diff *
327 ( 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000) ) + middle_amt[j];
328 lower_color[i][j] = fog_color[j] + outer_amt[j];
330 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
331 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
332 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
333 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
334 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
335 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
338 outer_amt -= outer_diff;
339 middle_amt -= middle_diff;
342 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
343 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
344 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
345 middle_color[i][0], middle_color[i][1], middle_color[i][2],
347 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
348 lower_color[i][0], lower_color[i][1], lower_color[i][2],
353 outer_amt = SGVec3f(0, 0, 0);
354 middle_amt = SGVec3f(0, 0, 0);
356 for ( int i = 6; i < 12; i++ ) {
357 for ( int j = 0; j < 3; j++ ) {
358 double saif = sun_angle/SGD_PI;
359 double diff = (sky_color[j] - fog_color[j])
360 * (0.8 + j * 0.2) * (0.8 + saif - ((-i+12)/10));
362 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
363 // sky_color[j], fog_color[j], diff);
365 upper_color[i][j] = sky_color[j] - diff *
366 ( 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000) );
367 middle_color[i][j] = sky_color[j] - diff *
368 ( 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000) ) + middle_amt[j];
369 lower_color[i][j] = fog_color[j] + outer_amt[j];
371 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
372 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
373 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
374 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
375 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
376 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
379 outer_amt += outer_diff;
380 middle_amt += middle_diff;
383 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
384 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
385 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
386 middle_color[i][0], middle_color[i][1], middle_color[i][2],
388 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
389 lower_color[i][0], lower_color[i][1], lower_color[i][2],
394 fade_to_black( ¢er_color, asl * center_elev, 1);
395 fade_to_black( upper_color, (asl+0.05f) * upper_elev, 12);
396 fade_to_black( middle_color, (asl+0.05f) * middle_elev, 12);
397 fade_to_black( lower_color, (asl+0.05f) * lower_elev, 12);
399 for ( int i = 0; i < 12; i++ )
400 bottom_color[i] = fog_color.osg();
403 // Second, assign the basic colors to the object color arrays
406 // update the center disk color arrays
408 (*center_disk_cl)[counter++] = center_color;
409 for ( int i = 11; i >= 0; i-- ) {
410 (*center_disk_cl)[counter++] = upper_color[i];
412 (*center_disk_cl)[counter++] = upper_color[11];
413 center_disk_cl->dirty();
415 // generate the upper ring
417 for ( int i = 0; i < 12; i++ ) {
418 (*upper_ring_cl)[counter++] = middle_color[i];
419 (*upper_ring_cl)[counter++] = upper_color[i];
421 (*upper_ring_cl)[counter++] = middle_color[0];
422 (*upper_ring_cl)[counter++] = upper_color[0];
423 upper_ring_cl->dirty();
425 // generate middle ring
427 for ( int i = 0; i < 12; i++ ) {
428 (*middle_ring_cl)[counter++] = lower_color[i];
429 (*middle_ring_cl)[counter++] = middle_color[i];
431 (*middle_ring_cl)[counter++] = lower_color[0];
432 (*middle_ring_cl)[counter++] = middle_color[0];
433 middle_ring_cl->dirty();
435 // generate lower ring
437 for ( int i = 0; i < 12; i++ ) {
438 (*lower_ring_cl)[counter++] = bottom_color[i];
439 (*lower_ring_cl)[counter++] = lower_color[i];
441 (*lower_ring_cl)[counter++] = bottom_color[0];
442 (*lower_ring_cl)[counter++] = lower_color[0];
443 lower_ring_cl->dirty();
449 // reposition the sky at the specified origin and orientation
450 // lon specifies a rotation about the Z axis
451 // lat specifies a rotation about the new Y axis
452 // spin specifies a rotation about the new Z axis (and orients the
453 // sunrise/set effects
455 SGSkyDome::reposition( const SGVec3f& p, double _asl,
456 double lon, double lat, double spin ) {
459 osg::Matrix T, LON, LAT, SPIN;
461 // Translate to view position
462 // Point3D zero_elev = current_view.get_cur_zero_elev();
463 // xglTranslatef( zero_elev.x(), zero_elev.y(), zero_elev.z() );
464 T.makeTranslate( p.osg() );
466 // printf(" Translated to %.2f %.2f %.2f\n",
467 // zero_elev.x, zero_elev.y, zero_elev.z );
469 // Rotate to proper orientation
470 // printf(" lon = %.2f lat = %.2f\n",
471 // lon * SGD_RADIANS_TO_DEGREES,
472 // lat * SGD_RADIANS_TO_DEGREES);
473 // xglRotatef( lon * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
474 LON.makeRotate(lon, osg::Vec3(0, 0, 1));
476 // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
478 LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
480 // xglRotatef( l->sun_rotation * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
481 SPIN.makeRotate(spin, osg::Vec3(0, 0, 1));
483 dome_transform->setMatrix( SPIN*LAT*LON*T );