]> git.mxchange.org Git - simgear.git/blob - simgear/scene/sky/dome.cxx
Reduce compiler.h to almost nothing (but it's worth keeping around I think, for
[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 #ifdef HAVE_WINDOWS_H
27 #  include <windows.h>
28 #endif
29
30 #include <math.h>
31
32 #include <simgear/compiler.h>
33
34 #include <osg/Array>
35 #include <osg/Geode>
36 #include <osg/Geometry>
37 #include <osg/Node>
38 #include <osg/Math>
39 #include <osg/MatrixTransform>
40 #include <osg/Material>
41 #include <osg/ShadeModel>
42 #include <osg/PrimitiveSet>
43
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/math/Math.hxx>
46 #include <simgear/scene/util/VectorArrayAdapter.hxx>
47
48 #include "dome.hxx"
49
50 using namespace osg;
51 using namespace simgear;
52
53 // proportions of max dimensions fed to the build() routine
54 static const float center_elev = 1.0;
55
56 namespace
57 {
58 struct DomeParam
59 {
60     float radius;
61     float elev;
62 } domeParams[] = {{.5, .8660},  // 60deg from horizon
63                   {.8660, .5},  // 30deg from horizon
64                   // Original dome horizon vertices
65                   {0.9701, 0.2425}, {0.9960, 0.0885},
66                   {1.0, 0.0}, {0.9922, -0.1240}};
67
68 const int numRings = sizeof(domeParams) / sizeof(domeParams[0]);
69 const int numBands = 12;
70 }
71
72 static const float upper_radius = 0.9701; // (.6, 0.15)
73 static const float upper_elev = 0.2425;
74
75 static const float middle_radius = 0.9960; // (.9, .08)
76 static const float middle_elev = 0.0885;
77
78 static const float lower_radius = 1.0;
79 static const float lower_elev = 0.0;
80
81 static const float bottom_radius = 0.9922; // (.8, -.1)
82 static const float bottom_elev = -0.1240;
83
84
85 // Constructor
86 SGSkyDome::SGSkyDome( void ) {
87     asl = 0;
88 }
89
90
91 // Destructor
92 SGSkyDome::~SGSkyDome( void ) {
93 }
94
95 // Generate indices for a dome mesh. Assume a center vertex at 0, then
96 // rings of vertices. Each ring's vertices are stored together. An
97 // even number of longitudinal bands are assumed.
98
99 namespace
100 {
101 // Calculate the index of a vertex in the grid by using its address in
102 // the array that holds its location.
103 struct GridIndex
104 {
105     VectorArrayAdapter<Vec3Array> gridAdapter;
106     Vec3Array& grid;
107     GridIndex(Vec3Array& array, int rowStride, int baseOffset) :
108         gridAdapter(array, rowStride, baseOffset), grid(array)
109     {
110     }
111     unsigned short operator() (int ring, int band)
112     {
113         return (unsigned short)(&gridAdapter(ring, band) - &grid[0]);
114     }
115 };
116 }
117 void SGSkyDome::makeDome(int rings, int bands, DrawElementsUShort& elements)
118 {
119     std::back_insert_iterator<DrawElementsUShort> pusher
120         = std::back_inserter(elements);
121     GridIndex grid(*dome_vl, numBands, 1);
122     for (int i = 0; i < bands; i += 2) {
123         *pusher = 0;  *pusher = grid(0, i);  *pusher = grid(0, i + 1);  
124         // down a band
125         for (int j = 0; j < rings - 1; ++j) {
126             *pusher = grid(j, i);  *pusher = grid(j, i + 1);
127             *pusher = grid(j + 1, i + 1);
128             *pusher = grid(j, i);  *pusher =  grid(j + 1, i + 1);
129             *pusher =  grid(j + 1, i);
130         }
131         // and up the next one
132         for (int j = rings - 1; j > 0; --j) {
133             *pusher = grid(j, i + 1);  *pusher = grid(j - 1, i + 1);
134             *pusher = grid(j, (i + 2) % bands);
135             *pusher = grid(j, (i + 2) % bands); *pusher = grid(j - 1, i + 1);
136             *pusher = grid(j - 1, (i + 2) % bands);
137         }
138         *pusher = grid(0, i + 1);  *pusher = 0;
139         *pusher = grid(0, (i + 2) % bands);
140     }
141 }
142
143 // initialize the sky object and connect it into our scene graph
144 osg::Node*
145 SGSkyDome::build( double hscale, double vscale ) {
146
147     osg::Geode* geode = new osg::Geode;
148
149     // set up the state
150     osg::StateSet* stateSet = geode->getOrCreateStateSet();
151     stateSet->setRenderBinDetails(-10, "RenderBin");
152
153     osg::ShadeModel* shadeModel = new osg::ShadeModel;
154     shadeModel->setMode(osg::ShadeModel::SMOOTH);
155     stateSet->setAttributeAndModes(shadeModel);
156     stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
157     stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
158     stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
159     stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
160     stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
161     stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
162     osg::Material* material = new osg::Material;
163     stateSet->setAttribute(material);
164
165     dome_vl = new osg::Vec3Array(1 + numRings * numBands);
166     dome_cl = new osg::Vec3Array(1 + numRings * numBands);
167     // generate the raw vertex data
168
169     (*dome_vl)[0].set(0.0, 0.0, center_elev * vscale);
170     simgear::VectorArrayAdapter<Vec3Array> vertices(*dome_vl, numBands, 1);
171
172     for ( int i = 0; i < numBands; ++i ) {
173         double theta = (i * 30) * SGD_DEGREES_TO_RADIANS;
174         double sTheta = hscale*sin(theta);
175         double cTheta = hscale*cos(theta);
176         for (int j = 0; j < numRings; ++j) {
177             vertices(j, i).set(cTheta * domeParams[j].radius,
178                                sTheta * domeParams[j].radius,
179                                domeParams[j].elev * vscale);
180         }
181     }
182
183     DrawElementsUShort* domeElements
184         = new osg::DrawElementsUShort(GL_TRIANGLES);
185     makeDome(numRings, numBands, *domeElements);
186     osg::Geometry* geom = new Geometry;
187     geom->setName("Dome Elements");
188     geom->setUseDisplayList(false);
189     geom->setVertexArray(dome_vl.get());
190     geom->setColorArray(dome_cl.get());
191     geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
192     geom->setNormalBinding(osg::Geometry::BIND_OFF);
193     geom->addPrimitiveSet(domeElements);
194     geode->addDrawable(geom);
195     // force a repaint of the sky colors with ugly defaults
196     repaint(SGVec3f(1, 1, 1), SGVec3f(1, 1, 1), 0.0, 5000.0 );
197     dome_transform = new osg::MatrixTransform;
198     dome_transform->addChild(geode);
199
200     return dome_transform.get();
201 }
202
203 static void fade_to_black(osg::Vec3 sky_color[], float asl, int count) {
204     const float ref_asl = 10000.0f;
205     const float d = exp( - asl / ref_asl );
206     for(int i = 0; i < count ; i++)
207         sky_color[i] *= d;
208 }
209
210 inline void clampColor(osg::Vec3& color)
211 {
212     color.x() = osg::clampTo(color.x(), 0.0f, 1.0f);
213     color.y() = osg::clampTo(color.y(), 0.0f, 1.0f);
214     color.z() = osg::clampTo(color.z(), 0.0f, 1.0f);
215 }
216
217 // repaint the sky colors based on current value of sun_angle, sky,
218 // and fog colors.  This updates the color arrays for ssgVtxTable.
219 // sun angle in degrees relative to verticle
220 // 0 degrees = high noon
221 // 90 degrees = sun rise/set
222 // 180 degrees = darkest midnight
223 bool
224 SGSkyDome::repaint( const SGVec3f& sky_color, const SGVec3f& fog_color,
225                     double sun_angle, double vis )
226 {
227     SGVec3f outer_param, outer_diff;
228     SGVec3f middle_param, middle_diff;
229
230     // Check for sunrise/sunset condition
231     if (sun_angle > 80) {
232         // 0.0 - 0.4
233         double sunAngleFactor = 10.0 - fabs(90.0 - sun_angle);
234         static const SGVec3f outerConstant(1.0 / 20.0, 1.0 / 40.0, -1.0 / 30.0);
235         static const SGVec3f middleConstant(1.0 / 40.0, 1.0 / 80.0, 0.0);
236         outer_param = sunAngleFactor * outerConstant;
237         middle_param = sunAngleFactor * middleConstant;
238         outer_diff = (1.0 / 6.0) * outer_param;
239         middle_diff = (1.0 / 6.0) * middle_param;
240     } else {
241         outer_param = SGVec3f(0, 0, 0);
242         middle_param = SGVec3f(0, 0, 0);
243         outer_diff = SGVec3f(0, 0, 0);
244         middle_diff = SGVec3f(0, 0, 0);
245     }
246     // printf("  outer_red_param = %.2f  outer_red_diff = %.2f\n", 
247     //        outer_red_param, outer_red_diff);
248
249     // calculate transition colors between sky and fog
250     SGVec3f outer_amt = outer_param;
251     SGVec3f middle_amt = middle_param;
252
253     //
254     // First, recalulate the basic colors
255     //
256
257     // Magic factors for coloring the sky according visibility and
258     // zenith angle.
259     const double cvf = osg::clampBelow(vis, 45000.0);
260     const double vis_factor = osg::clampTo((vis - 1000.0) / 2000.0, 0.0, 1.0);
261     const float upperVisFactor = 1.0 - vis_factor * (0.7 + 0.3 * cvf/45000);
262     const float middleVisFactor = 1.0 - vis_factor * (0.1 + 0.85 * cvf/45000);
263
264     (*dome_cl)[0] = sky_color.osg();
265     simgear::VectorArrayAdapter<Vec3Array> colors(*dome_cl, numBands, 1);
266     const double saif = sun_angle/SG_PI;
267     static const SGVec3f blueShift(0.8, 1.0, 1.2);
268     const SGVec3f skyFogDelta = sky_color - fog_color;
269     // For now the colors of the upper two rings are linearly
270     // interpolated between the zenith color and the first horizon
271     // ring color.
272     
273     for (int i = 0; i < 7; i++) {
274         SGVec3f diff = mult(skyFogDelta, blueShift);
275         diff *= (0.8 + saif - ((6-i)/10));
276         colors(2, i) = (sky_color - upperVisFactor * diff).osg();
277         colors(3, i) = (sky_color - middleVisFactor * diff + middle_amt).osg();
278         colors(4, i) = (fog_color + outer_amt).osg();
279         // Interpolate using distance along dome segment
280         colors(0, i) = simgear::math::lerp(sky_color.osg(), colors(2, i), .3942);
281         colors(1, i) = simgear::math::lerp(sky_color.osg(), colors(2, i), .7885);
282         for (int j = 0; j < numRings - 1; ++j)
283             clampColor(colors(j, i));
284         outer_amt -= outer_diff;
285         middle_amt -= middle_diff;
286     }
287
288     for (int i = 7; i < 12; ++i)
289         for (int j = 0; j < 5; ++j)
290             colors(j, i) = colors(j, 12 - i);
291
292     fade_to_black(&(*dome_cl)[0], asl * center_elev, 1);
293     for (int i = 0; i < numRings - 1; ++i)
294         fade_to_black(&colors(i, 0), (asl+0.05f) * domeParams[i].elev,
295                       numBands);
296
297     for ( int i = 0; i < numBands; i++ )
298         colors(numRings - 1, i) = fog_color.osg();
299     dome_cl->dirty();
300     return true;
301 }
302
303
304 // reposition the sky at the specified origin and orientation
305 // lon specifies a rotation about the Z axis
306 // lat specifies a rotation about the new Y axis
307 // spin specifies a rotation about the new Z axis (and orients the
308 // sunrise/set effects
309 bool
310 SGSkyDome::reposition( const SGVec3f& p, double _asl,
311                        double lon, double lat, double spin ) {
312     asl = _asl;
313
314     osg::Matrix T, LON, LAT, SPIN;
315
316     // Translate to view position
317     // Point3D zero_elev = current_view.get_cur_zero_elev();
318     // xglTranslatef( zero_elev.x(), zero_elev.y(), zero_elev.z() );
319     T.makeTranslate( p.osg() );
320
321     // printf("  Translated to %.2f %.2f %.2f\n", 
322     //        zero_elev.x, zero_elev.y, zero_elev.z );
323
324     // Rotate to proper orientation
325     // printf("  lon = %.2f  lat = %.2f\n",
326     //        lon * SGD_RADIANS_TO_DEGREES,
327     //        lat * SGD_RADIANS_TO_DEGREES);
328     // xglRotatef( lon * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
329     LON.makeRotate(lon, osg::Vec3(0, 0, 1));
330
331     // xglRotatef( 90.0 - f->get_Latitude() * SGD_RADIANS_TO_DEGREES,
332     //             0.0, 1.0, 0.0 );
333     LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0));
334
335     // xglRotatef( l->sun_rotation * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0 );
336     SPIN.makeRotate(spin, osg::Vec3(0, 0, 1));
337
338     dome_transform->setMatrix( SPIN*LAT*LON*T );
339     return true;
340 }