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/scene/util/SGDebugDrawCallback.hxx>
46 #include <simgear/debug/logstream.hxx>
52 # pragma global_optimizer off
56 // proportions of max dimensions fed to the build() routine
57 static const float center_elev = 1.0;
59 static const float upper_radius = 0.6;
60 static const float upper_elev = 0.15;
62 static const float middle_radius = 0.9;
63 static const float middle_elev = 0.08;
65 static const float lower_radius = 1.0;
66 static const float lower_elev = 0.0;
68 static const float bottom_radius = 0.8;
69 static const float bottom_elev = -0.1;
73 SGSkyDome::SGSkyDome( void ) {
79 SGSkyDome::~SGSkyDome( void ) {
83 // initialize the sky object and connect it into our scene graph
85 SGSkyDome::build( double hscale, double vscale ) {
87 osg::Geode* geode = new osg::Geode;
90 osg::StateSet* stateSet = geode->getOrCreateStateSet();
91 stateSet->setRenderBinDetails(-10, "RenderBin");
93 osg::ShadeModel* shadeModel = new osg::ShadeModel;
94 shadeModel->setMode(osg::ShadeModel::SMOOTH);
95 stateSet->setAttributeAndModes(shadeModel);
96 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
97 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
98 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
99 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
100 stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
101 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
102 osg::Material* material = new osg::Material;
103 // material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
104 // material->setEmission(osg::Material::FRONT_AND_BACK,
105 // osg::Vec4(0, 0, 0, 1));
106 // material->setSpecular(osg::Material::FRONT_AND_BACK,
107 // osg::Vec4(0, 0, 0, 1));
108 // material->setShininess(osg::Material::FRONT_AND_BACK, 0);
109 stateSet->setAttribute(material);
110 // stateSet->setMode(GL_COLOR_MATERIAL, osg::StateAttribute::OFF);
113 // initially seed to all blue
114 center_disk_vl = new osg::Vec3Array;
115 center_disk_cl = new osg::Vec3Array;
116 center_disk_cl->assign(14, osg::Vec3(0, 0, 1));
118 upper_ring_vl = new osg::Vec3Array;
119 upper_ring_cl = new osg::Vec3Array;
120 upper_ring_cl->assign(26, osg::Vec3(0, 0, 1));
122 middle_ring_vl = new osg::Vec3Array;
123 middle_ring_cl = new osg::Vec3Array;
124 middle_ring_cl->assign(26, osg::Vec3(0, 0, 1));
126 lower_ring_vl = new osg::Vec3Array;
127 lower_ring_cl = new osg::Vec3Array;
128 lower_ring_cl->assign(26, osg::Vec3(0, 0, 1));
131 // generate the raw vertex data
132 osg::Vec3 center_vertex(0.0, 0.0, center_elev*vscale);
133 osg::Vec3 upper_vertex[12];
134 osg::Vec3 middle_vertex[12];
135 osg::Vec3 lower_vertex[12];
136 osg::Vec3 bottom_vertex[12];
138 for ( int i = 0; i < 12; ++i ) {
139 double theta = (i * 30) * SGD_DEGREES_TO_RADIANS;
140 double sTheta = hscale*sin(theta);
141 double cTheta = hscale*cos(theta);
143 upper_vertex[i] = osg::Vec3(cTheta * upper_radius,
144 sTheta * upper_radius,
145 upper_elev * vscale);
147 middle_vertex[i] = osg::Vec3(cTheta * middle_radius,
148 sTheta * middle_radius,
149 middle_elev * vscale);
151 lower_vertex[i] = osg::Vec3(cTheta * lower_radius,
152 sTheta * lower_radius,
153 lower_elev * vscale);
155 bottom_vertex[i] = osg::Vec3(cTheta * bottom_radius,
156 sTheta * bottom_radius,
157 bottom_elev * vscale);
160 // generate the center disk vertex/color arrays
161 center_disk_vl->push_back(center_vertex);
162 for ( int i = 11; i >= 0; --i )
163 center_disk_vl->push_back(upper_vertex[i]);
164 center_disk_vl->push_back(upper_vertex[11]);
166 // generate the upper ring
167 for ( int i = 0; i < 12; ++i ) {
168 upper_ring_vl->push_back( middle_vertex[i] );
169 upper_ring_vl->push_back( upper_vertex[i] );
171 upper_ring_vl->push_back( middle_vertex[0] );
172 upper_ring_vl->push_back( upper_vertex[0] );
174 // generate middle ring
175 for ( int i = 0; i < 12; i++ ) {
176 middle_ring_vl->push_back( lower_vertex[i] );
177 middle_ring_vl->push_back( middle_vertex[i] );
179 middle_ring_vl->push_back( lower_vertex[0] );
180 middle_ring_vl->push_back( middle_vertex[0] );
182 // generate lower ring
183 for ( int i = 0; i < 12; i++ ) {
184 lower_ring_vl->push_back( bottom_vertex[i] );
185 lower_ring_vl->push_back( lower_vertex[i] );
187 lower_ring_vl->push_back( bottom_vertex[0] );
188 lower_ring_vl->push_back( lower_vertex[0] );
190 // force a repaint of the sky colors with ugly defaults
191 repaint(SGVec3f(1, 1, 1), SGVec3f(1, 1, 1), 0.0, 5000.0 );
193 // build the ssg scene graph sub tree for the sky and connected
194 // into the provide scene graph branch
195 osg::Geometry* geometry = new osg::Geometry;
196 geometry->setName("Dome Center");
197 // geometry->setDrawCallback(new SGDebugDrawCallback);
198 geometry->setUseDisplayList(false);
199 geometry->setVertexArray(center_disk_vl.get());
200 geometry->setColorArray(center_disk_cl.get());
201 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
202 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
203 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_FAN, 0, 14));
204 geode->addDrawable(geometry);
206 geometry = new osg::Geometry;
207 geometry->setName("Dome Upper Ring");
208 // geometry->setDrawCallback(new SGDebugDrawCallback);
209 geometry->setUseDisplayList(false);
210 geometry->setVertexArray(upper_ring_vl.get());
211 geometry->setColorArray(upper_ring_cl.get());
212 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
213 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
214 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
215 geode->addDrawable(geometry);
217 geometry = new osg::Geometry;
218 geometry->setName("Dome Middle Ring");
219 // geometry->setDrawCallback(new SGDebugDrawCallback);
220 geometry->setUseDisplayList(false);
221 geometry->setVertexArray(middle_ring_vl.get());
222 geometry->setColorArray(middle_ring_cl.get());
223 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
224 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
225 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
226 geode->addDrawable(geometry);
228 geometry = new osg::Geometry;
229 geometry->setName("Dome Lower Ring");
230 // geometry->setDrawCallback(new SGDebugDrawCallback);
231 geometry->setUseDisplayList(false);
232 geometry->setVertexArray(lower_ring_vl.get());
233 geometry->setColorArray(lower_ring_cl.get());
234 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
235 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
236 geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 26));
237 geode->addDrawable(geometry);
239 dome_transform = new osg::MatrixTransform;
240 dome_transform->addChild(geode);
242 return dome_transform.get();
245 static void fade_to_black(osg::Vec3 sky_color[], float asl, int count) {
246 const float ref_asl = 10000.0f;
247 float d = exp( - asl / ref_asl );
248 for(int i = 0; i < count ; i++) {
250 sky_color[i][0] = sky_color[i][0] - f * sky_color[i][0] ;
251 sky_color[i][1] = sky_color[i][1] - f * sky_color[i][1] ;
252 sky_color[i][2] = sky_color[i][2] - f * sky_color[i][2] ;
256 // repaint the sky colors based on current value of sun_angle, sky,
257 // and fog colors. This updates the color arrays for ssgVtxTable.
258 // sun angle in degrees relative to verticle
259 // 0 degrees = high noon
260 // 90 degrees = sun rise/set
261 // 180 degrees = darkest midnight
263 SGSkyDome::repaint( const SGVec3f& sky_color, const SGVec3f& fog_color,
264 double sun_angle, double vis )
266 SGVec3f outer_param, outer_diff;
267 SGVec3f middle_param, middle_diff;
269 // Check for sunrise/sunset condition
273 outer_param[0] = (10.0 - fabs(90.0 - sun_angle)) / 20.0;
274 outer_param[1] = (10.0 - fabs(90.0 - sun_angle)) / 40.0;
275 outer_param[2] = -(10.0 - fabs(90.0 - sun_angle)) / 30.0;
277 middle_param[0] = (10.0 - fabs(90.0 - sun_angle)) / 40.0;
278 middle_param[1] = (10.0 - fabs(90.0 - sun_angle)) / 80.0;
279 middle_param[2] = 0.0;
281 outer_diff = (1.0 / 6.0) * outer_param;
282 middle_diff = (1.0 / 6.0) * middle_param;
284 outer_param = SGVec3f(0, 0, 0);
285 middle_param = SGVec3f(0, 0, 0);
287 outer_diff = SGVec3f(0, 0, 0);
288 middle_diff = SGVec3f(0, 0, 0);
290 // printf(" outer_red_param = %.2f outer_red_diff = %.2f\n",
291 // outer_red_param, outer_red_diff);
293 // calculate transition colors between sky and fog
294 SGVec3f outer_amt = outer_param;
295 SGVec3f middle_amt = middle_param;
298 // First, recalulate the basic colors
301 osg::Vec3 center_color;
302 osg::Vec3 upper_color[12];
303 osg::Vec3 middle_color[12];
304 osg::Vec3 lower_color[12];
305 osg::Vec3 bottom_color[12];
307 double vis_factor, cvf = vis;
312 vis_factor = (vis - 1000.0) / 2000.0;
313 if ( vis_factor < 0.0 ) {
315 } else if ( vis_factor > 1.0) {
319 center_color = sky_color.osg();
321 for ( int i = 0; i < 6; i++ ) {
322 for ( int j = 0; j < 3; j++ ) {
323 double saif = sun_angle/SG_PI;
324 double diff = (sky_color[j] - fog_color[j])
325 * (0.8 + j * 0.2) * (0.8 + saif - ((6-i)/10));
327 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
328 // l->sky_color[j], l->fog_color[j], diff);
330 upper_color[i][j] = sky_color[j] - diff *
331 ( 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000) );
332 middle_color[i][j] = sky_color[j] - diff *
333 ( 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000) ) + middle_amt[j];
334 lower_color[i][j] = fog_color[j] + outer_amt[j];
336 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
337 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
338 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
339 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
340 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
341 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
344 outer_amt -= outer_diff;
345 middle_amt -= middle_diff;
348 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
349 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
350 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
351 middle_color[i][0], middle_color[i][1], middle_color[i][2],
353 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
354 lower_color[i][0], lower_color[i][1], lower_color[i][2],
359 outer_amt = SGVec3f(0, 0, 0);
360 middle_amt = SGVec3f(0, 0, 0);
362 for ( int i = 6; i < 12; i++ ) {
363 for ( int j = 0; j < 3; j++ ) {
364 double saif = sun_angle/SGD_PI;
365 double diff = (sky_color[j] - fog_color[j])
366 * (0.8 + j * 0.2) * (0.8 + saif - ((-i+12)/10));
368 // printf("sky = %.2f fog = %.2f diff = %.2f\n",
369 // sky_color[j], fog_color[j], diff);
371 upper_color[i][j] = sky_color[j] - diff *
372 ( 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000) );
373 middle_color[i][j] = sky_color[j] - diff *
374 ( 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000) ) + middle_amt[j];
375 lower_color[i][j] = fog_color[j] + outer_amt[j];
377 if ( upper_color[i][j] > 1.0 ) { upper_color[i][j] = 1.0; }
378 if ( upper_color[i][j] < 0.0 ) { upper_color[i][j] = 0.0; }
379 if ( middle_color[i][j] > 1.0 ) { middle_color[i][j] = 1.0; }
380 if ( middle_color[i][j] < 0.0 ) { middle_color[i][j] = 0.0; }
381 if ( lower_color[i][j] > 1.0 ) { lower_color[i][j] = 1.0; }
382 if ( lower_color[i][j] < 0.0 ) { lower_color[i][j] = 0.0; }
385 outer_amt += outer_diff;
386 middle_amt += middle_diff;
389 printf("upper_color[%d] = %.2f %.2f %.2f %.2f\n", i, upper_color[i][0],
390 upper_color[i][1], upper_color[i][2], upper_color[i][3]);
391 printf("middle_color[%d] = %.2f %.2f %.2f %.2f\n", i,
392 middle_color[i][0], middle_color[i][1], middle_color[i][2],
394 printf("lower_color[%d] = %.2f %.2f %.2f %.2f\n", i,
395 lower_color[i][0], lower_color[i][1], lower_color[i][2],
400 fade_to_black( ¢er_color, asl * center_elev, 1);
401 fade_to_black( upper_color, (asl+0.05f) * upper_elev, 12);
402 fade_to_black( middle_color, (asl+0.05f) * middle_elev, 12);
403 fade_to_black( lower_color, (asl+0.05f) * lower_elev, 12);
405 for ( int i = 0; i < 12; i++ )
406 bottom_color[i] = fog_color.osg();
409 // Second, assign the basic colors to the object color arrays
412 // update the center disk color arrays
414 (*center_disk_cl)[counter++] = center_color;
415 for ( int i = 11; i >= 0; i-- ) {
416 (*center_disk_cl)[counter++] = upper_color[i];
418 (*center_disk_cl)[counter++] = upper_color[11];
419 center_disk_cl->dirty();
421 // generate the upper ring
423 for ( int i = 0; i < 12; i++ ) {
424 (*upper_ring_cl)[counter++] = middle_color[i];
425 (*upper_ring_cl)[counter++] = upper_color[i];
427 (*upper_ring_cl)[counter++] = middle_color[0];
428 (*upper_ring_cl)[counter++] = upper_color[0];
429 upper_ring_cl->dirty();
431 // generate middle ring
433 for ( int i = 0; i < 12; i++ ) {
434 (*middle_ring_cl)[counter++] = lower_color[i];
435 (*middle_ring_cl)[counter++] = middle_color[i];
437 (*middle_ring_cl)[counter++] = lower_color[0];
438 (*middle_ring_cl)[counter++] = middle_color[0];
439 middle_ring_cl->dirty();
441 // generate lower ring
443 for ( int i = 0; i < 12; i++ ) {
444 (*lower_ring_cl)[counter++] = bottom_color[i];
445 (*lower_ring_cl)[counter++] = lower_color[i];
447 (*lower_ring_cl)[counter++] = bottom_color[0];
448 (*lower_ring_cl)[counter++] = lower_color[0];
449 lower_ring_cl->dirty();
455 // reposition the sky at the specified origin and orientation
456 // lon specifies a rotation about the Z axis
457 // lat specifies a rotation about the new Y axis
458 // spin specifies a rotation about the new Z axis (and orients the
459 // sunrise/set effects
461 SGSkyDome::reposition( const SGVec3f& p, double _asl,
462 double lon, double lat, double spin ) {
465 osg::Matrix T, LON, LAT, SPIN;
467 // Translate to view position
468 // Point3D zero_elev = current_view.get_cur_zero_elev();
469 // xglTranslatef( zero_elev.x(), zero_elev.y(), zero_elev.z() );
470 T.makeTranslate( p.osg() );
472 // printf(" Translated to %.2f %.2f %.2f\n",
473 // zero_elev.x, zero_elev.y, zero_elev.z );
475 // Rotate to proper orientation
476 // printf(" lon = %.2f lat = %.2f\n",
477 // lon * SGD_RADIANS_TO_DEGREES,
478 // lat * SGD_RADIANS_TO_DEGREES);
479 // xglRotatef( lon * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
480 LON.makeRotate(lon, osg::Vec3(0, 0, 1));
482 // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
484 LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
486 // xglRotatef( l->sun_rotation * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
487 SPIN.makeRotate(spin, osg::Vec3(0, 0, 1));
489 dome_transform->setMatrix( SPIN*LAT*LON*T );