]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/obj.cxx
Modified Files:
[simgear.git] / simgear / scene / tgdb / obj.cxx
1 // obj.cxx -- routines to handle loading scenery and building the plib
2 //            scene graph.
3 //
4 // Written by Curtis Olson, started October 1997.
5 //
6 // Copyright (C) 1997  Curtis L. Olson  - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // 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 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include <list>
32
33 #include STL_STRING
34
35 #include <osg/Depth>
36 #include <osg/Fog>
37 #include <osg/Geode>
38 #include <osg/Geometry>
39 #include <osg/Group>
40 #include <osg/LOD>
41 #include <osg/MatrixTransform>
42 #include <osg/StateSet>
43
44 #include <osgUtil/Optimizer>
45 #include <osgDB/WriteFile>
46
47 #include <simgear/bucket/newbucket.hxx>
48 #include <simgear/io/sg_binobj.hxx>
49 #include <simgear/math/sg_geodesy.hxx>
50 #include <simgear/math/sg_random.h>
51 #include <simgear/math/sg_types.hxx>
52 #include <simgear/misc/texcoord.hxx>
53 #include <simgear/scene/material/mat.hxx>
54 #include <simgear/scene/material/matlib.hxx>
55 #include <simgear/scene/util/SGUpdateVisitor.hxx>
56 #include <simgear/scene/util/SGNodeMasks.hxx>
57 #include <simgear/scene/tgdb/leaf.hxx>
58 #include <simgear/scene/tgdb/pt_lights.hxx>
59 #include <simgear/scene/tgdb/userdata.hxx>
60
61 #include "obj.hxx"
62
63 SG_USING_STD(string);
64 SG_USING_STD(list);
65
66 struct Leaf {
67     GLenum type;
68     int index;
69 };
70
71
72 // Generate an ocean tile
73 bool SGGenTile( const string& path, const SGBucket& b,
74                 SGMaterialLib *matlib, osg::Group* group )
75 {
76     osg::StateSet *state = 0;
77
78     double tex_width = 1000.0;
79
80     // find Ocean material in the properties list
81     SGMaterial *mat = matlib->find( "Ocean" );
82     if ( mat != NULL ) {
83         // set the texture width and height values for this
84         // material
85         tex_width = mat->get_xsize();
86         
87         // set ssgState
88         state = mat->get_state();
89     } else {
90         SG_LOG( SG_TERRAIN, SG_ALERT, 
91                 "Ack! unknown usemtl name = " << "Ocean" 
92                 << " in " << path );
93     }
94
95     // Calculate center point
96     SGVec3d cartCenter = SGVec3d::fromGeod(b.get_center());
97     Point3D center = Point3D(cartCenter[0], cartCenter[1], cartCenter[2]);
98
99     double clon = b.get_center_lon();
100     double clat = b.get_center_lat();
101     double height = b.get_height();
102     double width = b.get_width();
103
104     // Caculate corner vertices
105     Point3D geod[4];
106     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
107     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
108     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
109     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
110
111     Point3D rad[4];
112     int i;
113     for ( i = 0; i < 4; ++i ) {
114         rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
115                           geod[i].y() * SGD_DEGREES_TO_RADIANS,
116                           geod[i].z() );
117     }
118
119     Point3D cart[4], rel[4];
120     for ( i = 0; i < 4; ++i ) {
121         cart[i] = sgGeodToCart(rad[i]);
122         rel[i] = cart[i] - center;
123         // cout << "corner " << i << " = " << cart[i] << endl;
124     }
125
126     // Calculate normals
127     Point3D normals[4];
128     for ( i = 0; i < 4; ++i ) {
129         double length = cart[i].distance3D( Point3D(0.0) );
130         normals[i] = cart[i] / length;
131         // cout << "normal = " << normals[i] << endl;
132     }
133
134     // Calculate texture coordinates
135     point_list geod_nodes;
136     geod_nodes.clear();
137     geod_nodes.reserve(4);
138     int_list rectangle;
139     rectangle.clear();
140     rectangle.reserve(4);
141     for ( i = 0; i < 4; ++i ) {
142         geod_nodes.push_back( geod[i] );
143         rectangle.push_back( i );
144     }
145     point_list texs = sgCalcTexCoords( b, geod_nodes, rectangle, 
146                                        1000.0 / tex_width );
147
148     // Allocate ssg structure
149     osg::Vec3Array *vl = new osg::Vec3Array;
150     osg::Vec3Array *nl = new osg::Vec3Array;
151     osg::Vec2Array *tl = new osg::Vec2Array;
152
153     for ( i = 0; i < 4; ++i ) {
154         vl->push_back(osg::Vec3(rel[i].x(), rel[i].y(), rel[i].z()));
155         nl->push_back(osg::Vec3(normals[i].x(), normals[i].y(), normals[i].z()));
156         tl->push_back(osg::Vec2(texs[i].x(), texs[i].y()));
157     }
158     
159     osg::Vec4Array* cl = new osg::Vec4Array;
160     cl->push_back(osg::Vec4(1, 1, 1, 1));
161
162     osg::Geometry* geometry = new osg::Geometry;
163     geometry->setVertexArray(vl);
164     geometry->setNormalArray(nl);
165     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
166     geometry->setColorArray(cl);
167     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
168     geometry->setTexCoordArray(0, tl);
169     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_FAN, 0, vl->size()));
170     osg::Geode* geode = new osg::Geode;
171     geode->setName(path);
172     geode->addDrawable(geometry);
173     geode->setStateSet(state);
174
175     group->addChild(geode);
176
177     return true;
178 }
179
180
181 /**
182  * SSG callback for an in-range leaf of randomly-placed objects.
183  *
184  * This pretraversal callback is attached to a branch that is
185  * traversed only when a leaf is in range.  If the leaf is not
186  * currently prepared to be populated with randomly-placed objects,
187  * this callback will prepare it (actual population is handled by
188  * the tri_in_range_callback for individual triangles).
189  *
190  * @param entity The entity to which the callback is attached (not used).
191  * @param mask The entity's traversal mask (not used).
192  * @return Always 1, to allow traversal and culling to continue.
193  */
194 // static int
195 // leaf_in_range_callback (ssgEntity * entity, int mask)
196 // {
197 //   SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
198
199 //   if (!data->is_filled_in) {
200 //                                 // Iterate through all the triangles
201 //                                 // and populate them.
202 //     int num_tris = data->leaf->getNumTriangles();
203 //     for ( int i = 0; i < num_tris; ++i ) {
204 //             data->setup_triangle(i);
205 //     }
206 //     data->is_filled_in = true;
207 //   }
208 //   return 1;
209 // }
210
211
212 /**
213  * SSG callback for an out-of-range leaf of randomly-placed objects.
214  *
215  * This pretraversal callback is attached to a branch that is
216  * traversed only when a leaf is out of range.  If the leaf is
217  * currently prepared to be populated with randomly-placed objects (or
218  * is actually populated), the objects will be removed.
219  *
220  * @param entity The entity to which the callback is attached (not used).
221  * @param mask The entity's traversal mask (not used).
222  * @return Always 0, to prevent any further traversal or culling.
223  */
224 // static int
225 // leaf_out_of_range_callback (ssgEntity * entity, int mask)
226 // {
227 //   SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
228 //   if (data->is_filled_in) {
229 //     data->branch->removeAllKids();
230 //     data->is_filled_in = false;
231 //   }
232 //   return 0;
233 // }
234
235
236 /**
237  * Randomly place objects on a surface.
238  *
239  * The leaf node provides the geometry of the surface, while the
240  * material provides the objects and placement density.  Latitude
241  * and longitude are required so that the objects can be rotated
242  * to the world-up vector.  This function does not actually add
243  * any objects; instead, it attaches an ssgRangeSelector to the
244  * branch with callbacks to generate the objects when needed.
245  *
246  * @param leaf The surface where the objects should be placed.
247  * @param branch The branch that will hold the randomly-placed objects.
248  * @param center The center of the leaf in FlightGear coordinates.
249  * @param material_name The name of the surface's material.
250  */
251 static void
252 gen_random_surface_objects (osg::Drawable *leaf,
253                             osg::Group *branch,
254                             Point3D *center,
255                             SGMaterial *mat )
256 {
257   // OSGFIXME
258 #if 0
259                                 // If the surface has no triangles, return
260                                 // now.
261     int num_tris = leaf->getNumTriangles();
262     if (num_tris < 1)
263         return;
264
265                                 // If the material has no randomly-placed
266                                 // objects, return now.
267     if (mat->get_object_group_count() < 1)
268         return;
269
270                                 // Calculate the geodetic centre of
271                                 // the tile, for aligning automatic
272                                 // objects.
273     double xyz[3], lon_rad, lat_rad, alt_m;
274     xyz[0] = center->x(); xyz[1] = center->y(); xyz[2] = center->z();
275     sgCartToGeod(xyz, &lat_rad, &lon_rad, &alt_m);
276
277                                 // LOD for the leaf
278                                 // max random object range: 20000m
279     osg::LOD * lod = new osg::LOD;
280     branch->addChild(lod);
281
282                                 // Create the in-range and out-of-range
283                                 // branches.
284     osg::Group * in_range = new osg::Group;
285 //     osg::Group * out_of_range = new osg::Group;
286     lod->addChild(in_range, 0, 20000 /*OSGFIXME hardcoded visibility ???*/);
287 //     lod->addChild(out_of_range, 20000, 1e30);
288
289     SGLeafUserData * data = new SGLeafUserData;
290     data->is_filled_in = false;
291     data->leaf = leaf;
292     data->mat = mat;
293     data->branch = in_range;
294     data->sin_lat = sin(lat_rad);
295     data->cos_lat = cos(lat_rad);
296     data->sin_lon = sin(lon_rad);
297     data->cos_lon = cos(lon_rad);
298
299     in_range->setUserData(data);
300     // OSGFIXME: implement random objects to be loaded when in sight
301 //     in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
302
303     // OSGFIXME: implement deletion of tiles that are no longer used
304 //     out_of_range->setUserData(data);
305 //     out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
306 //                                    leaf_out_of_range_callback);
307 //     out_of_range
308 //       ->addChild(new SGDummyBSphereEntity(leaf->getBSphere()->getRadius()));
309 #endif
310 }
311
312
313 // Ok, somehow polygon offset for lights ...
314 // Could never make the polygon offset for our lights get right.
315 // So, why not in this way ...
316 class SGLightOffsetTransform : public osg::Transform {
317 public:
318 #define SCALE_FACTOR 0.94
319   virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
320                                          osg::NodeVisitor* nv) const
321   {
322     if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) {
323       double scaleFactor = SCALE_FACTOR;
324       osg::Vec3 center = nv->getEyePoint();
325       osg::Matrix transform;
326       transform(0,0) = scaleFactor;
327       transform(1,1) = scaleFactor;
328       transform(2,2) = scaleFactor;
329       transform(3,0) = center[0]*(1 - scaleFactor);
330       transform(3,1) = center[1]*(1 - scaleFactor);
331       transform(3,2) = center[2]*(1 - scaleFactor);
332       matrix.preMult(transform);
333     }
334     return true;
335   }
336   virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
337                                          osg::NodeVisitor* nv) const
338   {
339     if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) {
340       double scaleFactor = 1/SCALE_FACTOR;
341       osg::Vec3 center = nv->getEyePoint();
342       osg::Matrix transform;
343       transform(0,0) = scaleFactor;
344       transform(1,1) = scaleFactor;
345       transform(2,2) = scaleFactor;
346       transform(3,0) = center[0]*(1 - scaleFactor);
347       transform(3,1) = center[1]*(1 - scaleFactor);
348       transform(3,2) = center[2]*(1 - scaleFactor);
349       matrix.postMult(transform);
350     }
351     return true;
352   }
353 #undef SCALE_FACTOR
354 };
355
356 static SGMaterial* findMaterial(const std::string& material,
357                                 const std::string& path,
358                                 SGMaterialLib *matlib)
359 {
360   SGMaterial *mat = matlib->find( material );
361   if (mat)
362     return mat;
363
364   // see if this is an on the fly texture
365   string file = path;
366   string::size_type pos = file.rfind( "/" );
367   file = file.substr( 0, pos );
368   // cout << "current file = " << file << endl;
369   file += "/";
370   file += material;
371   // cout << "current file = " << file << endl;
372   if ( ! matlib->add_item( file ) ) {
373     SG_LOG( SG_TERRAIN, SG_ALERT, 
374             "Ack! unknown usemtl name = " << material 
375             << " in " << path );
376   } else {
377     // locate our newly created material
378     mat = matlib->find( material );
379     if ( mat == NULL ) {
380       SG_LOG( SG_TERRAIN, SG_ALERT, 
381               "Ack! bad on the fly material create = "
382               << material << " in " << path );
383     }
384   }
385   return mat;
386 }
387
388 \f
389 ////////////////////////////////////////////////////////////////////////
390 // Scenery loaders.
391 ////////////////////////////////////////////////////////////////////////
392
393 // Load an Binary obj file
394 static bool SGBinObjLoad( const string& path, const bool is_base,
395                    Point3D& center,
396                    SGMaterialLib *matlib,
397                    bool use_random_objects,
398                    osg::Group *local_terrain,
399                    osg::Group *vasi_lights,
400                    osg::Group *rwy_lights,
401                    osg::Group *taxi_lights,
402                    osg::Vec3Array *ground_lights )
403 {
404     SGBinObject obj;
405
406     if ( ! obj.read_bin( path ) ) {
407         return false;
408     }
409
410     // reference point (center offset/bounding sphere)
411     center = obj.get_gbs_center();
412
413     point_list const& nodes = obj.get_wgs84_nodes();
414     point_list const& normals = obj.get_normals();
415     point_list const& texcoords = obj.get_texcoords();
416
417     string material;
418     int_list tex_index;
419
420     group_list::size_type i;
421
422     osg::Geode* geode = new osg::Geode;
423     local_terrain->addChild( geode );
424
425     // generate points
426     string_list const& pt_materials = obj.get_pt_materials();
427     group_list const& pts_v = obj.get_pts_v();
428     group_list const& pts_n = obj.get_pts_n();
429     for ( i = 0; i < pts_v.size(); ++i ) {
430         // cout << "pts_v.size() = " << pts_v.size() << endl;
431         if ( pt_materials[i].substr(0, 3) == "RWY" ) {
432             // airport environment lighting
433             SGVec3d up(center.x(), center.y(), center.z());
434             // returns a transform -> lod -> leaf structure
435             osg::Node *branch = SGMakeDirectionalLights( nodes, normals,
436                                                          pts_v[i], pts_n[i],
437                                                          matlib,
438                                                          pt_materials[i], up );
439             if ( pt_materials[i] == "RWY_VASI_LIGHTS" ) {
440                 vasi_lights->addChild( branch );
441             } else if ( pt_materials[i] == "RWY_BLUE_TAXIWAY_LIGHTS"
442                 || pt_materials[i] == "RWY_GREEN_TAXIWAY_LIGHTS" )
443             {
444                 taxi_lights->addChild( branch );
445             } else {
446                 rwy_lights->addChild( branch );
447             }
448         } else {
449             // other geometry
450             SGMaterial *mat = findMaterial( pt_materials[i], path, matlib );
451             tex_index.clear();
452             osg::Drawable *leaf = SGMakeLeaf( path, GL_POINTS, mat,
453                                         nodes, normals, texcoords,
454                                         pts_v[i], pts_n[i], tex_index,
455                                         false, ground_lights );
456             
457
458
459             geode->addDrawable( leaf );
460         }
461     }
462
463     // Put all randomly-placed objects under a separate branch
464     // (actually an ssgRangeSelector) named "random-models".
465     osg::Group * random_object_branch = 0;
466 //     if (use_random_objects) {
467 //         osg::LOD* object_lod = new osg::LOD;
468 //         object_lod->setName("random-models");
469 //         geometry->addChild(object_lod);
470 //         random_object_branch = new osg::Group;
471 //         // Maximum 20km range for random objects
472 //         object_lod->addChild(random_object_branch, 0, 20000);
473 //     }
474
475     typedef map<string,list<Leaf> > LeafMap;
476     LeafMap leafMap;
477     Leaf leaf;
478     leaf.type = GL_TRIANGLES;
479     string_list const& tri_materials = obj.get_tri_materials();
480     group_list const& tris_v = obj.get_tris_v();
481     group_list const& tris_n = obj.get_tris_n();
482     group_list const& tris_tc = obj.get_tris_tc();
483     for ( i = 0; i < tris_v.size(); i++ ) {
484         leaf.index = i;
485         leafMap[ tri_materials[i] ].push_back( leaf );
486     }
487     leaf.type = GL_TRIANGLE_STRIP;
488     string_list const& strip_materials = obj.get_strip_materials();
489     group_list const& strips_v = obj.get_strips_v();
490     group_list const& strips_n = obj.get_strips_n();
491     group_list const& strips_tc = obj.get_strips_tc();
492     for ( i = 0; i < strips_v.size(); i++ ) {
493         leaf.index = i;
494         leafMap[ strip_materials[i] ].push_back( leaf );
495     }
496     leaf.type = GL_TRIANGLE_FAN;
497     string_list const& fan_materials = obj.get_fan_materials();
498     group_list const& fans_v = obj.get_fans_v();
499     group_list const& fans_n = obj.get_fans_n();
500     group_list const& fans_tc = obj.get_fans_tc();
501     for ( i = 0; i < fans_v.size(); i++ ) {
502         leaf.index = i;
503         leafMap[ fan_materials[i] ].push_back( leaf );
504     }
505
506     LeafMap::iterator lmi = leafMap.begin();
507     while ( lmi != leafMap.end() ) {
508         SGMaterial *mat = findMaterial( lmi->first, path, matlib );
509         list<Leaf> &leaf_list = lmi->second;
510         list<Leaf>::iterator li = leaf_list.begin();
511         while ( li != leaf_list.end() ) {
512             Leaf &leaf = *li;
513             int ind = leaf.index;
514             if ( leaf.type == GL_TRIANGLES ) {
515                 osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLES, mat,
516                                             nodes, normals, texcoords,
517                                             tris_v[ind], tris_n[ind], tris_tc[ind],
518                                             is_base, ground_lights );
519                 if ( random_object_branch ) {
520                     if ( mat ) {
521                         gen_random_surface_objects( leaf, random_object_branch,
522                                                     &center, mat );
523                     }
524                 }
525                 geode->addDrawable( leaf );
526             } else if ( leaf.type == GL_TRIANGLE_STRIP ) {
527                 osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLE_STRIP, mat,
528                                             nodes, normals, texcoords,
529                                             strips_v[ind], strips_n[ind], strips_tc[ind],
530                                             is_base, ground_lights );
531                 if ( random_object_branch ) {
532                     if ( mat ) {
533                         gen_random_surface_objects( leaf, random_object_branch,
534                                                     &center, mat );
535                     }
536                 }
537                 geode->addDrawable( leaf );
538             } else {
539                 osg::Drawable *leaf = SGMakeLeaf( path, GL_TRIANGLE_FAN, mat,
540                                             nodes, normals, texcoords,
541                                             fans_v[ind], fans_n[ind], fans_tc[ind],
542                                             is_base, ground_lights );
543                 if ( random_object_branch ) {
544                     if ( mat ) {
545                         gen_random_surface_objects( leaf, random_object_branch,
546                                                     &center, mat );
547                     }
548                 }
549                 geode->addDrawable( leaf );
550             }
551             ++li;
552         }
553         ++lmi;
554     }
555
556     return true;
557 }
558
559
560
561
562 static osg::Node*
563 gen_lights( SGMaterialLib *matlib, osg::Vec3Array *lights, int inc, float bright )
564 {
565     // generate a repeatable random seed
566     sg_srandom( (unsigned)(*lights)[0][0] );
567
568     // Allocate ssg structure
569     osg::Vec3Array *vl = new osg::Vec3Array;
570     osg::Vec4Array *cl = new osg::Vec4Array;
571
572     for ( unsigned i = 0; i < lights->size(); ++i ) {
573         // this loop is slightly less efficient than it otherwise
574         // could be, but we want a red light to always be red, and a
575         // yellow light to always be yellow, etc. so we are trying to
576         // preserve the random sequence.
577         float zombie = sg_random();
578         if ( i % inc == 0 ) {
579             vl->push_back( (*lights)[i] );
580
581             // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
582             float factor = sg_random();
583             factor *= factor;
584
585             osg::Vec4 color;
586             if ( zombie > 0.5 ) {
587                 // 50% chance of yellowish
588                 color = osg::Vec4( 0.9, 0.9, 0.3, bright - factor * 0.2 );
589             } else if ( zombie > 0.15 ) {
590                 // 35% chance of whitish
591                 color = osg::Vec4( 0.9, 0.9, 0.8, bright - factor * 0.2 );
592             } else if ( zombie > 0.05 ) {
593                 // 10% chance of orangish
594                 color = osg::Vec4( 0.9, 0.6, 0.2, bright - factor * 0.2 );
595             } else {
596                 // 5% chance of redish
597                 color = osg::Vec4( 0.9, 0.2, 0.2, bright - factor * 0.2 );
598             }
599             cl->push_back( color );
600         }
601     }
602
603     // create ssg leaf
604     osg::Geometry* geometry = new osg::Geometry;
605     geometry->setVertexArray(vl);
606     geometry->setColorArray(cl);
607     geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
608     geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, vl->size()));
609     osg::Geode* geode = new osg::Geode;
610     geode->addDrawable(geometry);
611
612     // assign state
613     SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
614     geometry->setStateSet(mat->get_state());
615
616     return geode;
617 }
618
619 class SGTileUpdateCallback : public osg::NodeCallback {
620 public:
621   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
622   {
623     assert(dynamic_cast<osg::Switch*>(node));
624     assert(dynamic_cast<SGUpdateVisitor*>(nv));
625
626     osg::Switch* lightSwitch = static_cast<osg::Switch*>(node);
627     SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
628
629     // The current sun angle in degree
630     float sun_angle = updateVisitor->getSunAngleDeg();
631
632     // vasi is always on
633     lightSwitch->setValue(0, true);
634     if (sun_angle > 85 || updateVisitor->getVisibility() < 5000) {
635       // runway and taxi
636       lightSwitch->setValue(1, true);
637       lightSwitch->setValue(2, true);
638     } else {
639       // runway and taxi
640       lightSwitch->setValue(1, false);
641       lightSwitch->setValue(2, false);
642     }
643     
644     // ground lights
645     if ( sun_angle > 95 )
646       lightSwitch->setValue(5, true);
647     else
648       lightSwitch->setValue(5, false);
649     if ( sun_angle > 92 )
650       lightSwitch->setValue(4, true);
651     else
652       lightSwitch->setValue(4, false);
653     if ( sun_angle > 89 )
654       lightSwitch->setValue(3, true);
655     else
656       lightSwitch->setValue(3, false);
657
658     traverse(node, nv);
659   }
660 };
661
662 class SGRunwayLightFogUpdateCallback : public osg::StateAttribute::Callback {
663 public:
664   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
665   {
666     assert(dynamic_cast<SGUpdateVisitor*>(nv));
667     assert(dynamic_cast<osg::Fog*>(sa));
668     SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
669     osg::Fog* fog = static_cast<osg::Fog*>(sa);
670     fog->setMode(osg::Fog::EXP2);
671     fog->setColor(updateVisitor->getFogColor().osg());
672     fog->setDensity(updateVisitor->getRunwayFogExp2Density());
673   }
674 };
675
676 class SGTaxiLightFogUpdateCallback : public osg::StateAttribute::Callback {
677 public:
678   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
679   {
680     assert(dynamic_cast<SGUpdateVisitor*>(nv));
681     assert(dynamic_cast<osg::Fog*>(sa));
682     SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
683     osg::Fog* fog = static_cast<osg::Fog*>(sa);
684     fog->setMode(osg::Fog::EXP2);
685     fog->setColor(updateVisitor->getFogColor().osg());
686     fog->setDensity(updateVisitor->getTaxiFogExp2Density());
687   }
688 };
689
690 class SGGroundLightFogUpdateCallback : public osg::StateAttribute::Callback {
691 public:
692   virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
693   {
694     assert(dynamic_cast<SGUpdateVisitor*>(nv));
695     assert(dynamic_cast<osg::Fog*>(sa));
696     SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
697     osg::Fog* fog = static_cast<osg::Fog*>(sa);
698     fog->setMode(osg::Fog::EXP2);
699     fog->setColor(updateVisitor->getFogColor().osg());
700     fog->setDensity(updateVisitor->getGroundLightsFogExp2Density());
701   }
702 };
703
704
705 osg::Node*
706 SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
707 {
708   osg::Group* vasiLights = new osg::Group;
709   osg::StateSet* stateSet = vasiLights->getOrCreateStateSet();
710   osg::Fog* fog = new osg::Fog;
711   fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
712   stateSet->setAttribute(fog);
713
714   osg::Group* rwyLights = new osg::Group;
715   stateSet = rwyLights->getOrCreateStateSet();
716   fog = new osg::Fog;
717   fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
718   stateSet->setAttribute(fog);
719
720   osg::Group* taxiLights = new osg::Group;
721   stateSet = taxiLights->getOrCreateStateSet();
722   fog = new osg::Fog;
723   fog->setUpdateCallback(new SGTaxiLightFogUpdateCallback);
724   stateSet->setAttribute(fog);
725
726   osg::Group* groundLights0 = new osg::Group;
727   stateSet = groundLights0->getOrCreateStateSet();
728   fog = new osg::Fog;
729   fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
730   stateSet->setAttribute(fog);
731
732   osg::Group* groundLights1 = new osg::Group;
733   stateSet = groundLights1->getOrCreateStateSet();
734   fog = new osg::Fog;
735   fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
736   stateSet->setAttribute(fog);
737
738   osg::Group* groundLights2 = new osg::Group;
739   stateSet = groundLights2->getOrCreateStateSet();
740   fog = new osg::Fog;
741   fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
742   stateSet->setAttribute(fog);
743
744   osg::Switch* lightSwitch = new osg::Switch;
745   lightSwitch->setUpdateCallback(new SGTileUpdateCallback);
746   lightSwitch->addChild(vasiLights, true);
747   lightSwitch->addChild(rwyLights, true);
748   lightSwitch->addChild(taxiLights, true);
749   lightSwitch->addChild(groundLights0, true);
750   lightSwitch->addChild(groundLights1, true);
751   lightSwitch->addChild(groundLights2, true);
752
753   osg::Group* lightGroup = new SGLightOffsetTransform;
754   lightGroup->addChild(lightSwitch);
755   unsigned nodeMask = ~0u;
756   nodeMask &= ~SG_NODEMASK_CASTSHADOW_BIT;
757   nodeMask &= ~SG_NODEMASK_RECIEVESHADOW_BIT;
758   nodeMask &= ~SG_NODEMASK_PICK_BIT;
759   nodeMask &= ~SG_NODEMASK_TERRAIN_BIT;
760   lightGroup->setNodeMask(nodeMask);
761   
762   osg::Group* terrainGroup = new osg::Group;
763
764   osg::ref_ptr<osg::Vec3Array> light_pts = new osg::Vec3Array;
765   Point3D center;
766   SGBinObjLoad(path, calc_lights, center, matlib, use_random_objects,
767                terrainGroup, vasiLights, rwyLights, taxiLights, light_pts.get());
768
769   if ( light_pts->size() ) {
770     osg::Node *lights;
771     
772     lights = gen_lights( matlib, light_pts.get(), 4, 0.7 );
773     groundLights0->addChild( lights );
774     
775     lights = gen_lights( matlib, light_pts.get(), 2, 0.85 );
776     groundLights1->addChild( lights );
777     
778     lights = gen_lights( matlib, light_pts.get(), 1, 1.0 );
779     groundLights2->addChild( lights );
780   }
781
782   // The toplevel transform for that tile.
783   osg::MatrixTransform* transform = new osg::MatrixTransform;
784   transform->setName(path);
785   transform->setMatrix(osg::Matrix::translate(osg::Vec3d(center[0], center[1], center[2])));
786   transform->addChild(terrainGroup);
787   transform->addChild(lightGroup);
788
789   return transform;
790 }