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 #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>
36 #include <Aircraft/aircraft.hxx>
37 #include <Include/general.hxx>
38 #include <Main/globals.hxx>
39 #include <Scenery/scenery.hxx>
40 #include <Time/light.hxx>
41 #include <Objects/matlib.hxx>
42 #include <Objects/newmat.hxx>
43 #include <Objects/obj.hxx>
45 #include "tileentry.hxx"
46 #include "tilemgr.hxx"
50 FGTileEntry::FGTileEntry ( const SGBucket& b )
52 center( Point3D( 0.0 ) ),
54 terra_transform( new ssgTransform ),
55 terra_range( new ssgRangeSelector ),
61 // update the contents
62 // if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
63 // SG_LOG( SG_TERRAIN, SG_ALERT,
64 // "Attempting to overwrite existing or"
65 // << " not properly freed leaf data." );
72 FGTileEntry::~FGTileEntry () {
73 // cout << "nodes = " << nodes.size() << endl;;
79 // This is the current method cut and pasted from
80 // FGTileEntry::load( const SGPath& base, bool is_base )
82 FGTileEntry::WorldCoordinate( sgCoord *obj_pos, Point3D center,
83 double lat, double lon, double elev, double hdg)
86 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
87 lat * SGD_DEGREES_TO_RADIANS,
90 Point3D world_pos = sgGeodToCart( geod );
91 Point3D offset = world_pos - center;
94 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
96 sgVec3 obj_rt, obj_up;
97 sgSetVec3( obj_rt, 0.0, 1.0, 0.0); // Y axis
98 sgSetVec3( obj_up, 0.0, 0.0, 1.0); // Z axis
100 sgMat4 ROT_lon, ROT_lat, ROT_hdg;
101 sgMakeRotMat4( ROT_lon, lon, obj_up );
102 sgMakeRotMat4( ROT_lat, 90 - lat, obj_rt );
103 sgMakeRotMat4( ROT_hdg, hdg, obj_up );
106 sgCopyMat4( TUX, ROT_hdg );
107 sgPostMultMat4( TUX, ROT_lat );
108 sgPostMultMat4( TUX, ROT_lon );
109 sgPostMultMat4( TUX, POS );
111 sgSetCoord( obj_pos, TUX );
116 // Norman's 'fast hack' for above
117 static void WorldCoordinate( sgCoord *obj_pos, Point3D center, double lat,
118 double lon, double elev, double hdg )
120 double lon_rad = lon * SGD_DEGREES_TO_RADIANS;
121 double lat_rad = lat * SGD_DEGREES_TO_RADIANS;
122 double hdg_rad = hdg * SGD_DEGREES_TO_RADIANS;
125 Point3D geod( lon_rad, lat_rad, elev );
127 Point3D world_pos = sgGeodToCart( geod );
128 Point3D offset = world_pos - center;
132 SGfloat sin_lat = (SGfloat)sin( lat_rad );
133 SGfloat cos_lat = (SGfloat)cos( lat_rad );
134 SGfloat cos_lon = (SGfloat)cos( lon_rad );
135 SGfloat sin_lon = (SGfloat)sin( lon_rad );
136 SGfloat sin_hdg = (SGfloat)sin( hdg_rad ) ;
137 SGfloat cos_hdg = (SGfloat)cos( hdg_rad ) ;
139 mat[0][0] = cos_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - sin_hdg * (SGfloat)sin_lon;
140 mat[0][1] = cos_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + sin_hdg * (SGfloat)cos_lon;
141 mat[0][2] = -cos_hdg * (SGfloat)cos_lat;
144 mat[1][0] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)cos_lon - cos_hdg * (SGfloat)sin_lon;
145 mat[1][1] = -sin_hdg * (SGfloat)sin_lat * (SGfloat)sin_lon + cos_hdg * (SGfloat)cos_lon;
146 mat[1][2] = sin_hdg * (SGfloat)cos_lat;
149 mat[2][0] = (SGfloat)cos_lat * (SGfloat)cos_lon;
150 mat[2][1] = (SGfloat)cos_lat * (SGfloat)sin_lon;
151 mat[2][2] = (SGfloat)sin_lat;
154 mat[3][0] = offset.x();
155 mat[3][1] = offset.y();
156 mat[3][2] = offset.z();
159 sgSetCoord( obj_pos, mat );
163 // recurse an ssg tree and call removeKid() on every node from the
164 // bottom up. Leaves the original branch in existance, but empty so
165 // it can be removed by the calling routine.
166 static void my_remove_branch( ssgBranch * branch ) {
167 for ( ssgEntity *k = branch->getKid( 0 );
169 k = branch->getNextKid() )
171 if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
172 my_remove_branch( (ssgBranch *)k );
173 branch -> removeKid ( k );
174 } else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
175 branch -> removeKid ( k ) ;
181 #ifdef WISH_PLIB_WAS_THREADED // but it isn't
183 // Schedule tile to be freed/removed
184 void FGTileEntry::sched_removal() {
185 global_tile_mgr.ready_to_delete( this );
191 // Clean up the memory used by this tile and delete the arrays used by
192 // ssg as well as the whole ssg branch
193 void FGTileEntry::free_tile() {
195 SG_LOG( SG_TERRAIN, SG_INFO,
196 "FREEING TILE = (" << tile_bucket << ")" );
198 SG_LOG( SG_TERRAIN, SG_DEBUG,
199 " deleting " << nodes.size() << " nodes" );
202 // delete the ssg structures
203 SG_LOG( SG_TERRAIN, SG_DEBUG,
204 " deleting (leaf data) vertex, normal, and "
205 << " texture coordinate arrays" );
207 for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
208 delete [] vec3_ptrs[i];
212 for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
213 delete [] vec2_ptrs[i];
217 for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
218 delete index_ptrs[i];
222 // delete the terrain branch (this should already have been
223 // disconnected from the scene graph)
224 ssgDeRefDelete( terra_transform );
226 if ( lights_transform ) {
227 // delete the terrain lighting branch (this should already have been
228 // disconnected from the scene graph)
229 ssgDeRefDelete( lights_transform );
234 // Update the ssg transform node for this tile so it can be
235 // properly drawn relative to our (0,0,0) point
236 void FGTileEntry::prep_ssg_node( const Point3D& p, float vis) {
237 if ( !loaded ) return;
241 // #define USE_UP_AND_COMING_PLIB_FEATURE
242 #ifdef USE_UP_AND_COMING_PLIB_FEATURE
243 terra_range->setRange( 0, SG_ZERO );
244 terra_range->setRange( 1, vis + bounding_radius );
245 lights_range->setRange( 0, SG_ZERO );
246 lights_range->setRange( 1, vis * 1.5 + bounding_radius );
250 ranges[1] = vis + bounding_radius;
251 terra_range->setRanges( ranges, 2 );
252 if ( lights_range ) {
253 ranges[1] = vis * 1.5 + bounding_radius;
254 lights_range->setRanges( ranges, 2 );
258 sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
259 terra_transform->setTransform( sgTrans );
261 if ( lights_transform ) {
262 // we need to lift the lights above the terrain to avoid
263 // z-buffer fighting. We do this based on our altitude and
264 // the distance this tile is away from scenery center.
267 sgCopyVec3( up, globals->get_current_view()->get_world_up() );
270 if ( current_aircraft.fdm_state ) {
271 agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
277 // sgTrans just happens to be the
278 // vector from scenery center to the center of this tile which
279 // is what we want to calculate the distance of
281 sgCopyVec3( to, sgTrans );
282 double dist = sgLengthVec3( to );
284 if ( general.get_glDepthBits() > 16 ) {
285 sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
287 sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
289 sgAddVec3( sgTrans, up );
290 lights_transform->setTransform( sgTrans );
292 // select which set of lights based on sun angle
293 float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
294 if ( sun_angle > 95 ) {
295 lights_brightness->select(0x04);
296 } else if ( sun_angle > 92 ) {
297 lights_brightness->select(0x02);
298 } else if ( sun_angle > 89 ) {
299 lights_brightness->select(0x01);
301 lights_brightness->select(0x00);
307 ssgLeaf* FGTileEntry::gen_lights( ssgVertexArray *lights, int inc, float bright ) {
308 // generate a repeatable random seed
309 float *p1 = lights->get( 0 );
310 unsigned int *seed = (unsigned int *)p1;
313 int size = lights->getNum() / inc;
315 // Allocate ssg structure
316 ssgVertexArray *vl = new ssgVertexArray( size + 1 );
317 ssgNormalArray *nl = NULL;
318 ssgTexCoordArray *tl = NULL;
319 ssgColourArray *cl = new ssgColourArray( size + 1 );
322 for ( int i = 0; i < lights->getNum(); ++i ) {
323 // this loop is slightly less efficient than it otherwise
324 // could be, but we want a red light to always be red, and a
325 // yellow light to always be yellow, etc. so we are trying to
326 // preserve the random sequence.
327 float zombie = sg_random();
328 if ( i % inc == 0 ) {
329 vl->add( lights->get(i) );
331 // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
332 float factor = sg_random();
335 if ( zombie > 0.5 ) {
336 // 50% chance of yellowish
337 sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
338 } else if ( zombie > 0.15 ) {
339 // 35% chance of whitish
340 sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
341 } else if ( zombie > 0.05 ) {
342 // 10% chance of orangish
343 sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
345 // 5% chance of redish
346 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
354 new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
357 FGNewMat *newmat = material_lib.find( "LIGHTS" );
358 leaf->setState( newmat->get_state() );
365 FGTileEntry::obj_load( const std::string& path,
366 ssgVertexArray* lights, bool is_base )
368 ssgBranch* result = 0;
370 // try loading binary format
371 result = fgBinObjLoad( path, this, lights, is_base );
372 if ( result == NULL ) {
373 // next try the older ascii format
374 result = fgAsciiObjLoad( path, this, lights, is_base );
375 if ( result == NULL ) {
376 // default to an ocean tile
377 result = fgGenTile( path, this );
386 FGTileEntry::load( const SGPath& base, bool is_base )
388 string index_str = tile_bucket.gen_index_str();
390 SGPath tile_path = base;
391 // Generate name of file to load.
392 tile_path.append( tile_bucket.gen_base_path() );
393 SGPath basename = tile_path;
394 basename.append( index_str );
395 string path = basename.str();
397 SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
399 // fgObjLoad will generate ground lighting for us ...
400 ssgVertexArray *light_pts = new ssgVertexArray( 100 );
402 ssgBranch* new_tile = obj_load( path, light_pts, is_base );
403 if ( new_tile != NULL ) {
404 terra_range->addKid( new_tile );
407 // load custom objects
408 SG_LOG( SG_TERRAIN, SG_DEBUG, "Checking for custom objects ..." );
410 SGPath index_path = tile_path;
411 index_path.append( index_str );
412 index_path.concat( ".ind" );
414 SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
416 sg_gzifstream in( index_path.str() );
418 if ( in.is_open() ) {
421 while ( ! in.eof() ) {
424 if ( token == "OBJECT" ) {
425 in >> name >> ::skipws;
426 SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
427 << " name = " << name );
429 SGPath custom_path = tile_path;
430 custom_path.append( name );
431 ssgBranch *custom_obj
432 = obj_load( custom_path.str(), NULL, false );
433 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
434 new_tile -> addKid( custom_obj );
436 } else if ( token == "OBJECT_STATIC" ) {
438 double lon, lat, elev, hdg;
439 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
440 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
441 << " name = " << name
442 << " pos = " << lon << ", " << lat
443 << " elevation = " << elev
444 << " heading = " << hdg );
446 // object loading is deferred to main render thread,
447 // but lets figure out the paths right now.
448 SGPath custom_path = tile_path;
449 custom_path.append( name );
452 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
454 ssgTransform *obj_trans = new ssgTransform;
455 obj_trans->setTransform( &obj_pos );
457 // wire as much of the scene graph together as we can
458 new_tile->addKid( obj_trans );
460 // bump up the pending models count
463 // push an entry onto the model load queue
465 = new FGDeferredModel( custom_path.str(), tile_path.str(),
467 FGTileMgr::model_ready( dm );
468 } else if ( token == "OBJECT_TAXI_SIGN" ) {
470 double lon, lat, elev, hdg;
471 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
472 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
473 << " name = " << name
474 << " pos = " << lon << ", " << lat
475 << " elevation = " << elev
476 << " heading = " << hdg );
478 // load the object itself
479 SGPath custom_path = tile_path;
480 custom_path.append( name );
483 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
485 ssgTransform *obj_trans = new ssgTransform;
486 obj_trans->setTransform( &obj_pos );
488 ssgBranch *custom_obj
489 = gen_taxi_sign( custom_path.str(), name );
491 // wire the pieces together
492 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
493 obj_trans -> addKid( custom_obj );
495 new_tile->addKid( obj_trans );
496 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
498 double lon, lat, elev, hdg;
499 in >> name >> lon >> lat >> elev >> hdg >> ::skipws;
500 SG_LOG( SG_TERRAIN, SG_INFO, "token = " << token
501 << " name = " << name
502 << " pos = " << lon << ", " << lat
503 << " elevation = " << elev
504 << " heading = " << hdg );
506 // load the object itself
507 SGPath custom_path = tile_path;
508 custom_path.append( name );
511 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
513 ssgTransform *obj_trans = new ssgTransform;
514 obj_trans->setTransform( &obj_pos );
516 ssgBranch *custom_obj
517 = gen_runway_sign( custom_path.str(), name );
519 // wire the pieces together
520 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
521 obj_trans -> addKid( custom_obj );
523 new_tile->addKid( obj_trans );
525 SG_LOG( SG_TERRAIN, SG_ALERT,
526 "Unknown token " << token << " in "
527 << index_path.str() );
533 terra_transform->addKid( terra_range );
535 // calculate initial tile offset
536 SetOffset( scenery.center );
538 sgSetCoord( &sgcoord,
539 offset.x(), offset.y(), offset.z(),
541 terra_transform->setTransform( &sgcoord );
542 // terrain->addKid( terra_transform );
544 lights_transform = NULL;
546 /* uncomment this section for testing ground lights */
547 if ( light_pts->getNum() ) {
548 SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
549 lights_transform = new ssgTransform;
550 lights_range = new ssgRangeSelector;
551 lights_brightness = new ssgSelector;
554 lights = gen_lights( light_pts, 4, 0.7 );
555 lights_brightness->addKid( lights );
557 lights = gen_lights( light_pts, 2, 0.85 );
558 lights_brightness->addKid( lights );
560 lights = gen_lights( light_pts, 1, 1.0 );
561 lights_brightness->addKid( lights );
563 lights_range->addKid( lights_brightness );
564 lights_transform->addKid( lights_range );
565 lights_transform->setTransform( &sgcoord );
566 // ground->addKid( lights_transform );
568 /* end of ground light section */
573 FGTileEntry::add_ssg_nodes( ssgBranch* terrain, ssgBranch* ground )
575 // bump up the ref count so we can remove this later without
576 // having ssg try to free the memory.
577 terra_transform->ref();
578 terrain->addKid( terra_transform );
580 SG_LOG( SG_TERRAIN, SG_DEBUG,
581 "connected a tile into scene graph. terra_transform = "
582 << terra_transform );
583 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
584 << terra_transform->getNumParents() );
586 if ( lights_transform != 0 ) {
587 // bump up the ref count so we can remove this later without
588 // having ssg try to free the memory.
589 lights_transform->ref();
590 ground->addKid( lights_transform );
598 FGTileEntry::disconnect_ssg_nodes()
600 SG_LOG( SG_TERRAIN, SG_INFO, "disconnecting ssg nodes" );
603 SG_LOG( SG_TERRAIN, SG_INFO, "removing a not-fully loaded tile!" );
605 SG_LOG( SG_TERRAIN, SG_INFO, "removing a fully loaded tile! terra_transform = " << terra_transform );
608 // find the terrain branch parent
609 int pcount = terra_transform->getNumParents();
611 // find the first parent (should only be one)
612 ssgBranch *parent = terra_transform->getParent( 0 ) ;
614 // disconnect the tile (we previously ref()'d it so it
615 // won't get freed now)
616 parent->removeKid( terra_transform );
618 SG_LOG( SG_TERRAIN, SG_ALERT,
619 "parent pointer is NULL! Dying" );
623 SG_LOG( SG_TERRAIN, SG_ALERT,
624 "Parent count is zero for an ssg tile! Dying" );
628 // find the terrain lighting branch
629 if ( lights_transform ) {
630 pcount = lights_transform->getNumParents();
632 // find the first parent (should only be one)
633 ssgBranch *parent = lights_transform->getParent( 0 ) ;
635 // disconnect the light branch (we previously ref()'d
636 // it so it won't get freed now)
637 parent->removeKid( lights_transform );
639 SG_LOG( SG_TERRAIN, SG_ALERT,
640 "parent pointer is NULL! Dying" );
644 SG_LOG( SG_TERRAIN, SG_ALERT,
645 "Parent count is zero for an ssg light tile! Dying" );