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