]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/dome.cxx
Added some OSG headers for the correct evaluation of the OSG_VERSION_LESS_THAN macro.
[simgear.git] / simgear / scene / sky / dome.cxx
1 // dome.cxx -- model sky with an upside down "bowl"
2 //
3 // Written by Curtis Olson, started December 1997.
4 // SSG-ified by Curtis Olson, February 2000.
5 //
6 // Copyright (C) 1997-2000  Curtis L. Olson  - http://www.flightgear.org/~curt
7 //
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.
12 //
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.
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, MA  02110-1301, USA.
21
22 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include <math.h>
27 #include <iterator>
28
29 #include <simgear/compiler.h>
30
31 #include <osg/Array>
32 #include <osg/Geode>
33 #include <osg/Geometry>
34 #include <osg/Node>
35 #include <osg/Math>
36 #include <osg/MatrixTransform>
37 #include <osg/Material>
38 #include <osg/ShadeModel>
39 #include <osg/PrimitiveSet>
40 #include <osg/CullFace>
41 #include <osgDB/Registry>
42
43 #include <simgear/debug/logstream.hxx>
44 #include <simgear/scene/util/OsgMath.hxx>
45 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
46 #include <simgear/scene/util/VectorArrayAdapter.hxx>
47 #include <simgear/scene/material/Effect.hxx>
48 #include <simgear/scene/material/EffectGeode.hxx>
49
50 #include "dome.hxx"
51
52 using namespace osg;
53 using namespace simgear;
54
55 namespace
56 {
57   // proportions of max dimensions fed to the build() routine
58   const float center_elev = 1.0;
59
60   const int numRings = 64; //sizeof(domeParams) / sizeof(domeParams[0]);
61   const int numBands = 64; // 12
62   const int halfBands = numBands / 2;
63
64   // Make dome a bit over half sphere
65   const float domeAngle = 120.0;
66
67   const float bandDelta = 360.0 / numBands;
68   const float ringDelta = domeAngle / (numRings+1);
69
70   // Which band is at horizon
71   const int halfRings = numRings * (90.0 / domeAngle);
72   const int upperRings = numRings * (60.0 / domeAngle); // top half
73   const int middleRings = numRings * (15.0 / domeAngle);
74 }
75
76 // Constructor
77 SGSkyDome::SGSkyDome( void ) {
78     asl = 0;
79 }
80
81
82 // Destructor
83 SGSkyDome::~SGSkyDome( void ) {
84 }
85
86 // Generate indices for a dome mesh. Assume a center vertex at 0, then
87 // rings of vertices. Each ring's vertices are stored together. An
88 // even number of longitudinal bands are assumed.
89
90 namespace
91 {
92 // Calculate the index of a vertex in the grid by using its address in
93 // the array that holds its location.
94 struct GridIndex
95 {
96     VectorArrayAdapter<Vec3Array> gridAdapter;
97     Vec3Array& grid;
98     GridIndex(Vec3Array& array, int rowStride, int baseOffset) :
99         gridAdapter(array, rowStride, baseOffset), grid(array)
100     {
101     }
102     unsigned short operator() (int ring, int band)
103     {
104         return (unsigned short)(&gridAdapter(ring, band) - &grid[0]);
105     }
106 };
107 }
108 void SGSkyDome::makeDome(int rings, int bands, DrawElementsUShort& elements)
109 {
110     std::back_insert_iterator<DrawElementsUShort> pusher
111         = std::back_inserter(elements);
112     GridIndex grid(*dome_vl, numBands, 1);
113     for (int i = 0; i < bands; i++) {
114         *pusher = 0;  *pusher = grid(0, (i+1)%bands);  *pusher = grid(0, i);
115         // down a band
116         for (int j = 0; j < rings - 1; ++j) {
117             *pusher = grid(j, i);  *pusher = grid(j, (i + 1)%bands);
118             *pusher = grid(j + 1, (i + 1)%bands);
119             *pusher = grid(j, i);  *pusher =  grid(j + 1, (i + 1)%bands);
120             *pusher =  grid(j + 1, i);
121         }
122     }
123 }
124
125 // initialize the sky object and connect it into our scene graph
126 osg::Node*
127 SGSkyDome::build( double hscale, double vscale, simgear::SGReaderWriterOptions *options ) {
128
129     EffectGeode* geode = new EffectGeode;
130     geode->setName("Skydome");
131     geode->setCullingActive(false); // Prevent skydome from being culled away
132
133     Effect *effect = makeEffect("Effects/skydome", true, options);
134     if(effect)
135       geode->setEffect(effect);
136
137     // set up the state
138     osg::StateSet* stateSet = geode->getOrCreateStateSet();
139     stateSet->setRenderBinDetails(-10, "RenderBin");
140
141     osg::ShadeModel* shadeModel = new osg::ShadeModel;
142     shadeModel->setMode(osg::ShadeModel::SMOOTH);
143     stateSet->setAttributeAndModes(shadeModel);
144     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
145     stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
146     stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
147     stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
148     stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
149     stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
150
151     stateSet->setAttribute(new osg::CullFace(osg::CullFace::BACK));
152
153     osg::Material* material = new osg::Material;
154     stateSet->setAttribute(material);
155
156     dome_vl = new osg::Vec3Array(1 + numRings * numBands);
157     dome_cl = new osg::Vec3Array(1 + numRings * numBands);
158     // generate the raw vertex data
159
160     (*dome_vl)[0].set(0.0, 0.0, center_elev * vscale);
161     simgear::VectorArrayAdapter<Vec3Array> vertices(*dome_vl, numBands, 1);
162
163     for ( int i = 0; i < numBands; ++i ) {
164         double theta = (i * bandDelta) * SGD_DEGREES_TO_RADIANS;
165         double sTheta = hscale*sin(theta);
166         double cTheta = hscale*cos(theta);
167         for (int j = 0; j < numRings; ++j) {
168             vertices(j, i).set(cTheta * sin((j+1)*ringDelta*SGD_DEGREES_TO_RADIANS), //domeParams[j].radius,
169                                sTheta * sin((j+1)*ringDelta*SGD_DEGREES_TO_RADIANS),// domeParams[j].radius,
170                                vscale * cos((j+1)*ringDelta*SGD_DEGREES_TO_RADIANS)); //domeParams[j].elev * vscale);
171         }
172     }
173
174     DrawElementsUShort* domeElements
175         = new osg::DrawElementsUShort(GL_TRIANGLES);
176     makeDome(numRings, numBands, *domeElements);
177     osg::Geometry* geom = new Geometry;
178     geom->setName("Dome Elements");
179     geom->setUseDisplayList(false);
180     geom->setVertexArray(dome_vl.get());
181     geom->setColorArray(dome_cl.get(), osg::Array::BIND_PER_VERTEX);
182     geom->setNormalBinding(osg::Geometry::BIND_OFF);
183     geom->addPrimitiveSet(domeElements);
184     geode->addDrawable(geom);
185     // force a repaint of the sky colors with ugly defaults
186     repaint(SGVec3f(1, 1, 1), SGVec3f(1, 1, 1), SGVec3f(1, 1, 1), 0.0, 5000.0 );
187     dome_transform = new osg::MatrixTransform;
188     dome_transform->addChild(geode);
189
190     return dome_transform.get();
191 }
192
193 static void fade_to_black(osg::Vec3 sky_color[], float asl, int count) {
194     const float ref_asl = 10000.0f;
195     const float d = exp( - asl / ref_asl );
196     for(int i = 0; i < count ; i++)
197         sky_color[i] *= d;
198 }
199
200 static inline void clampColor(osg::Vec3& color)
201 {
202     color.x() = osg::clampTo(color.x(), 0.0f, 1.0f);
203     color.y() = osg::clampTo(color.y(), 0.0f, 1.0f);
204     color.z() = osg::clampTo(color.z(), 0.0f, 1.0f);
205 }
206
207 // repaint the sky colors based on current value of sun_angle, sky,
208 // and fog colors.  This updates the color arrays for ssgVtxTable.
209 // sun angle in degrees relative to verticle
210 // 0 degrees = high noon
211 // 90 degrees = sun rise/set
212 // 180 degrees = darkest midnight
213 bool
214 SGSkyDome::repaint( const SGVec3f& sun_color, const SGVec3f& sky_color,
215                     const SGVec3f& fog_color, double sun_angle, double vis )
216 {
217     SGVec3f outer_param, outer_diff;
218     SGVec3f middle_param, middle_diff;
219
220     // Check for sunrise/sunset condition
221     if (sun_angle > 80) {
222         // 0.0 - 0.4
223         double sunAngleFactor = 10.0 - fabs(90.0 - sun_angle);
224         static const SGVec3f outerConstant(1.0 / 20.0, 1.0 / 40.0, -1.0 / 30.0);
225         static const SGVec3f middleConstant(1.0 / 40.0, 1.0 / 80.0, 0.0);
226         outer_param = sunAngleFactor * outerConstant;
227         middle_param = sunAngleFactor * middleConstant;
228         outer_diff = (1.0 / numRings) * outer_param;
229         middle_diff = (1.0 / numRings) * middle_param;
230     } else {
231         outer_param = SGVec3f(0, 0, 0);
232         middle_param = SGVec3f(0, 0, 0);
233         outer_diff = SGVec3f(0, 0, 0);
234         middle_diff = SGVec3f(0, 0, 0);
235     }
236     // printf("  outer_red_param = %.2f  outer_red_diff = %.2f\n",
237     //        outer_red_param, outer_red_diff);
238
239     // calculate transition colors between sky and fog
240     SGVec3f outer_amt = outer_param;
241     SGVec3f middle_amt = middle_param;
242
243     //
244     // First, recalulate the basic colors
245     //
246
247     // Magic factors for coloring the sky according visibility and
248     // zenith angle.
249     const double cvf = osg::clampBelow(vis, 45000.0);
250     const double vis_factor = osg::clampTo((vis - 1000.0) / 2000.0, 0.0, 1.0);
251     const float upperVisFactor = 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000);
252     const float middleVisFactor = 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000);
253
254     // Dome top is always sky_color
255     (*dome_cl)[0] = toOsg(sky_color);
256     simgear::VectorArrayAdapter<Vec3Array> colors(*dome_cl, numBands, 1);
257     const double saif = sun_angle/SG_PI;
258     static const SGVec3f blueShift(0.8, 1.0, 1.2);
259     const SGVec3f skyFogDelta = sky_color - fog_color;
260 //    const SGVec3f sunSkyDelta = sun_color - sky_color;
261
262     // For now the colors of the upper two rings are linearly
263     // interpolated between the zenith color and the first horizon
264     // ring color. Means angles from top to 30 degrees
265
266     for (int i = 0; i < halfBands+1; i++) {
267         SGVec3f diff = mult(skyFogDelta, blueShift);
268         diff *= (0.8 + saif - ((halfBands-i)/(float)(numBands-2)));
269
270         // Color the ~60 deg ring
271         colors(upperRings, i) = toOsg(sky_color - upperVisFactor * diff);
272
273         int j=0;
274         // Color top half by linear interpolation (90...60 degrees)
275         for (; j < upperRings; j++)
276             colors(j, i) = SGMiscf::lerp(toOsg(sky_color), colors(upperRings, i), j / (float)upperRings);
277
278         j++; // Skip the 60 deg ring
279         // From 60 to ~85 degrees
280         for (int l = 0; j < upperRings + middleRings + 1; j++, l++)
281             colors(j, i) = SGMiscf::lerp(colors(upperRings, i),
282                        toOsg(sky_color - middleVisFactor * diff + middle_amt), l / (float)middleRings);
283
284         // 85 to 90 degrees
285         for (int l = 0; j < halfRings; j++, l++)
286             colors(j, i) = SGMiscf::lerp(colors(upperRings + middleRings, i), toOsg(fog_color + outer_amt),
287                         l / (float)(halfRings - upperRings - middleRings));
288
289         // Original colors
290         //colors(2, i) = toOsg(sky_color - upperVisFactor * diff);
291         //colors(3, i) = toOsg(sky_color - middleVisFactor * diff + middle_amt);
292         //colors(4, i) = toOsg(fog_color + outer_amt);
293         //colors(0, i) = simgear::math::lerp(toOsg(sky_color), colors(2, i), .3942);
294         //colors(1, i) = simgear::math::lerp(toOsg(sky_color), colors(2, i), .7885);
295
296         for (int j = 0; j < numRings - 1; ++j)
297             clampColor(colors(j, i));
298
299         outer_amt -= outer_diff;
300         middle_amt -= middle_diff;
301     }
302
303     // Other side of dome is mirror of the other
304     for (int i = halfBands+1; i < numBands; ++i)
305         for (int j = 0; j < numRings-1; ++j)
306             colors(j, i) = colors(j, numBands - i);
307
308     // Fade colors to black when going to space
309     // Center of dome is blackest and then fade decreases towards horizon
310     fade_to_black(&(*dome_cl)[0], asl * center_elev, 1);
311     for (int i = 0; i < numRings - 1; ++i) {
312         float fadeValue = (asl+0.05f) * cos(i*ringDelta*SGD_DEGREES_TO_RADIANS);
313         if(fadeValue < 0.0) fadeValue = 0.0; // Prevent brightening up if dome is over 90 degrees
314         fade_to_black(&colors(i, 0), fadeValue, //domeParams[i].elev,
315                       numBands);
316     }
317
318     // All rings below horizon are fog color
319     for ( int i = halfRings; i < numRings; i++)
320         for ( int j = 0; j < numBands; j++ )
321             colors(i, j) = toOsg(fog_color);
322
323     dome_cl->dirty();
324     return true;
325 }
326
327
328 // reposition the sky at the specified origin and orientation
329 // lon specifies a rotation about the Z axis
330 // lat specifies a rotation about the new Y axis
331 // spin specifies a rotation about the new Z axis (and orients the
332 // sunrise/set effects
333 bool
334 SGSkyDome::reposition( const SGVec3f& p, double _asl,
335                        double lon, double lat, double spin ) {
336     asl = _asl;
337
338     osg::Matrix T, LON, LAT, SPIN;
339
340     // Translate to view position
341     // Point3D zero_elev = current_view.get_cur_zero_elev();
342     // xglTranslatef( zero_elev.x(), zero_elev.y(), zero_elev.z() );
343     T.makeTranslate( toOsg(p) );
344
345     // printf("  Translated to %.2f %.2f %.2f\n",
346     //        zero_elev.x, zero_elev.y, zero_elev.z );
347
348     // Rotate to proper orientation
349     // printf("  lon = %.2f  lat = %.2f\n",
350     //        lon * SGD_RADIANS_TO_DEGREES,
351     //        lat * SGD_RADIANS_TO_DEGREES);
352     // xglRotatef( lon * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
353     LON.makeRotate(lon, osg::Vec3(0, 0, 1));
354
355     // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
356     //             0.0, 1.0, 0.0 );
357     LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
358
359     // xglRotatef( l->sun_rotation * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
360     SPIN.makeRotate(spin, osg::Vec3(0, 0, 1));
361
362     dome_transform->setMatrix( SPIN*LAT*LON*T );
363     return true;
364 }