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>
30 #ifdef SG_MATH_EXCEPTION_CLASH
34 #include STL_FUNCTIONAL
35 #include STL_ALGORITHM
38 #include <simgear/bucket/newbucket.hxx>
39 #include <simgear/debug/logstream.hxx>
40 #include <simgear/math/sg_geodesy.hxx>
41 #include <simgear/math/sg_random.h>
42 #include <simgear/misc/sgstream.hxx>
44 #include <Aircraft/aircraft.hxx>
45 #include <Include/general.hxx>
46 #include <Main/globals.hxx>
47 #include <Scenery/scenery.hxx>
48 #include <Time/light.hxx>
49 #include <Objects/matlib.hxx>
50 #include <Objects/newmat.hxx>
51 #include <Objects/obj.hxx>
53 #include "tileentry.hxx"
54 #include "tilemgr.hxx"
56 SG_USING_STD(for_each);
57 SG_USING_STD(mem_fun_ref);
62 FGTileEntry::FGTileEntry ( const SGBucket& b )
64 center( Point3D( 0.0 ) ),
66 terra_transform( new ssgTransform ),
67 terra_range( new ssgRangeSelector ),
73 // update the contents
74 // if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
75 // SG_LOG( SG_TERRAIN, SG_ALERT,
76 // "Attempting to overwrite existing or"
77 // << " not properly freed leaf data." );
84 FGTileEntry::~FGTileEntry () {
85 // cout << "nodes = " << nodes.size() << endl;;
90 // recurse an ssg tree and call removeKid() on every node from the
91 // bottom up. Leaves the original branch in existance, but empty so
92 // it can be removed by the calling routine.
93 static void my_remove_branch( ssgBranch * branch ) {
94 for ( ssgEntity *k = branch->getKid( 0 );
96 k = branch->getNextKid() )
98 if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
99 my_remove_branch( (ssgBranch *)k );
100 branch -> removeKid ( k );
101 } else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
102 branch -> removeKid ( k ) ;
108 #ifdef WISH_PLIB_WAS_THREADED // but it isn't
110 // Schedule tile to be freed/removed
111 void FGTileEntry::sched_removal() {
112 global_tile_mgr.ready_to_delete( this );
118 // Clean up the memory used by this tile and delete the arrays used by
119 // ssg as well as the whole ssg branch
120 void FGTileEntry::free_tile() {
122 SG_LOG( SG_TERRAIN, SG_INFO,
123 "FREEING TILE = (" << tile_bucket << ")" );
125 SG_LOG( SG_TERRAIN, SG_DEBUG,
126 " deleting " << nodes.size() << " nodes" );
129 // delete the ssg structures
130 SG_LOG( SG_TERRAIN, SG_DEBUG,
131 " deleting (leaf data) vertex, normal, and "
132 << " texture coordinate arrays" );
134 for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
135 delete [] vec3_ptrs[i];
139 for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
140 delete [] vec2_ptrs[i];
144 for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
145 delete index_ptrs[i];
149 // delete the terrain branch (this should already have been
150 // disconnected from the scene graph)
151 ssgDeRefDelete( terra_transform );
153 if ( lights_transform ) {
154 // delete the terrain lighting branch (this should already have been
155 // disconnected from the scene graph)
156 ssgDeRefDelete( lights_transform );
161 // Update the ssg transform node for this tile so it can be
162 // properly drawn relative to our (0,0,0) point
163 void FGTileEntry::prep_ssg_node( const Point3D& p, float vis) {
164 if ( !loaded ) return;
168 // #define USE_UP_AND_COMING_PLIB_FEATURE
169 #ifdef USE_UP_AND_COMING_PLIB_FEATURE
170 terra_range->setRange( 0, SG_ZERO );
171 terra_range->setRange( 1, vis + bounding_radius );
172 lights_range->setRange( 0, SG_ZERO );
173 lights_range->setRange( 1, vis * 1.5 + bounding_radius );
177 ranges[1] = vis + bounding_radius;
178 terra_range->setRanges( ranges, 2 );
179 if ( lights_range ) {
180 ranges[1] = vis * 1.5 + bounding_radius;
181 lights_range->setRanges( ranges, 2 );
185 sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
186 terra_transform->setTransform( sgTrans );
188 if ( lights_transform ) {
189 // we need to lift the lights above the terrain to avoid
190 // z-buffer fighting. We do this based on our altitude and
191 // the distance this tile is away from scenery center.
194 sgCopyVec3( up, globals->get_current_view()->get_world_up() );
197 if ( current_aircraft.fdm_state ) {
198 agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
204 // sgTrans just happens to be the
205 // vector from scenery center to the center of this tile which
206 // is what we want to calculate the distance of
208 sgCopyVec3( to, sgTrans );
209 double dist = sgLengthVec3( to );
211 if ( general.get_glDepthBits() > 16 ) {
212 sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
214 sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
216 sgAddVec3( sgTrans, up );
217 lights_transform->setTransform( sgTrans );
219 // select which set of lights based on sun angle
220 float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
221 if ( sun_angle > 95 ) {
222 lights_brightness->select(0x04);
223 } else if ( sun_angle > 92 ) {
224 lights_brightness->select(0x02);
225 } else if ( sun_angle > 89 ) {
226 lights_brightness->select(0x01);
228 lights_brightness->select(0x00);
234 ssgLeaf* FGTileEntry::gen_lights( ssgVertexArray *lights, int inc, float bright ) {
235 // generate a repeatable random seed
236 float *p1 = lights->get( 0 );
237 unsigned int *seed = (unsigned int *)p1;
240 int size = lights->getNum() / inc;
242 // Allocate ssg structure
243 ssgVertexArray *vl = new ssgVertexArray( size + 1 );
244 ssgNormalArray *nl = NULL;
245 ssgTexCoordArray *tl = NULL;
246 ssgColourArray *cl = new ssgColourArray( size + 1 );
249 for ( int i = 0; i < lights->getNum(); ++i ) {
250 // this loop is slightly less efficient than it otherwise
251 // could be, but we want a red light to always be red, and a
252 // yellow light to always be yellow, etc. so we are trying to
253 // preserve the random sequence.
254 float zombie = sg_random();
255 if ( i % inc == 0 ) {
256 vl->add( lights->get(i) );
258 // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
259 float factor = sg_random();
262 if ( zombie > 0.5 ) {
263 // 50% chance of yellowish
264 sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
265 } else if ( zombie > 0.15 ) {
266 // 35% chance of whitish
267 sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
268 } else if ( zombie > 0.05 ) {
269 // 10% chance of orangish
270 sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
272 // 5% chance of redish
273 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
281 new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
284 FGNewMat *newmat = material_lib.find( "LIGHTS" );
285 leaf->setState( newmat->get_state() );
292 FGTileEntry::obj_load( const std::string& path,
293 ssgVertexArray* lights, bool is_base )
295 ssgBranch* result = 0;
297 // try loading binary format
298 result = fgBinObjLoad( path, this, lights, is_base );
299 if ( result == NULL ) {
300 // next try the older ascii format
301 result = fgAsciiObjLoad( path, this, lights, is_base );
302 if ( result == NULL ) {
303 // default to an ocean tile
304 result = fgGenTile( path, this );
313 FGTileEntry::load( const SGPath& base, bool is_base )
315 string index_str = tile_bucket.gen_index_str();
317 SGPath tile_path = base;
318 // Generate name of file to load.
319 tile_path.append( tile_bucket.gen_base_path() );
320 SGPath basename = tile_path;
321 basename.append( index_str );
322 string path = basename.str();
324 SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
326 // fgObjLoad will generate ground lighting for us ...
327 ssgVertexArray *light_pts = new ssgVertexArray( 100 );
329 ssgBranch* new_tile = obj_load( path, light_pts, is_base );
330 if ( new_tile != NULL ) {
331 terra_range->addKid( new_tile );
334 // load custom objects
335 SG_LOG( SG_TERRAIN, SG_DEBUG, "Checking for custom objects ..." );
337 SGPath index_path = tile_path;
338 index_path.append( index_str );
339 index_path.concat( ".ind" );
341 SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
343 sg_gzifstream in( index_path.str() );
345 if ( in.is_open() ) {
348 while ( ! in.eof() ) {
351 if ( token == "OBJECT" ) {
352 in >> name >> ::skipws;
353 SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
354 << " name = " << name );
356 SGPath custom_path = tile_path;
357 custom_path.append( name );
358 ssgBranch *custom_obj
359 = obj_load( custom_path.str(), NULL, false );
360 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
361 new_tile -> addKid( custom_obj );
363 } else if ( token == "OBJECT_STATIC" ) {
365 double lon, lat, elev, hdg;
366 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
367 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
368 << " name = " << name
369 << " pos = " << lon << ", " << lat
370 << " elevation = " << elev
371 << " heading = " << hdg );
373 // object loading is deferred to main render thread,
374 // but lets figure out the paths right now.
375 SGPath custom_path = tile_path;
376 custom_path.append( name );
379 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
380 lat * SGD_DEGREES_TO_RADIANS,
382 Point3D world_pos = sgGeodToCart( geod );
383 Point3D offset = world_pos - center;
385 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
387 sgVec3 obj_rt, obj_up;
388 sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
389 sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
391 sgMat4 ROT_lon, ROT_lat, ROT_hdg;
392 sgMakeRotMat4( ROT_lon, lon, obj_up );
393 sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
394 sgMakeRotMat4( ROT_hdg, hdg, obj_up );
397 sgCopyMat4( TUX, ROT_hdg );
398 sgPostMultMat4( TUX, ROT_lat );
399 sgPostMultMat4( TUX, ROT_lon );
400 sgPostMultMat4( TUX, POS );
403 sgSetCoord( &obj_pos, TUX );
404 ssgTransform *obj_trans = new ssgTransform;
405 obj_trans->setTransform( &obj_pos );
407 // wire as much of the scene graph together as we can
408 new_tile->addKid( obj_trans );
410 // bump up the pending models count
413 // push an entry onto the model load queue
415 = new FGDeferredModel( custom_path.str(), tile_path.str(),
417 FGTileMgr::model_ready( dm );
418 } else if ( token == "OBJECT_TAXI_SIGN" ) {
420 double lon, lat, elev, hdg;
421 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
422 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
423 << " name = " << name
424 << " pos = " << lon << ", " << lat
425 << " elevation = " << elev
426 << " heading = " << hdg );
428 // load the object itself
429 SGPath custom_path = tile_path;
430 custom_path.append( name );
433 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
434 lat * SGD_DEGREES_TO_RADIANS,
436 Point3D world_pos = sgGeodToCart( geod );
437 Point3D offset = world_pos - center;
439 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
441 sgVec3 obj_rt, obj_up;
442 sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
443 sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
445 sgMat4 ROT_lon, ROT_lat, ROT_hdg;
446 sgMakeRotMat4( ROT_lon, lon, obj_up );
447 sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
448 sgMakeRotMat4( ROT_hdg, hdg, obj_up );
451 sgCopyMat4( TUX, ROT_hdg );
452 sgPostMultMat4( TUX, ROT_lat );
453 sgPostMultMat4( TUX, ROT_lon );
454 sgPostMultMat4( TUX, POS );
457 sgSetCoord( &obj_pos, TUX );
458 ssgTransform *obj_trans = new ssgTransform;
459 obj_trans->setTransform( &obj_pos );
461 ssgBranch *custom_obj
462 = gen_taxi_sign( custom_path.str(), name );
464 // wire the pieces together
465 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
466 obj_trans -> addKid( custom_obj );
468 new_tile->addKid( obj_trans );
469 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
471 double lon, lat, elev, hdg;
472 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
473 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
474 << " name = " << name
475 << " pos = " << lon << ", " << lat
476 << " elevation = " << elev
477 << " heading = " << hdg );
479 // load the object itself
480 SGPath custom_path = tile_path;
481 custom_path.append( name );
484 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
485 lat * SGD_DEGREES_TO_RADIANS,
487 Point3D world_pos = sgGeodToCart( geod );
488 Point3D offset = world_pos - center;
490 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
492 sgVec3 obj_rt, obj_up;
493 sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
494 sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
496 sgMat4 ROT_lon, ROT_lat, ROT_hdg;
497 sgMakeRotMat4( ROT_lon, lon, obj_up );
498 sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
499 sgMakeRotMat4( ROT_hdg, hdg, obj_up );
502 sgCopyMat4( TUX, ROT_hdg );
503 sgPostMultMat4( TUX, ROT_lat );
504 sgPostMultMat4( TUX, ROT_lon );
505 sgPostMultMat4( TUX, POS );
508 sgSetCoord( &obj_pos, TUX );
509 ssgTransform *obj_trans = new ssgTransform;
510 obj_trans->setTransform( &obj_pos );
512 ssgBranch *custom_obj
513 = gen_runway_sign( custom_path.str(), name );
515 // wire the pieces together
516 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
517 obj_trans -> addKid( custom_obj );
519 new_tile->addKid( obj_trans );
521 SG_LOG( SG_TERRAIN, SG_ALERT,
522 "Unknown token " << token << " in "
523 << index_path.str() );
529 terra_transform->addKid( terra_range );
531 // calculate initial tile offset
532 SetOffset( scenery.center );
534 sgSetCoord( &sgcoord,
535 offset.x(), offset.y(), offset.z(),
537 terra_transform->setTransform( &sgcoord );
538 // terrain->addKid( terra_transform );
540 lights_transform = NULL;
542 /* uncomment this section for testing ground lights */
543 if ( light_pts->getNum() ) {
544 SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
545 lights_transform = new ssgTransform;
546 lights_range = new ssgRangeSelector;
547 lights_brightness = new ssgSelector;
550 lights = gen_lights( light_pts, 4, 0.7 );
551 lights_brightness->addKid( lights );
553 lights = gen_lights( light_pts, 2, 0.85 );
554 lights_brightness->addKid( lights );
556 lights = gen_lights( light_pts, 1, 1.0 );
557 lights_brightness->addKid( lights );
559 lights_range->addKid( lights_brightness );
560 lights_transform->addKid( lights_range );
561 lights_transform->setTransform( &sgcoord );
562 // ground->addKid( lights_transform );
564 /* end of ground light section */
569 FGTileEntry::add_ssg_nodes( ssgBranch* terrain, ssgBranch* ground )
571 // bump up the ref count so we can remove this later without
572 // having ssg try to free the memory.
573 terra_transform->ref();
574 terrain->addKid( terra_transform );
576 if ( lights_transform != 0 ) {
577 // bump up the ref count so we can remove this later without
578 // having ssg try to free the memory.
579 lights_transform->ref();
580 ground->addKid( lights_transform );
588 FGTileEntry::disconnect_ssg_nodes()
590 cout << "disconnecting ssg nodes" << endl;
592 // find the terrain branch parent
593 int pcount = terra_transform->getNumParents();
595 // find the first parent (should only be one)
596 ssgBranch *parent = terra_transform->getParent( 0 ) ;
598 // disconnect the tile (we previously ref()'d it so it
599 // won't get freed now)
600 parent->removeKid( terra_transform );
602 SG_LOG( SG_TERRAIN, SG_ALERT,
603 "parent pointer is NULL! Dying" );
607 SG_LOG( SG_TERRAIN, SG_ALERT,
608 "Parent count is zero for an ssg tile! Dying" );
612 // find the terrain lighting branch
613 if ( lights_transform ) {
614 pcount = lights_transform->getNumParents();
616 // find the first parent (should only be one)
617 ssgBranch *parent = lights_transform->getParent( 0 ) ;
619 // disconnect the light branch (we previously ref()'d
620 // it so it won't get freed now)
621 parent->removeKid( lights_transform );
623 SG_LOG( SG_TERRAIN, SG_ALERT,
624 "parent pointer is NULL! Dying" );
628 SG_LOG( SG_TERRAIN, SG_ALERT,
629 "Parent count is zero for an ssg light tile! Dying" );