1 // tileentry.cxx -- routines to handle a scenery tile
3 // Written by Curtis Olson, started May 1998.
5 // Copyright (C) 1998 - 2001 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
28 #include <simgear/compiler.h>
32 #include <simgear/bucket/newbucket.hxx>
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/math/sg_geodesy.hxx>
35 #include <simgear/math/sg_random.h>
36 #include <simgear/misc/sgstream.hxx>
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/material/matlib.hxx>
39 #include <simgear/scene/tgdb/apt_signs.hxx>
41 #include <Aircraft/aircraft.hxx>
42 #include <Include/general.hxx>
43 #include <Main/globals.hxx>
44 #include <Main/viewer.hxx>
45 #include <Scenery/scenery.hxx>
46 #include <Time/light.hxx>
47 #include <Objects/obj.hxx>
49 #include "tileentry.hxx"
50 #include "tilemgr.hxx"
56 FGTileEntry::FGTileEntry ( const SGBucket& b )
58 center( Point3D( 0.0 ) ),
60 terra_transform( new ssgTransform ),
61 rwy_lights_transform( new ssgTransform ),
62 taxi_lights_transform( new ssgTransform ),
63 terra_range( new ssgRangeSelector ),
68 // update the contents
69 // if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
70 // SG_LOG( SG_TERRAIN, SG_ALERT,
71 // "Attempting to overwrite existing or"
72 // << " not properly freed leaf data." );
79 FGTileEntry::~FGTileEntry () {
80 // cout << "nodes = " << nodes.size() << endl;;
86 // Please keep this for reference. We use Norman's optimized routine,
87 // but here is what the routine really is doing.
89 FGTileEntry::WorldCoordinate( sgCoord *obj_pos, Point3D center,
90 double lat, double lon, double elev, double hdg)
93 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
94 lat * SGD_DEGREES_TO_RADIANS,
97 Point3D world_pos = sgGeodToCart( geod );
98 Point3D offset = world_pos - center;
101 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
103 sgVec3 obj_rt, obj_up;
104 sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
105 sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
107 sgMat4 ROT_lon, ROT_lat, ROT_hdg;
108 sgMakeRotMat4( ROT_lon, lon, obj_up );
109 sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
110 sgMakeRotMat4( ROT_hdg, hdg, obj_up );
113 sgCopyMat4( TUX, ROT_hdg );
114 sgPostMultMat4( TUX, ROT_lat );
115 sgPostMultMat4( TUX, ROT_lon );
116 sgPostMultMat4( TUX, POS );
118 sgSetCoord( obj_pos, TUX );
123 // Norman's 'fast hack' for above
124 static void WorldCoordinate( sgCoord *obj_pos, Point3D center, double lat,
125 double lon, double elev, double hdg )
127 double lon_rad = lon * SGD_DEGREES_TO_RADIANS;
128 double lat_rad = lat * SGD_DEGREES_TO_RADIANS;
129 double hdg_rad = hdg * SGD_DEGREES_TO_RADIANS;
132 Point3D geod( lon_rad, lat_rad, elev );
134 Point3D world_pos = sgGeodToCart( geod );
135 Point3D offset = world_pos - center;
139 SGfloat sin_lat = (SGfloat)sin( lat_rad );
140 SGfloat cos_lat = (SGfloat)cos( lat_rad );
141 SGfloat cos_lon = (SGfloat)cos( lon_rad );
142 SGfloat sin_lon = (SGfloat)sin( lon_rad );
143 SGfloat sin_hdg = (SGfloat)sin( hdg_rad ) ;
144 SGfloat cos_hdg = (SGfloat)cos( hdg_rad ) ;
146 mat[0][0] = cos_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - sin_hdg * (SGfloat)sin_lon;
147 mat[0][1] = cos_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + sin_hdg * (SGfloat)cos_lon;
148 mat[0][2] = -cos_hdg * (SGfloat)cos_lat;
151 mat[1][0] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - cos_hdg * (SGfloat)sin_lon;
152 mat[1][1] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + cos_hdg * (SGfloat)cos_lon;
153 mat[1][2] = sin_hdg * (SGfloat)cos_lat;
156 mat[2][0] = (SGfloat)cos_lat * (SGfloat)cos_lon;
157 mat[2][1] = (SGfloat)cos_lat * (SGfloat)sin_lon;
158 mat[2][2] = (SGfloat)sin_lat;
161 mat[3][0] = offset.x();
162 mat[3][1] = offset.y();
163 mat[3][2] = offset.z();
166 sgSetCoord( obj_pos, mat );
170 // recurse an ssg tree and call removeKid() on every node from the
171 // bottom up. Leaves the original branch in existance, but empty so
172 // it can be removed by the calling routine.
173 static void my_remove_branch( ssgBranch * branch ) {
174 for ( ssgEntity *k = branch->getKid( 0 );
176 k = branch->getNextKid() )
178 if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
179 my_remove_branch( (ssgBranch *)k );
180 branch -> removeKid ( k );
181 } else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
182 branch -> removeKid ( k ) ;
188 // Free "n" leaf elements of an ssg tree. returns the number of
189 // elements freed. An empty branch node is considered a leaf. This
190 // is intended to spread the load of freeing a complex tile out over
192 static int fgPartialFreeSSGtree( ssgBranch *b, int n ) {
195 // for testing: we could call the following two lines and replace
196 // the functionality of this entire function and everything will
197 // get properly freed, but it will happen all at once and could
198 // cause a huge frame rate hit.
206 // we still have some delete budget left
207 // if ( b->getNumKids() > 100 ) {
208 // cout << "large family = " << b->getNumKids() << endl;
210 // deleting in reverse would help if my plib patch get's
211 // applied, but for now it will make things slower.
212 // for ( int i = b->getNumKids() - 1; i >= 0 ; --i ) {
213 for ( int i = 0; i < b->getNumKids(); ++i ) {
214 ssgEntity *kid = b->getKid(i);
215 if ( kid->isAKindOf( ssgTypeBranch() ) && kid->getRef() <= 1 ) {
216 int result = fgPartialFreeSSGtree( (ssgBranch *)kid, n );
217 num_deletes += result;
223 // remove the kid if (a) it is now empty -or- (b) it's ref
224 // count is > zero at which point we don't care if it's
225 // empty, we don't want to touch it's contents.
226 if ( kid->getNumKids() == 0 || kid->getRef() > 1 ) {
238 // Clean up the memory used by this tile and delete the arrays used by
239 // ssg as well as the whole ssg branch
240 bool FGTileEntry::free_tile() {
241 int delete_size = 100;
242 SG_LOG( SG_TERRAIN, SG_DEBUG,
243 "FREEING TILE = (" << tile_bucket << ")" );
245 SG_LOG( SG_TERRAIN, SG_DEBUG, "(start) free_tracker = " << free_tracker );
247 if ( !(free_tracker & NODES) ) {
248 free_tracker |= NODES;
249 } else if ( !(free_tracker & VEC_PTRS) ) {
250 free_tracker |= VEC_PTRS;
251 } else if ( !(free_tracker & TERRA_NODE) ) {
252 // delete the terrain branch (this should already have been
253 // disconnected from the scene graph)
254 SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING terra_transform" );
255 if ( fgPartialFreeSSGtree( terra_transform, delete_size ) == 0 ) {
256 ssgDeRefDelete( terra_transform );
257 free_tracker |= TERRA_NODE;
259 } else if ( !(free_tracker & GROUND_LIGHTS) && gnd_lights_transform ) {
260 // delete the terrain lighting branch (this should already have been
261 // disconnected from the scene graph)
262 SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING gnd_lights_transform" );
263 if ( fgPartialFreeSSGtree( gnd_lights_transform, delete_size ) == 0 ) {
264 ssgDeRefDelete( gnd_lights_transform );
265 free_tracker |= GROUND_LIGHTS;
267 } else if ( !(free_tracker & RWY_LIGHTS) && rwy_lights_transform ) {
268 // delete the runway lighting branch (this should already have
269 // been disconnected from the scene graph)
270 SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING rwy_lights_transform" );
271 if ( fgPartialFreeSSGtree( rwy_lights_transform, delete_size ) == 0 ) {
272 ssgDeRefDelete( rwy_lights_transform );
273 free_tracker |= RWY_LIGHTS;
275 } else if ( !(free_tracker & TAXI_LIGHTS) && taxi_lights_transform ) {
276 // delete the taxi lighting branch (this should already have been
277 // disconnected from the scene graph)
278 SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING taxi_lights_transform" );
279 if ( fgPartialFreeSSGtree( taxi_lights_transform, delete_size ) == 0 ) {
280 ssgDeRefDelete( taxi_lights_transform );
281 free_tracker |= TAXI_LIGHTS;
283 } else if ( !(free_tracker & LIGHTMAPS) ) {
284 free_tracker |= LIGHTMAPS;
289 SG_LOG( SG_TERRAIN, SG_DEBUG, "(end) free_tracker = " << free_tracker );
291 // if we fall down to here, we still have work todo, return false
296 // Update the ssg transform node for this tile so it can be
297 // properly drawn relative to our (0,0,0) point
298 void FGTileEntry::prep_ssg_node( const Point3D& p, sgVec3 up, float vis) {
299 if ( !loaded ) return;
303 // visibility can change from frame to frame so we update the
304 // range selector cutoff's each time.
305 terra_range->setRange( 0, SG_ZERO );
306 terra_range->setRange( 1, vis + bounding_radius );
308 if ( gnd_lights_range ) {
309 gnd_lights_range->setRange( 0, SG_ZERO );
310 gnd_lights_range->setRange( 1, vis * 1.5 + bounding_radius );
314 sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
315 terra_transform->setTransform( sgTrans );
317 if ( gnd_lights_transform ) {
318 // we need to lift the lights above the terrain to avoid
319 // z-buffer fighting. We do this based on our altitude and
320 // the distance this tile is away from scenery center.
322 // we expect 'up' to be a unit vector coming in, but since we
323 // modify the value of lift_vec, we need to create a local
326 sgCopyVec3( lift_vec, up );
329 agl = globals->get_current_view()->getAltitudeASL_ft()
330 * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev();
332 // sgTrans just happens to be the
333 // vector from scenery center to the center of this tile which
334 // is what we want to calculate the distance of
336 sgCopyVec3( to, sgTrans );
337 double dist = sgLengthVec3( to );
339 if ( general.get_glDepthBits() > 16 ) {
340 sgScaleVec3( lift_vec, 10.0 + agl / 100.0 + dist / 10000 );
342 sgScaleVec3( lift_vec, 10.0 + agl / 20.0 + dist / 5000 );
346 sgCopyVec3( lt_trans, sgTrans );
348 sgAddVec3( lt_trans, lift_vec );
349 gnd_lights_transform->setTransform( lt_trans );
351 // select which set of lights based on sun angle
352 float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
353 if ( sun_angle > 95 ) {
354 gnd_lights_brightness->select(0x04);
355 } else if ( sun_angle > 92 ) {
356 gnd_lights_brightness->select(0x02);
357 } else if ( sun_angle > 89 ) {
358 gnd_lights_brightness->select(0x01);
360 gnd_lights_brightness->select(0x00);
364 if ( rwy_lights_transform ) {
365 // we need to lift the lights above the terrain to avoid
366 // z-buffer fighting. We do this based on our altitude and
367 // the distance this tile is away from scenery center.
370 sgCopyVec3( lift_vec, up );
372 // we fudge agl by 30 meters so that the lifting function
373 // doesn't phase in until we are > 30m agl.
375 agl = globals->get_current_view()->getAltitudeASL_ft()
376 * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev()
382 if ( general.get_glDepthBits() > 16 ) {
383 sgScaleVec3( lift_vec, 0.0 + agl / 500.0 );
385 sgScaleVec3( lift_vec, 0.0 + agl / 150.0 );
389 sgCopyVec3( lt_trans, sgTrans );
391 sgAddVec3( lt_trans, lift_vec );
392 rwy_lights_transform->setTransform( lt_trans );
394 // select which set of lights based on sun angle
395 // float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
396 // if ( sun_angle > 95 ) {
397 // gnd_lights_brightness->select(0x04);
398 // } else if ( sun_angle > 92 ) {
399 // gnd_lights_brightness->select(0x02);
400 // } else if ( sun_angle > 89 ) {
401 // gnd_lights_brightness->select(0x01);
403 // gnd_lights_brightness->select(0x00);
407 if ( taxi_lights_transform ) {
408 // we need to lift the lights above the terrain to avoid
409 // z-buffer fighting. We do this based on our altitude and
410 // the distance this tile is away from scenery center.
413 sgCopyVec3( lift_vec, up );
415 // we fudge agl by 30 meters so that the lifting function
416 // doesn't phase in until we are > 30m agl.
418 agl = globals->get_current_view()->getAltitudeASL_ft()
419 * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev()
425 if ( general.get_glDepthBits() > 16 ) {
426 sgScaleVec3( lift_vec, 0.0 + agl / 500.0 );
428 sgScaleVec3( lift_vec, 0.0 + agl / 150.0 );
432 sgCopyVec3( lt_trans, sgTrans );
434 sgAddVec3( lt_trans, lift_vec );
435 taxi_lights_transform->setTransform( lt_trans );
437 // select which set of lights based on sun angle
438 // float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
439 // if ( sun_angle > 95 ) {
440 // gnd_lights_brightness->select(0x04);
441 // } else if ( sun_angle > 92 ) {
442 // gnd_lights_brightness->select(0x02);
443 // } else if ( sun_angle > 89 ) {
444 // gnd_lights_brightness->select(0x01);
446 // gnd_lights_brightness->select(0x00);
452 // Set up lights rendering call backs
453 static int fgLightsPredraw( ssgEntity *e ) {
455 #ifdef GL_EXT_point_parameters
456 if (glutExtensionSupported("GL_EXT_point_parameters")) {
457 static float quadratic[3] = {1.0, 0.01, 0.0001};
458 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, quadratic);
459 glPointParameterfEXT(GL_POINT_SIZE_MIN_EXT, 1.0);
467 static int fgLightsPostdraw( ssgEntity *e ) {
469 #ifdef GL_EXT_point_parameters
470 if (glutExtensionSupported("GL_EXT_point_parameters")) {
471 static float default_attenuation[3] = {1.0, 0.0, 0.0};
472 glPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT,
473 default_attenuation);
482 ssgLeaf* FGTileEntry::gen_lights( SGMaterialLib *matlib, ssgVertexArray *lights,
483 int inc, float bright )
485 // generate a repeatable random seed
486 float *p1 = lights->get( 0 );
487 unsigned int *seed = (unsigned int *)p1;
490 int size = lights->getNum() / inc;
492 // Allocate ssg structure
493 ssgVertexArray *vl = new ssgVertexArray( size + 1 );
494 ssgNormalArray *nl = NULL;
495 ssgTexCoordArray *tl = NULL;
496 ssgColourArray *cl = new ssgColourArray( size + 1 );
499 for ( int i = 0; i < lights->getNum(); ++i ) {
500 // this loop is slightly less efficient than it otherwise
501 // could be, but we want a red light to always be red, and a
502 // yellow light to always be yellow, etc. so we are trying to
503 // preserve the random sequence.
504 float zombie = sg_random();
505 if ( i % inc == 0 ) {
506 vl->add( lights->get(i) );
508 // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
509 float factor = sg_random();
512 if ( zombie > 0.5 ) {
513 // 50% chance of yellowish
514 sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
515 } else if ( zombie > 0.15 ) {
516 // 35% chance of whitish
517 sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
518 } else if ( zombie > 0.05 ) {
519 // 10% chance of orangish
520 sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
522 // 5% chance of redish
523 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
531 new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
534 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
535 leaf->setState( mat->get_state() );
536 leaf->setCallback( SSG_CALLBACK_PREDRAW, fgLightsPredraw );
537 leaf->setCallback( SSG_CALLBACK_POSTDRAW, fgLightsPostdraw );
543 bool FGTileEntry::obj_load( const string& path,
545 ssgBranch* rwy_lights,
546 ssgBranch* taxi_lights,
547 ssgVertexArray* ground_lights, bool is_base )
549 Point3D c; // returned center point
550 double br; // returned bounding radius
552 // try loading binary format
553 if ( fgBinObjLoad( path, is_base,
554 &c, &br, globals->get_matlib(), geometry,
555 rwy_lights, taxi_lights, ground_lights ) )
559 bounding_radius = br;
562 // default to an ocean tile
563 if ( fgGenTile( path, tile_bucket, &c, &br,
564 globals->get_matlib(), geometry ) )
567 bounding_radius = br;
569 SG_LOG( SG_TERRAIN, SG_ALERT,
570 "Warning: failed to generate ocean tile!" );
574 return (geometry != NULL);
579 FGTileEntry::load( const SGPath& base, bool is_base )
581 SG_LOG( SG_TERRAIN, SG_INFO, "load() base = " << base.str() );
583 // Generate names for later use
584 string index_str = tile_bucket.gen_index_str();
586 SGPath tile_path = base;
587 tile_path.append( tile_bucket.gen_base_path() );
589 SGPath basename = tile_path;
590 basename.append( index_str );
591 // string path = basename.str();
593 SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << basename.str() );
595 #define FG_MAX_LIGHTS 1000
597 // obj_load() will generate ground lighting for us ...
598 ssgVertexArray *light_pts = new ssgVertexArray( 100 );
600 ssgBranch* new_tile = new ssgBranch;
602 // Check for master .stg (scene terra gear) file
603 SGPath stg_name = basename;
604 stg_name.concat( ".stg" );
606 sg_gzifstream in( stg_name.str() );
608 if ( in.is_open() ) {
611 while ( ! in.eof() ) {
614 if ( token == "OBJECT_BASE" ) {
615 in >> name >> ::skipws;
616 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
617 << " name = " << name );
619 SGPath custom_path = tile_path;
620 custom_path.append( name );
622 ssgBranch *geometry = new ssgBranch;
623 if ( obj_load( custom_path.str(),
624 geometry, NULL, NULL, light_pts, true ) )
626 new_tile -> addKid( geometry );
630 } else if ( token == "OBJECT" ) {
631 in >> name >> ::skipws;
632 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
633 << " name = " << name );
635 SGPath custom_path = tile_path;
636 custom_path.append( name );
638 ssgBranch *geometry = new ssgBranch;
639 ssgBranch *rwy_lights = new ssgBranch;
640 ssgBranch *taxi_lights = new ssgBranch;
641 if ( obj_load( custom_path.str(),
642 geometry, rwy_lights, taxi_lights,
645 if ( geometry -> getNumKids() > 0 ) {
646 new_tile -> addKid( geometry );
650 if ( rwy_lights -> getNumKids() > 0 ) {
651 rwy_lights_transform -> addKid( rwy_lights );
655 if ( taxi_lights -> getNumKids() > 0 ) {
656 taxi_lights_transform -> addKid( taxi_lights );
666 } else if ( token == "OBJECT_STATIC" ||
667 token == "OBJECT_SHARED" ) {
669 double lon, lat, elev, hdg;
670 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
671 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
672 << " name = " << name
673 << " pos = " << lon << ", " << lat
674 << " elevation = " << elev
675 << " heading = " << hdg );
677 // object loading is deferred to main render thread,
678 // but lets figure out the paths right now.
680 if ( token == "OBJECT_STATIC" )
681 custom_path= tile_path;
682 custom_path.append( name );
685 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
687 ssgTransform *obj_trans = new ssgTransform;
688 obj_trans->setTransform( &obj_pos );
690 // wire as much of the scene graph together as we can
691 new_tile->addKid( obj_trans );
693 // bump up the pending models count
696 // push an entry onto the model load queue
698 = new FGDeferredModel( custom_path.str(), tile_path.str(),
700 FGTileMgr::model_ready( dm );
701 } else if ( token == "OBJECT_TAXI_SIGN" ) {
703 double lon, lat, elev, hdg;
704 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
705 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
706 << " name = " << name
707 << " pos = " << lon << ", " << lat
708 << " elevation = " << elev
709 << " heading = " << hdg );
711 // load the object itself
712 SGPath custom_path = tile_path;
713 custom_path.append( name );
716 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
718 ssgTransform *obj_trans = new ssgTransform;
719 obj_trans->setTransform( &obj_pos );
721 ssgBranch *custom_obj
722 = gen_taxi_sign( globals->get_matlib(),
723 custom_path.str(), name );
725 // wire the pieces together
726 if ( custom_obj != NULL ) {
727 obj_trans -> addKid( custom_obj );
729 new_tile->addKid( obj_trans );
730 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
732 double lon, lat, elev, hdg;
733 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
734 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
735 << " name = " << name
736 << " pos = " << lon << ", " << lat
737 << " elevation = " << elev
738 << " heading = " << hdg );
740 // load the object itself
741 SGPath custom_path = tile_path;
742 custom_path.append( name );
745 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
747 ssgTransform *obj_trans = new ssgTransform;
748 obj_trans->setTransform( &obj_pos );
750 ssgBranch *custom_obj
751 = gen_runway_sign( globals->get_matlib(),
752 custom_path.str(), name );
754 // wire the pieces together
755 if ( custom_obj != NULL ) {
756 obj_trans -> addKid( custom_obj );
758 new_tile->addKid( obj_trans );
759 } else if ( token == "RWY_LIGHTS" ) {
760 double lon, lat, hdg, len, width;
761 string common, end1, end2;
762 in >> lon >> lat >> hdg >> len >> width
763 >> common >> end1 >> end2;
764 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
765 << " pos = " << lon << ", " << lat
767 << " size = " << len << ", " << width
768 << " codes = " << common << " "
769 << end1 << " " << end2 );
771 SG_LOG( SG_TERRAIN, SG_ALERT,
772 "Unknown token " << token << " in "
778 // no .stg file, generate an ocean tile on the fly for this
780 ssgBranch *geometry = new ssgBranch;
783 if ( fgGenTile( basename.str(), tile_bucket, &c, &br,
784 globals->get_matlib(), geometry ) ) {
786 bounding_radius = br;
787 new_tile -> addKid( geometry );
790 SG_LOG( SG_TERRAIN, SG_ALERT,
791 "Warning: failed to generate ocean tile!" );
795 if ( new_tile != NULL ) {
796 terra_range->addKid( new_tile );
799 terra_transform->addKid( terra_range );
801 // calculate initial tile offset
802 SetOffset( globals->get_scenery()->get_center() );
804 sgSetCoord( &sgcoord,
805 offset.x(), offset.y(), offset.z(),
807 terra_transform->setTransform( &sgcoord );
808 // terrain->addKid( terra_transform );
810 // Add ground lights to scene graph if any exist
811 gnd_lights_transform = NULL;
812 gnd_lights_range = NULL;
813 if ( light_pts->getNum() ) {
814 SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
815 gnd_lights_transform = new ssgTransform;
816 gnd_lights_range = new ssgRangeSelector;
817 gnd_lights_brightness = new ssgSelector;
820 lights = gen_lights( globals->get_matlib(), light_pts, 4, 0.7 );
821 gnd_lights_brightness->addKid( lights );
823 lights = gen_lights( globals->get_matlib(), light_pts, 2, 0.85 );
824 gnd_lights_brightness->addKid( lights );
826 lights = gen_lights( globals->get_matlib(), light_pts, 1, 1.0 );
827 gnd_lights_brightness->addKid( lights );
829 gnd_lights_range->addKid( gnd_lights_brightness );
830 gnd_lights_transform->addKid( gnd_lights_range );
831 gnd_lights_transform->setTransform( &sgcoord );
834 // Add runway lights to scene graph if any exist
835 if ( rwy_lights_transform->getNumKids() > 0 ) {
836 SG_LOG( SG_TERRAIN, SG_DEBUG, "adding runway lights" );
837 rwy_lights_transform->setTransform( &sgcoord );
840 // Add taxi lights to scene graph if any exist
841 if ( taxi_lights_transform->getNumKids() > 0 ) {
842 SG_LOG( SG_TERRAIN, SG_DEBUG, "adding taxi lights" );
843 taxi_lights_transform->setTransform( &sgcoord );
849 FGTileEntry::add_ssg_nodes( ssgBranch* terrain_branch,
850 ssgBranch* gnd_lights_branch,
851 ssgBranch* rwy_lights_branch,
852 ssgBranch* taxi_lights_branch )
854 // bump up the ref count so we can remove this later without
855 // having ssg try to free the memory.
856 terra_transform->ref();
857 terrain_branch->addKid( terra_transform );
859 SG_LOG( SG_TERRAIN, SG_DEBUG,
860 "connected a tile into scene graph. terra_transform = "
861 << terra_transform );
862 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
863 << terra_transform->getNumParents() );
865 if ( gnd_lights_transform != NULL ) {
866 // bump up the ref count so we can remove this later without
867 // having ssg try to free the memory.
868 gnd_lights_transform->ref();
869 gnd_lights_branch->addKid( gnd_lights_transform );
872 if ( rwy_lights_transform != NULL ) {
873 // bump up the ref count so we can remove this later without
874 // having ssg try to free the memory.
875 rwy_lights_transform->ref();
876 rwy_lights_branch->addKid( rwy_lights_transform );
879 if ( taxi_lights_transform != NULL ) {
880 // bump up the ref count so we can remove this later without
881 // having ssg try to free the memory.
882 taxi_lights_transform->ref();
883 taxi_lights_branch->addKid( taxi_lights_transform );
891 FGTileEntry::disconnect_ssg_nodes()
893 SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting ssg nodes" );
896 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
898 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! terra_transform = " << terra_transform );
901 // find the terrain branch parent
902 int pcount = terra_transform->getNumParents();
904 // find the first parent (should only be one)
905 ssgBranch *parent = terra_transform->getParent( 0 ) ;
907 // disconnect the tile (we previously ref()'d it so it
908 // won't get freed now)
909 parent->removeKid( terra_transform );
911 SG_LOG( SG_TERRAIN, SG_ALERT,
912 "parent pointer is NULL! Dying" );
916 SG_LOG( SG_TERRAIN, SG_ALERT,
917 "Parent count is zero for an ssg tile! Dying" );
921 // find the ground lighting branch
922 if ( gnd_lights_transform ) {
923 pcount = gnd_lights_transform->getNumParents();
925 // find the first parent (should only be one)
926 ssgBranch *parent = gnd_lights_transform->getParent( 0 ) ;
928 // disconnect the light branch (we previously ref()'d
929 // it so it won't get freed now)
930 parent->removeKid( gnd_lights_transform );
932 SG_LOG( SG_TERRAIN, SG_ALERT,
933 "parent pointer is NULL! Dying" );
937 SG_LOG( SG_TERRAIN, SG_ALERT,
938 "Parent count is zero for an ssg light tile! Dying" );
943 // find the runway lighting branch
944 if ( rwy_lights_transform ) {
945 pcount = rwy_lights_transform->getNumParents();
947 // find the first parent (should only be one)
948 ssgBranch *parent = rwy_lights_transform->getParent( 0 ) ;
950 // disconnect the light branch (we previously ref()'d
951 // it so it won't get freed now)
952 parent->removeKid( rwy_lights_transform );
954 SG_LOG( SG_TERRAIN, SG_ALERT,
955 "parent pointer is NULL! Dying" );
959 SG_LOG( SG_TERRAIN, SG_ALERT,
960 "Parent count is zero for an ssg light tile! Dying" );
965 // find the taxi lighting branch
966 if ( taxi_lights_transform ) {
967 pcount = taxi_lights_transform->getNumParents();
969 // find the first parent (should only be one)
970 ssgBranch *parent = taxi_lights_transform->getParent( 0 ) ;
972 // disconnect the light branch (we previously ref()'d
973 // it so it won't get freed now)
974 parent->removeKid( taxi_lights_transform );
976 SG_LOG( SG_TERRAIN, SG_ALERT,
977 "parent pointer is NULL! Dying" );
981 SG_LOG( SG_TERRAIN, SG_ALERT,
982 "Parent count is zero for an ssg light tile! Dying" );