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