]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tileentry.cxx
Fix the nmea and garmin output to a) fake a GSA sentence, b) fix a y2k bug
[flightgear.git] / src / Scenery / tileentry.cxx
1 // tileentry.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2001  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #include <Main/main.hxx>
31
32
33 #include STL_STRING
34
35 #include <simgear/bucket/newbucket.hxx>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/math/sg_geodesy.hxx>
38 #include <simgear/math/sg_random.h>
39 #include <simgear/misc/sgstream.hxx>
40 #include <simgear/scene/material/mat.hxx>
41 #include <simgear/scene/material/matlib.hxx>
42 #include <simgear/scene/tgdb/apt_signs.hxx>
43 #include <simgear/scene/tgdb/obj.hxx>
44
45 #include <Aircraft/aircraft.hxx>
46 #include <Include/general.hxx>
47 #include <Main/fg_props.hxx>
48 #include <Main/globals.hxx>
49 #include <Main/viewer.hxx>
50 #include <Scenery/scenery.hxx>
51 #include <Time/light.hxx>
52
53 #include "tileentry.hxx"
54 #include "tilemgr.hxx"
55
56 SG_USING_STD(string);
57
58
59 // Constructor
60 FGTileEntry::FGTileEntry ( const SGBucket& b )
61     : center( Point3D( 0.0 ) ),
62       tile_bucket( b ),
63       terra_transform( new ssgTransform ),
64       vasi_lights_transform( new ssgTransform ),
65       rwy_lights_transform( new ssgTransform ),
66       taxi_lights_transform( new ssgTransform ),
67       terra_range( new ssgRangeSelector ),
68       vasi_lights_selector( new ssgSelector ),
69       rwy_lights_selector( new ssgSelector ),
70       taxi_lights_selector( new ssgSelector ),
71       loaded(false),
72       pending_models(0),
73       free_tracker(0)
74 {
75     // update the contents
76     // if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
77     //     SG_LOG( SG_TERRAIN, SG_ALERT, 
78     //             "Attempting to overwrite existing or"
79     //             << " not properly freed leaf data." );
80     //     exit(-1);
81     // }
82 }
83
84
85 // Destructor
86 FGTileEntry::~FGTileEntry () {
87     // cout << "nodes = " << nodes.size() << endl;;
88     // delete[] nodes;
89 }
90
91
92 #if 0
93 // Please keep this for reference.  We use Norman's optimized routine,
94 // but here is what the routine really is doing.
95 void
96 FGTileEntry::WorldCoordinate( sgCoord *obj_pos, Point3D center,
97                               double lat, double lon, double elev, double hdg)
98 {
99     // setup transforms
100     Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
101                   lat * SGD_DEGREES_TO_RADIANS,
102                   elev );
103         
104     Point3D world_pos = sgGeodToCart( geod );
105     Point3D offset = world_pos - center;
106         
107     sgMat4 POS;
108     sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
109
110     sgVec3 obj_rt, obj_up;
111     sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
112     sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
113
114     sgMat4 ROT_lon, ROT_lat, ROT_hdg;
115     sgMakeRotMat4( ROT_lon, lon, obj_up );
116     sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
117     sgMakeRotMat4( ROT_hdg, hdg, obj_up );
118
119     sgMat4 TUX;
120     sgCopyMat4( TUX, ROT_hdg );
121     sgPostMultMat4( TUX, ROT_lat );
122     sgPostMultMat4( TUX, ROT_lon );
123     sgPostMultMat4( TUX, POS );
124
125     sgSetCoord( obj_pos, TUX );
126 }
127 #endif
128
129
130 // Norman's 'fast hack' for above
131 static void WorldCoordinate( sgCoord *obj_pos, Point3D center, double lat,
132                              double lon, double elev, double hdg )
133 {
134     double lon_rad = lon * SGD_DEGREES_TO_RADIANS;
135     double lat_rad = lat * SGD_DEGREES_TO_RADIANS;
136     double hdg_rad = hdg * SGD_DEGREES_TO_RADIANS;
137
138     // setup transforms
139     Point3D geod( lon_rad, lat_rad, elev );
140         
141     Point3D world_pos = sgGeodToCart( geod );
142     Point3D offset = world_pos - center;
143
144     sgMat4 mat;
145
146     SGfloat sin_lat = (SGfloat)sin( lat_rad );
147     SGfloat cos_lat = (SGfloat)cos( lat_rad );
148     SGfloat cos_lon = (SGfloat)cos( lon_rad );
149     SGfloat sin_lon = (SGfloat)sin( lon_rad );
150     SGfloat sin_hdg = (SGfloat)sin( hdg_rad ) ;
151     SGfloat cos_hdg = (SGfloat)cos( hdg_rad ) ;
152
153     mat[0][0] =  cos_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - sin_hdg * (SGfloat)sin_lon;
154     mat[0][1] =  cos_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + sin_hdg * (SGfloat)cos_lon;
155     mat[0][2] = -cos_hdg * (SGfloat)cos_lat;
156     mat[0][3] =  SG_ZERO;
157
158     mat[1][0] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - cos_hdg * (SGfloat)sin_lon;
159     mat[1][1] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + cos_hdg * (SGfloat)cos_lon;
160     mat[1][2] =  sin_hdg * (SGfloat)cos_lat;
161     mat[1][3] =  SG_ZERO;
162
163     mat[2][0] = (SGfloat)cos_lat * (SGfloat)cos_lon;
164     mat[2][1] = (SGfloat)cos_lat * (SGfloat)sin_lon;
165     mat[2][2] = (SGfloat)sin_lat;
166     mat[2][3] =  SG_ZERO;
167
168     mat[3][0] = offset.x();
169     mat[3][1] = offset.y();
170     mat[3][2] = offset.z();
171     mat[3][3] = SG_ONE ;
172
173     sgSetCoord( obj_pos, mat );
174 }
175
176
177 // recurse an ssg tree and call removeKid() on every node from the
178 // bottom up.  Leaves the original branch in existance, but empty so
179 // it can be removed by the calling routine.
180 static void my_remove_branch( ssgBranch * branch ) {
181     for ( ssgEntity *k = branch->getKid( 0 );
182           k != NULL; 
183           k = branch->getNextKid() )
184     {
185         if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
186             my_remove_branch( (ssgBranch *)k );
187             branch -> removeKid ( k );
188         } else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
189             branch -> removeKid ( k ) ;
190         }
191     }
192 }
193
194
195 // Free "n" leaf elements of an ssg tree.  returns the number of
196 // elements freed.  An empty branch node is considered a leaf.  This
197 // is intended to spread the load of freeing a complex tile out over
198 // several frames.
199 static int fgPartialFreeSSGtree( ssgBranch *b, int n ) {
200
201 #if 0
202     // for testing: we could call the following two lines and replace
203     // the functionality of this entire function and everything will
204     // get properly freed, but it will happen all at once and could
205     // cause a huge frame rate hit.
206     ssgDeRefDelete( b );
207     return 0;
208 #endif
209
210     int num_deletes = 0;
211
212     if ( n > 0 ) {
213         // we still have some delete budget left
214         // if ( b->getNumKids() > 100 ) {
215         //     cout << "large family = " << b->getNumKids() << endl;
216         // }
217         // deleting in reverse would help if my plib patch get's
218         // applied, but for now it will make things slower.
219         // for ( int i = b->getNumKids() - 1; i >= 0 ; --i ) {
220         for ( int i = 0; i < b->getNumKids(); ++i ) {
221             ssgEntity *kid = b->getKid(i);
222             if ( kid->isAKindOf( ssgTypeBranch() ) && kid->getRef() <= 1 ) {
223                 int result = fgPartialFreeSSGtree( (ssgBranch *)kid, n );
224                 num_deletes += result;
225                 n -= result;
226                 if ( n < 0 ) {
227                     break;
228                 }
229             }
230             // remove the kid if (a) it is now empty -or- (b) it's ref
231             // count is > zero at which point we don't care if it's
232             // empty, we don't want to touch it's contents.
233             if ( kid->getNumKids() == 0 || kid->getRef() > 1 ) {
234                 b->removeKid( kid );
235                 num_deletes++;
236                 n--;
237             }
238         }
239     }
240
241     return num_deletes;
242 }
243
244
245 // Clean up the memory used by this tile and delete the arrays used by
246 // ssg as well as the whole ssg branch
247 bool FGTileEntry::free_tile() {
248     int delete_size = 100;
249     SG_LOG( SG_TERRAIN, SG_DEBUG,
250             "FREEING TILE = (" << tile_bucket << ")" );
251
252     SG_LOG( SG_TERRAIN, SG_DEBUG, "(start) free_tracker = " << free_tracker );
253     
254     if ( !(free_tracker & NODES) ) {
255         free_tracker |= NODES;
256     } else if ( !(free_tracker & VEC_PTRS) ) {
257         free_tracker |= VEC_PTRS;
258     } else if ( !(free_tracker & TERRA_NODE) ) {
259         // delete the terrain branch (this should already have been
260         // disconnected from the scene graph)
261         SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING terra_transform" );
262         if ( fgPartialFreeSSGtree( terra_transform, delete_size ) == 0 ) {
263             ssgDeRefDelete( terra_transform );
264             free_tracker |= TERRA_NODE;
265         }
266     } else if ( !(free_tracker & GROUND_LIGHTS) && gnd_lights_transform ) {
267         // delete the terrain lighting branch (this should already have been
268         // disconnected from the scene graph)
269         SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING gnd_lights_transform" );
270         if ( fgPartialFreeSSGtree( gnd_lights_transform, delete_size ) == 0 ) {
271             ssgDeRefDelete( gnd_lights_transform );
272             free_tracker |= GROUND_LIGHTS;
273         }
274     } else if ( !(free_tracker & VASI_LIGHTS) && vasi_lights_selector ) {
275         // delete the runway lighting branch (this should already have
276         // been disconnected from the scene graph)
277         SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING vasi_lights_selector" );
278         if ( fgPartialFreeSSGtree( vasi_lights_selector, delete_size ) == 0 ) {
279             ssgDeRefDelete( vasi_lights_selector );
280             free_tracker |= VASI_LIGHTS;
281         }
282     } else if ( !(free_tracker & RWY_LIGHTS) && rwy_lights_selector ) {
283         // delete the runway lighting branch (this should already have
284         // been disconnected from the scene graph)
285         SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING rwy_lights_selector" );
286         if ( fgPartialFreeSSGtree( rwy_lights_selector, delete_size ) == 0 ) {
287             ssgDeRefDelete( rwy_lights_selector );
288             free_tracker |= RWY_LIGHTS;
289         }
290     } else if ( !(free_tracker & TAXI_LIGHTS) && taxi_lights_selector ) {
291         // delete the taxi lighting branch (this should already have been
292         // disconnected from the scene graph)
293         SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING taxi_lights_selector" );
294         if ( fgPartialFreeSSGtree( taxi_lights_selector, delete_size ) == 0 ) {
295             ssgDeRefDelete( taxi_lights_selector );
296             free_tracker |= TAXI_LIGHTS;
297         }
298     } else if ( !(free_tracker & LIGHTMAPS) ) {
299         free_tracker |= LIGHTMAPS;
300     } else {
301         return true;
302     }
303
304     SG_LOG( SG_TERRAIN, SG_DEBUG, "(end) free_tracker = " << free_tracker );
305
306     // if we fall down to here, we still have work todo, return false
307     return false;
308 }
309
310
311 // Update the ssg transform node for this tile so it can be
312 // properly drawn relative to our (0,0,0) point
313 void FGTileEntry::prep_ssg_node( const Point3D& p, sgVec3 up, float vis) {
314     if ( !loaded ) return;
315
316     SetOffset( p );
317
318     // visibility can change from frame to frame so we update the
319     // range selector cutoff's each time.
320     terra_range->setRange( 0, SG_ZERO );
321     terra_range->setRange( 1, vis + bounding_radius );
322
323     if ( gnd_lights_range ) {
324         gnd_lights_range->setRange( 0, SG_ZERO );
325         gnd_lights_range->setRange( 1, vis * 1.5 + bounding_radius );
326     }
327
328     sgVec3 sgTrans;
329     sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
330     terra_transform->setTransform( sgTrans );
331
332     FGLight *l = (FGLight *)(globals->get_subsystem("lighting"));
333     if ( gnd_lights_transform ) {
334         // we need to lift the lights above the terrain to avoid
335         // z-buffer fighting.  We do this based on our altitude and
336         // the distance this tile is away from scenery center.
337
338         // we expect 'up' to be a unit vector coming in, but since we
339         // modify the value of lift_vec, we need to create a local
340         // copy.
341         sgVec3 lift_vec;
342         sgCopyVec3( lift_vec, up );
343
344         double agl;
345         agl = globals->get_current_view()->getAltitudeASL_ft()
346             * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev();
347
348         // sgTrans just happens to be the
349         // vector from scenery center to the center of this tile which
350         // is what we want to calculate the distance of
351         sgVec3 to;
352         sgCopyVec3( to, sgTrans );
353         double dist = sgLengthVec3( to );
354
355         if ( general.get_glDepthBits() > 16 ) {
356             sgScaleVec3( lift_vec, 10.0 + agl / 100.0 + dist / 10000 );
357         } else {
358             sgScaleVec3( lift_vec, 10.0 + agl / 20.0 + dist / 5000 );
359         }
360
361         sgVec3 lt_trans;
362         sgCopyVec3( lt_trans, sgTrans );
363
364         sgAddVec3( lt_trans, lift_vec );
365         gnd_lights_transform->setTransform( lt_trans );
366
367         // select which set of lights based on sun angle
368         float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
369         if ( sun_angle > 95 ) {
370             gnd_lights_brightness->select(0x04);
371         } else if ( sun_angle > 92 ) {
372             gnd_lights_brightness->select(0x02);
373         } else if ( sun_angle > 89 ) {
374             gnd_lights_brightness->select(0x01);
375         } else {
376             gnd_lights_brightness->select(0x00);
377         }
378     }
379
380     if ( vasi_lights_transform ) {
381         // we need to lift the lights above the terrain to avoid
382         // z-buffer fighting.  We do this based on our altitude and
383         // the distance this tile is away from scenery center.
384
385         sgVec3 lift_vec;
386         sgCopyVec3( lift_vec, up );
387
388         // we fudge agl by 30 meters so that the lifting function
389         // doesn't phase in until we are > 30m agl.
390         double agl;
391         agl = globals->get_current_view()->getAltitudeASL_ft()
392             * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev()
393             - 30.0;
394         if ( agl < 0.0 ) {
395             agl = 0.0;
396         }
397         
398         if ( general.get_glDepthBits() > 16 ) {
399             sgScaleVec3( lift_vec, 0.0 + agl / 500.0 );
400         } else {
401             sgScaleVec3( lift_vec, 0.0 + agl / 150.0 );
402         }
403
404         sgVec3 lt_trans;
405         sgCopyVec3( lt_trans, sgTrans );
406
407         sgAddVec3( lt_trans, lift_vec );
408         vasi_lights_transform->setTransform( lt_trans );
409
410         // generally, vasi lights are always on
411         vasi_lights_selector->select(0x01);
412     }
413
414     if ( rwy_lights_transform ) {
415         // we need to lift the lights above the terrain to avoid
416         // z-buffer fighting.  We do this based on our altitude and
417         // the distance this tile is away from scenery center.
418
419         sgVec3 lift_vec;
420         sgCopyVec3( lift_vec, up );
421
422         // we fudge agl by 30 meters so that the lifting function
423         // doesn't phase in until we are > 30m agl.
424         double agl;
425         agl = globals->get_current_view()->getAltitudeASL_ft()
426             * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev()
427             - 30.0;
428         if ( agl < 0.0 ) {
429             agl = 0.0;
430         }
431         
432         if ( general.get_glDepthBits() > 16 ) {
433             sgScaleVec3( lift_vec, 0.0 + agl / 500.0 );
434         } else {
435             sgScaleVec3( lift_vec, 0.0 + agl / 150.0 );
436         }
437
438         sgVec3 lt_trans;
439         sgCopyVec3( lt_trans, sgTrans );
440
441         sgAddVec3( lt_trans, lift_vec );
442         rwy_lights_transform->setTransform( lt_trans );
443
444         // turn runway lights on/off based on sun angle and visibility
445         float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
446         if ( sun_angle > 85 ||
447              (fgGetDouble("/environment/visibility-m") < 5000.0) ) {
448             rwy_lights_selector->select(0x01);
449         } else {
450             rwy_lights_selector->select(0x00);
451         }
452     }
453
454     if ( taxi_lights_transform ) {
455         // we need to lift the lights above the terrain to avoid
456         // z-buffer fighting.  We do this based on our altitude and
457         // the distance this tile is away from scenery center.
458
459         sgVec3 lift_vec;
460         sgCopyVec3( lift_vec, up );
461
462         // we fudge agl by 30 meters so that the lifting function
463         // doesn't phase in until we are > 30m agl.
464         double agl;
465         agl = globals->get_current_view()->getAltitudeASL_ft()
466             * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev()
467             - 30.0;
468         if ( agl < 0.0 ) {
469             agl = 0.0;
470         }
471         
472         if ( general.get_glDepthBits() > 16 ) {
473             sgScaleVec3( lift_vec, 0.0 + agl / 500.0 );
474         } else {
475             sgScaleVec3( lift_vec, 0.0 + agl / 150.0 );
476         }
477
478         sgVec3 lt_trans;
479         sgCopyVec3( lt_trans, sgTrans );
480
481         sgAddVec3( lt_trans, lift_vec );
482         taxi_lights_transform->setTransform( lt_trans );
483
484         // turn taxi lights on/off based on sun angle and visibility
485         float sun_angle = l->get_sun_angle() * SGD_RADIANS_TO_DEGREES;
486         if ( sun_angle > 85 ||
487              (fgGetDouble("/environment/visibility-m") < 5000.0) ) {
488             taxi_lights_selector->select(0x01);
489         } else {
490             taxi_lights_selector->select(0x00);
491         }
492     }
493 }
494
495
496 // Set up lights rendering call backs
497 static int fgLightsPredraw( ssgEntity *e ) {
498 #if 0
499     if (glPointParameterIsSupported) {
500         static float quadratic[3] = {1.0, 0.01, 0.0001};
501         glPointParameterfvProc(GL_DISTANCE_ATTENUATION_EXT, quadratic);
502         glPointParameterfProc(GL_POINT_SIZE_MIN_EXT, 1.0); 
503         glPointSize(4.0);
504     }
505 #endif
506     return true;
507 }
508
509 static int fgLightsPostdraw( ssgEntity *e ) {
510 #if 0
511     if (glPointParameterIsSupported) {
512         static float default_attenuation[3] = {1.0, 0.0, 0.0};
513         glPointParameterfvProc(GL_DISTANCE_ATTENUATION_EXT,
514                               default_attenuation);
515         glPointSize(1.0);
516     }
517 #endif
518     return true;
519 }
520
521
522 ssgLeaf* FGTileEntry::gen_lights( SGMaterialLib *matlib, ssgVertexArray *lights,
523                                   int inc, float bright )
524 {
525     // generate a repeatable random seed
526     float *p1 = lights->get( 0 );
527     unsigned int *seed = (unsigned int *)p1;
528     sg_srandom( *seed );
529
530     int size = lights->getNum() / inc;
531
532     // Allocate ssg structure
533     ssgVertexArray   *vl = new ssgVertexArray( size + 1 );
534     ssgNormalArray   *nl = NULL;
535     ssgTexCoordArray *tl = NULL;
536     ssgColourArray   *cl = new ssgColourArray( size + 1 );
537
538     sgVec4 color;
539     for ( int i = 0; i < lights->getNum(); ++i ) {
540         // this loop is slightly less efficient than it otherwise
541         // could be, but we want a red light to always be red, and a
542         // yellow light to always be yellow, etc. so we are trying to
543         // preserve the random sequence.
544         float zombie = sg_random();
545         if ( i % inc == 0 ) {
546             vl->add( lights->get(i) );
547
548             // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
549             float factor = sg_random();
550             factor *= factor;
551
552             if ( zombie > 0.5 ) {
553                 // 50% chance of yellowish
554                 sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
555             } else if ( zombie > 0.15 ) {
556                 // 35% chance of whitish
557                 sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
558             } else if ( zombie > 0.05 ) {
559                 // 10% chance of orangish
560                 sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
561             } else {
562                 // 5% chance of redish
563                 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
564             }
565             cl->add( color );
566         }
567     }
568
569     // create ssg leaf
570     ssgLeaf *leaf = 
571         new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
572
573     // assign state
574     SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
575     leaf->setState( mat->get_state() );
576     leaf->setCallback( SSG_CALLBACK_PREDRAW, fgLightsPredraw );
577     leaf->setCallback( SSG_CALLBACK_POSTDRAW, fgLightsPostdraw );
578
579     return leaf;
580 }
581
582
583 bool FGTileEntry::obj_load( const string& path,
584                             ssgBranch *geometry,
585                             ssgBranch *vasi_lights,
586                             ssgBranch *rwy_lights,
587                             ssgBranch *taxi_lights,
588                             ssgVertexArray *ground_lights, bool is_base )
589 {
590     Point3D c;                  // returned center point
591     double br;                  // returned bounding radius
592
593     bool use_random_objects =
594         fgGetBool("/sim/rendering/random-objects", true);
595
596     // try loading binary format
597     if ( sgBinObjLoad( path, is_base,
598                        &c, &br, globals->get_matlib(), use_random_objects,
599                        geometry, vasi_lights, rwy_lights, taxi_lights,
600                        ground_lights ) )
601     {
602         if ( is_base ) {
603             center = c;
604             bounding_radius = br;
605         }
606     }
607
608     return (geometry != NULL);
609 }
610
611
612 void
613 FGTileEntry::load( const string &base_path, bool is_base )
614 {
615     SG_LOG( SG_TERRAIN, SG_INFO, "load() base search path = "
616             << base_path );
617
618     bool found_tile_base = false;
619
620     string_list search = sgPathSplit( base_path );
621
622     // obj_load() will generate ground lighting for us ...
623     ssgVertexArray *light_pts = new ssgVertexArray( 100 );
624
625     ssgBranch* new_tile = new ssgBranch;
626
627     unsigned int i = 0;
628     while ( i < search.size() ) {
629
630         bool has_base = false;
631
632         // Generate names for later use
633         string index_str = tile_bucket.gen_index_str();
634
635         SGPath tile_path = search[i];
636         tile_path.append( tile_bucket.gen_base_path() );
637
638         SGPath basename = tile_path;
639         basename.append( index_str );
640         // string path = basename.str();
641
642         SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << basename.str() );
643
644 #define FG_MAX_LIGHTS 1000
645
646         // Check for master .stg (scene terra gear) file
647         SGPath stg_name = basename;
648         stg_name.concat( ".stg" );
649
650         sg_gzifstream in( stg_name.str() );
651
652         if ( in.is_open() ) {
653             string token, name;
654
655             while ( ! in.eof() ) {
656                 in >> token;
657
658                                 // Load only once (first found)
659                 if ( token == "OBJECT_BASE" ) {
660                     in >> name >> ::skipws;
661                     SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
662                             << " name = " << name );
663
664                     if (!found_tile_base) {
665                         found_tile_base = true;
666                         has_base = true;
667
668                         SGPath custom_path = tile_path;
669                         custom_path.append( name );
670
671                         ssgBranch *geometry = new ssgBranch;
672                         if ( obj_load( custom_path.str(),
673                                        geometry, NULL, NULL, NULL, light_pts,
674                                        true ) )
675                             {
676                                 new_tile -> addKid( geometry );
677                             } else {
678                                 delete geometry;
679                             }
680                     } else {
681                         SG_LOG( SG_TERRAIN, SG_INFO, " (skipped)" );
682                     }
683
684                                 // Load only if base is not in another file
685                 } else if ( token == "OBJECT" ) {
686                     if (!found_tile_base || has_base) {
687                         in >> name >> ::skipws;
688                         SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
689                                 << " name = " << name );
690
691                         SGPath custom_path = tile_path;
692                         custom_path.append( name );
693
694                         ssgBranch *geometry = new ssgBranch;
695                         ssgBranch *vasi_lights = new ssgBranch;
696                         ssgBranch *rwy_lights = new ssgBranch;
697                         ssgBranch *taxi_lights = new ssgBranch;
698                         if ( obj_load( custom_path.str(),
699                                        geometry, vasi_lights, rwy_lights,
700                                        taxi_lights, NULL, false ) )
701                             {
702                                 if ( geometry -> getNumKids() > 0 ) {
703                                     new_tile -> addKid( geometry );
704                                 } else {
705                                     delete geometry;
706                                 }
707                                 if ( vasi_lights -> getNumKids() > 0 ) {
708                                     vasi_lights_transform -> addKid( vasi_lights );
709                                 } else {
710                                     delete vasi_lights;
711                                 }
712                                 if ( rwy_lights -> getNumKids() > 0 ) {
713                                     rwy_lights_transform -> addKid( rwy_lights );
714                                 } else {
715                                     delete rwy_lights;
716                                 }
717                                 if ( taxi_lights -> getNumKids() > 0 ) {
718                                     taxi_lights_transform -> addKid( taxi_lights );
719                                 } else {
720                                     delete taxi_lights;
721                                 }
722                             } else {
723                                 delete geometry;
724                                 delete vasi_lights;
725                                 delete rwy_lights;
726                                 delete taxi_lights;
727                             }
728                     }
729
730                                 // Always OK to load
731                 } else if ( token == "OBJECT_STATIC" ||
732                             token == "OBJECT_SHARED" ) {
733                     // load object info
734                     double lon, lat, elev, hdg;
735                     in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
736                     SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
737                             << " name = " << name 
738                             << " pos = " << lon << ", " << lat
739                             << " elevation = " << elev
740                             << " heading = " << hdg );
741
742                     // object loading is deferred to main render thread,
743                     // but lets figure out the paths right now.
744                     SGPath custom_path;
745                     if ( token == "OBJECT_STATIC" ) {
746                         custom_path= tile_path;
747                     } else {
748                         custom_path = globals->get_fg_root();
749                     }
750                     custom_path.append( name );
751
752                     sgCoord obj_pos;
753                     WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
754                 
755                     ssgTransform *obj_trans = new ssgTransform;
756                     obj_trans->setTransform( &obj_pos );
757
758                     // wire as much of the scene graph together as we can
759                     new_tile->addKid( obj_trans );
760
761                     // bump up the pending models count
762                     pending_models++;
763
764                     // push an entry onto the model load queue
765                     FGDeferredModel *dm
766                         = new FGDeferredModel( custom_path.str(),
767                                                tile_path.str(),
768                                                this, obj_trans );
769                     FGTileMgr::model_ready( dm );
770
771                                 // Do we even use this one?
772                 } else if ( token == "OBJECT_TAXI_SIGN" ) {
773                     // load object info
774                     double lon, lat, elev, hdg;
775                     in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
776                     SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
777                             << " name = " << name 
778                             << " pos = " << lon << ", " << lat
779                             << " elevation = " << elev
780                             << " heading = " << hdg );
781
782                     // load the object itself
783                     SGPath custom_path = tile_path;
784                     custom_path.append( name );
785
786                     sgCoord obj_pos;
787                     WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
788
789                     ssgTransform *obj_trans = new ssgTransform;
790                     obj_trans->setTransform( &obj_pos );
791
792                     ssgBranch *custom_obj
793                         = sgMakeTaxiSign( globals->get_matlib(),
794                                           custom_path.str(), name );
795
796                     // wire the pieces together
797                     if ( custom_obj != NULL ) {
798                         obj_trans -> addKid( custom_obj );
799                     }
800                     new_tile->addKid( obj_trans );
801
802                                 // Do we even use this one?
803                 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
804                     // load object info
805                     double lon, lat, elev, hdg;
806                     in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
807                     SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
808                             << " name = " << name 
809                             << " pos = " << lon << ", " << lat
810                             << " elevation = " << elev
811                             << " heading = " << hdg );
812
813                     // load the object itself
814                     SGPath custom_path = tile_path;
815                     custom_path.append( name );
816
817                     sgCoord obj_pos;
818                     WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
819
820                     ssgTransform *obj_trans = new ssgTransform;
821                     obj_trans->setTransform( &obj_pos );
822
823                     ssgBranch *custom_obj
824                         = sgMakeRunwaySign( globals->get_matlib(),
825                                             custom_path.str(), name );
826
827                     // wire the pieces together
828                     if ( custom_obj != NULL ) {
829                         obj_trans -> addKid( custom_obj );
830                     }
831                     new_tile->addKid( obj_trans );
832
833                                 // I don't think we use this, either
834                 } else if ( token == "RWY_LIGHTS" ) {
835                     double lon, lat, hdg, len, width;
836                     string common, end1, end2;
837                     in >> lon >> lat >> hdg >> len >> width
838                        >> common >> end1 >> end2;
839                     SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
840                             << " pos = " << lon << ", " << lat
841                             << " hdg = " << hdg
842                             << " size = " << len << ", " << width
843                             << " codes = " << common << " "
844                             << end1 << " " << end2 );
845                 } else {
846                     SG_LOG( SG_TERRAIN, SG_DEBUG,
847                             "Unknown token " << token << " in "
848                             << stg_name.str() );
849                     in >> ::skipws;
850                 }
851             }
852         }
853
854         i++;
855     }
856
857     if ( !found_tile_base ) {
858         // no tile base found, generate an ocean tile on the fly for
859         // this area
860         ssgBranch *geometry = new ssgBranch;
861         Point3D c;
862         double br;
863         if ( sgGenTile( search[0], tile_bucket, &c, &br,
864                         globals->get_matlib(), geometry ) )
865         {
866             center = c;
867             bounding_radius = br;
868             new_tile -> addKid( geometry );
869         } else {
870             delete geometry;
871             SG_LOG( SG_TERRAIN, SG_ALERT,
872                     "Warning: failed to generate ocean tile!" );
873         }
874     }
875
876     if ( new_tile != NULL ) {
877         terra_range->addKid( new_tile );
878     }
879
880     terra_transform->addKid( terra_range );
881
882     // calculate initial tile offset
883     SetOffset( globals->get_scenery()->get_center() );
884     sgCoord sgcoord;
885     sgSetCoord( &sgcoord,
886                 offset.x(), offset.y(), offset.z(),
887                 0.0, 0.0, 0.0 );
888     terra_transform->setTransform( &sgcoord );
889     // terrain->addKid( terra_transform );
890
891     // Add ground lights to scene graph if any exist
892     gnd_lights_transform = NULL;
893     gnd_lights_range = NULL;
894     if ( light_pts->getNum() ) {
895         SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
896         gnd_lights_transform = new ssgTransform;
897         gnd_lights_range = new ssgRangeSelector;
898         gnd_lights_brightness = new ssgSelector;
899         ssgLeaf *lights;
900
901         lights = gen_lights( globals->get_matlib(), light_pts, 4, 0.7 );
902         gnd_lights_brightness->addKid( lights );
903
904         lights = gen_lights( globals->get_matlib(), light_pts, 2, 0.85 );
905         gnd_lights_brightness->addKid( lights );
906
907         lights = gen_lights( globals->get_matlib(), light_pts, 1, 1.0 );
908         gnd_lights_brightness->addKid( lights );
909
910         gnd_lights_range->addKid( gnd_lights_brightness );
911         gnd_lights_transform->addKid( gnd_lights_range );
912         gnd_lights_transform->setTransform( &sgcoord );
913     }
914
915     // Update vasi lights transform
916     if ( vasi_lights_transform->getNumKids() > 0 ) {
917         vasi_lights_transform->setTransform( &sgcoord );
918     }
919
920     // Update runway lights transform
921     if ( rwy_lights_transform->getNumKids() > 0 ) {
922         rwy_lights_transform->setTransform( &sgcoord );
923     }
924
925      // Update taxi lights transform
926     if ( taxi_lights_transform->getNumKids() > 0 ) {
927         taxi_lights_transform->setTransform( &sgcoord );
928     }
929 }
930
931
932 void
933 FGTileEntry::add_ssg_nodes( ssgBranch *terrain_branch,
934                             ssgBranch *gnd_lights_branch,
935                             ssgBranch *vasi_lights_branch,
936                             ssgBranch *rwy_lights_branch,
937                             ssgBranch *taxi_lights_branch )
938 {
939     // bump up the ref count so we can remove this later without
940     // having ssg try to free the memory.
941     terra_transform->ref();
942     terrain_branch->addKid( terra_transform );
943
944     SG_LOG( SG_TERRAIN, SG_DEBUG,
945             "connected a tile into scene graph.  terra_transform = "
946             << terra_transform );
947     SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
948             << terra_transform->getNumParents() );
949
950     if ( gnd_lights_transform != NULL ) {
951         // bump up the ref count so we can remove this later without
952         // having ssg try to free the memory.
953         gnd_lights_transform->ref();
954         gnd_lights_branch->addKid( gnd_lights_transform );
955     }
956
957     if ( vasi_lights_transform != NULL ) {
958         // bump up the ref count so we can remove this later without
959         // having ssg try to free the memory.
960         vasi_lights_selector->ref();
961         vasi_lights_selector->addKid( vasi_lights_transform );
962         vasi_lights_branch->addKid( vasi_lights_selector );
963     }
964
965     if ( rwy_lights_transform != NULL ) {
966         // bump up the ref count so we can remove this later without
967         // having ssg try to free the memory.
968         rwy_lights_selector->ref();
969         rwy_lights_selector->addKid( rwy_lights_transform );
970         rwy_lights_branch->addKid( rwy_lights_selector );
971     }
972
973     if ( taxi_lights_transform != NULL ) {
974         // bump up the ref count so we can remove this later without
975         // having ssg try to free the memory.
976         taxi_lights_selector->ref();
977         taxi_lights_selector->addKid( taxi_lights_transform );
978         taxi_lights_branch->addKid( taxi_lights_selector );
979     }
980
981     loaded = true;
982 }
983
984
985 void
986 FGTileEntry::disconnect_ssg_nodes()
987 {
988     SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting ssg nodes" );
989
990     if ( ! loaded ) {
991         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
992     } else {
993         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile!  terra_transform = " << terra_transform );
994     }
995         
996     // find the terrain branch parent
997     int pcount = terra_transform->getNumParents();
998     if ( pcount > 0 ) {
999         // find the first parent (should only be one)
1000         ssgBranch *parent = terra_transform->getParent( 0 ) ;
1001         if( parent ) {
1002             // disconnect the tile (we previously ref()'d it so it
1003             // won't get freed now)
1004             parent->removeKid( terra_transform );
1005         } else {
1006             SG_LOG( SG_TERRAIN, SG_ALERT,
1007                     "parent pointer is NULL!  Dying" );
1008             exit(-1);
1009         }
1010     } else {
1011         SG_LOG( SG_TERRAIN, SG_ALERT,
1012                 "Parent count is zero for an ssg tile!  Dying" );
1013         exit(-1);
1014     }
1015
1016     // find the ground lighting branch
1017     if ( gnd_lights_transform ) {
1018         pcount = gnd_lights_transform->getNumParents();
1019         if ( pcount > 0 ) {
1020             // find the first parent (should only be one)
1021             ssgBranch *parent = gnd_lights_transform->getParent( 0 ) ;
1022             if( parent ) {
1023                 // disconnect the light branch (we previously ref()'d
1024                 // it so it won't get freed now)
1025                 parent->removeKid( gnd_lights_transform );
1026             } else {
1027                 SG_LOG( SG_TERRAIN, SG_ALERT,
1028                         "parent pointer is NULL!  Dying" );
1029                 exit(-1);
1030             }
1031         } else {
1032             SG_LOG( SG_TERRAIN, SG_ALERT,
1033                     "Parent count is zero for an ssg light tile!  Dying" );
1034             exit(-1);
1035         }
1036     }
1037
1038     // find the vasi lighting branch
1039     if ( vasi_lights_transform ) {
1040         pcount = vasi_lights_transform->getNumParents();
1041         if ( pcount > 0 ) {
1042             // find the first parent (should only be one)
1043             ssgBranch *parent = vasi_lights_transform->getParent( 0 ) ;
1044             if( parent ) {
1045                 // disconnect the light branch (we previously ref()'d
1046                 // it so it won't get freed now)
1047                 parent->removeKid( vasi_lights_transform );
1048             } else {
1049                 SG_LOG( SG_TERRAIN, SG_ALERT,
1050                         "parent pointer is NULL!  Dying" );
1051                 exit(-1);
1052             }
1053         } else {
1054             SG_LOG( SG_TERRAIN, SG_ALERT,
1055                     "Parent count is zero for an ssg light tile!  Dying" );
1056             exit(-1);
1057         }
1058     }
1059
1060     // find the runway lighting branch
1061     if ( rwy_lights_transform ) {
1062         pcount = rwy_lights_transform->getNumParents();
1063         if ( pcount > 0 ) {
1064             // find the first parent (should only be one)
1065             ssgBranch *parent = rwy_lights_transform->getParent( 0 ) ;
1066             if( parent ) {
1067                 // disconnect the light branch (we previously ref()'d
1068                 // it so it won't get freed now)
1069                 parent->removeKid( rwy_lights_transform );
1070             } else {
1071                 SG_LOG( SG_TERRAIN, SG_ALERT,
1072                         "parent pointer is NULL!  Dying" );
1073                 exit(-1);
1074             }
1075         } else {
1076             SG_LOG( SG_TERRAIN, SG_ALERT,
1077                     "Parent count is zero for an ssg light tile!  Dying" );
1078             exit(-1);
1079         }
1080     }
1081
1082     // find the taxi lighting branch
1083     if ( taxi_lights_transform ) {
1084         pcount = taxi_lights_transform->getNumParents();
1085         if ( pcount > 0 ) {
1086             // find the first parent (should only be one)
1087             ssgBranch *parent = taxi_lights_transform->getParent( 0 ) ;
1088             if( parent ) {
1089                 // disconnect the light branch (we previously ref()'d
1090                 // it so it won't get freed now)
1091                 parent->removeKid( taxi_lights_transform );
1092             } else {
1093                 SG_LOG( SG_TERRAIN, SG_ALERT,
1094                         "parent pointer is NULL!  Dying" );
1095                 exit(-1);
1096             }
1097         } else {
1098             SG_LOG( SG_TERRAIN, SG_ALERT,
1099                     "Parent count is zero for an ssg light tile!  Dying" );
1100             exit(-1);
1101         }
1102     }
1103 }