]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tileentry.cxx
- adjusted for no-value constructor for FGPanel
[flightgear.git] / src / Scenery / tileentry.cxx
1 // tileentry.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2001  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #include <simgear/bucket/newbucket.hxx>
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/math/sg_geodesy.hxx>
33 #include <simgear/math/sg_random.h>
34 #include <simgear/misc/sgstream.hxx>
35
36 #include <Aircraft/aircraft.hxx>
37 #include <Include/general.hxx>
38 #include <Main/globals.hxx>
39 #include <Scenery/scenery.hxx>
40 #include <Time/light.hxx>
41 #include <Objects/matlib.hxx>
42 #include <Objects/newmat.hxx>
43 #include <Objects/obj.hxx>
44
45 #include "tileentry.hxx"
46 #include "tilemgr.hxx"
47
48
49 // Constructor
50 FGTileEntry::FGTileEntry ( const SGBucket& b )
51     : ncount( 0 ),
52       center( Point3D( 0.0 ) ),
53       tile_bucket( b ),
54       terra_transform( new ssgTransform ),
55       terra_range( new ssgRangeSelector ),
56       loaded(false),
57       pending_models(0)
58 {
59     nodes.clear();
60
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." );
66     //     exit(-1);
67     // }
68 }
69
70
71 // Destructor
72 FGTileEntry::~FGTileEntry () {
73     // cout << "nodes = " << nodes.size() << endl;;
74     // delete[] nodes;
75 }
76
77
78 #if 0
79 // This is the current method cut and pasted from 
80 //  FGTileEntry::load( const SGPath& base, bool is_base )
81 void
82 FGTileEntry::WorldCoordinate( sgCoord *obj_pos, Point3D center,
83                               double lat, double lon, double elev, double hdg)
84 {
85     // setup transforms
86     Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
87                   lat * SGD_DEGREES_TO_RADIANS,
88                   elev );
89         
90     Point3D world_pos = sgGeodToCart( geod );
91     Point3D offset = world_pos - center;
92         
93     sgMat4 POS;
94     sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
95
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
99
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 );
104
105     sgMat4 TUX;
106     sgCopyMat4( TUX, ROT_hdg );
107     sgPostMultMat4( TUX, ROT_lat );
108     sgPostMultMat4( TUX, ROT_lon );
109     sgPostMultMat4( TUX, POS );
110
111     sgSetCoord( obj_pos, TUX );
112 }
113 #endif
114
115
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 )
119 {
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;
123
124     // setup transforms
125     Point3D geod( lon_rad, lat_rad, elev );
126         
127     Point3D world_pos = sgGeodToCart( geod );
128     Point3D offset = world_pos - center;
129
130     sgMat4 mat;
131
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 ) ;
138
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;
142     mat[0][3] =  SG_ZERO;
143
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;
147     mat[1][3] =  SG_ZERO;
148
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;
152     mat[2][3] =  SG_ZERO;
153
154     mat[3][0] = offset.x();
155     mat[3][1] = offset.y();
156     mat[3][2] = offset.z();
157     mat[3][3] = SG_ONE ;
158
159     sgSetCoord( obj_pos, mat );
160 }
161
162
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 );
168           k != NULL; 
169           k = branch->getNextKid() )
170     {
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 ) ;
176         }
177     }
178 }
179
180
181 #ifdef WISH_PLIB_WAS_THREADED // but it isn't
182
183 // Schedule tile to be freed/removed
184 void FGTileEntry::sched_removal() {
185     global_tile_mgr.ready_to_delete( this );
186 }
187
188 #endif
189
190
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() {
194     int i;
195     SG_LOG( SG_TERRAIN, SG_INFO,
196             "FREEING TILE = (" << tile_bucket << ")" );
197
198     SG_LOG( SG_TERRAIN, SG_DEBUG,
199             "  deleting " << nodes.size() << " nodes" );
200     nodes.clear();
201
202     // delete the ssg structures
203     SG_LOG( SG_TERRAIN, SG_DEBUG,
204             "  deleting (leaf data) vertex, normal, and "
205             << " texture coordinate arrays" );
206
207     for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
208         delete [] vec3_ptrs[i];
209     }
210     vec3_ptrs.clear();
211
212     for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
213         delete [] vec2_ptrs[i];
214     }
215     vec2_ptrs.clear();
216
217     for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
218         delete index_ptrs[i];
219     }
220     index_ptrs.clear();
221
222     // delete the terrain branch (this should already have been
223     // disconnected from the scene graph)
224     ssgDeRefDelete( terra_transform );
225
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 );
230     }
231 }
232
233
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;
238
239     SetOffset( p );
240
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 );
247 #else
248     float ranges[2];
249     ranges[0] = SG_ZERO;
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 );
255     }
256 #endif
257     sgVec3 sgTrans;
258     sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
259     terra_transform->setTransform( sgTrans );
260
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.
265
266         sgVec3 up;
267         sgCopyVec3( up, globals->get_current_view()->get_world_up() );
268
269         double agl;
270         if ( current_aircraft.fdm_state ) {
271             agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
272                 - scenery.cur_elev;
273         } else {
274             agl = 0.0;
275         }
276
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
280         sgVec3 to;
281         sgCopyVec3( to, sgTrans );
282         double dist = sgLengthVec3( to );
283
284         if ( general.get_glDepthBits() > 16 ) {
285             sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
286         } else {
287             sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
288         }
289         sgAddVec3( sgTrans, up );
290         lights_transform->setTransform( sgTrans );
291
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);
300         } else {
301             lights_brightness->select(0x00);
302         }
303     }
304 }
305
306
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;
311     sg_srandom( *seed );
312
313     int size = lights->getNum() / inc;
314
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 );
320
321     sgVec4 color;
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) );
330
331             // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
332             float factor = sg_random();
333             factor *= factor;
334
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 );
344             } else {
345                 // 5% chance of redish
346                 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
347             }
348             cl->add( color );
349         }
350     }
351
352     // create ssg leaf
353     ssgLeaf *leaf = 
354         new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
355
356     // assign state
357     FGNewMat *newmat = material_lib.find( "LIGHTS" );
358     leaf->setState( newmat->get_state() );
359
360     return leaf;
361 }
362
363
364 ssgBranch*
365 FGTileEntry::obj_load( const std::string& path,
366                        ssgVertexArray* lights, bool is_base )
367 {
368     ssgBranch* result = 0;
369
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 );
378         }
379     }
380
381     return result;
382 }
383
384
385 void
386 FGTileEntry::load( const SGPath& base, bool is_base )
387 {
388     string index_str = tile_bucket.gen_index_str();
389
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();
396
397     SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
398
399     // fgObjLoad will generate ground lighting for us ...
400     ssgVertexArray *light_pts = new ssgVertexArray( 100 );
401
402     ssgBranch* new_tile = obj_load( path, light_pts, is_base );
403     if ( new_tile != NULL ) {
404         terra_range->addKid( new_tile );
405     }
406
407     // load custom objects
408     SG_LOG( SG_TERRAIN, SG_DEBUG, "Checking for custom objects ..." );
409
410     SGPath index_path = tile_path;
411     index_path.append( index_str );
412     index_path.concat( ".ind" );
413
414     SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
415
416     sg_gzifstream in( index_path.str() );
417
418     if ( in.is_open() ) {
419         string token, name;
420
421         while ( ! in.eof() ) {
422             in >> token;
423
424             if ( token == "OBJECT" ) {
425                 in >> name >> ::skipws;
426                 SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
427                         << " name = " << name );
428
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 );
435                 }
436             } else if ( token == "OBJECT_STATIC" ) {
437                 // load object info
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 );
445
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 );
450
451                 sgCoord obj_pos;
452                 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
453                 
454                 ssgTransform *obj_trans = new ssgTransform;
455                 obj_trans->setTransform( &obj_pos );
456
457                 // wire as much of the scene graph together as we can
458                 new_tile->addKid( obj_trans );
459
460                 // bump up the pending models count
461                 pending_models++;
462
463                 // push an entry onto the model load queue
464                 FGDeferredModel *dm
465                     = new FGDeferredModel( custom_path.str(), tile_path.str(),
466                                            this, obj_trans );
467                 FGTileMgr::model_ready( dm );
468             } else if ( token == "OBJECT_TAXI_SIGN" ) {
469                 // load object info
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 );
477
478                 // load the object itself
479                 SGPath custom_path = tile_path;
480                 custom_path.append( name );
481
482                 sgCoord obj_pos;
483                 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
484
485                 ssgTransform *obj_trans = new ssgTransform;
486                 obj_trans->setTransform( &obj_pos );
487
488                 ssgBranch *custom_obj
489                     = gen_taxi_sign( custom_path.str(), name );
490
491                 // wire the pieces together
492                 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
493                     obj_trans -> addKid( custom_obj );
494                 }
495                 new_tile->addKid( obj_trans );
496             } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
497                 // load object info
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 );
505
506                 // load the object itself
507                 SGPath custom_path = tile_path;
508                 custom_path.append( name );
509
510                 sgCoord obj_pos;
511                 WorldCoordinate( &obj_pos, center, lat, lon, elev, hdg );
512
513                 ssgTransform *obj_trans = new ssgTransform;
514                 obj_trans->setTransform( &obj_pos );
515
516                 ssgBranch *custom_obj
517                     = gen_runway_sign( custom_path.str(), name );
518
519                 // wire the pieces together
520                 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
521                     obj_trans -> addKid( custom_obj );
522                 }
523                 new_tile->addKid( obj_trans );
524             } else {
525                 SG_LOG( SG_TERRAIN, SG_ALERT,
526                         "Unknown token " << token << " in "
527                         << index_path.str() );
528                 in >> ::skipws;
529             }
530         }
531     }
532
533     terra_transform->addKid( terra_range );
534
535     // calculate initial tile offset
536     SetOffset( scenery.center );
537     sgCoord sgcoord;
538     sgSetCoord( &sgcoord,
539                 offset.x(), offset.y(), offset.z(),
540                 0.0, 0.0, 0.0 );
541     terra_transform->setTransform( &sgcoord );
542     // terrain->addKid( terra_transform );
543
544     lights_transform = NULL;
545     lights_range = 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;
552         ssgLeaf *lights;
553
554         lights = gen_lights( light_pts, 4, 0.7 );
555         lights_brightness->addKid( lights );
556
557         lights = gen_lights( light_pts, 2, 0.85 );
558         lights_brightness->addKid( lights );
559
560         lights = gen_lights( light_pts, 1, 1.0 );
561         lights_brightness->addKid( lights );
562
563         lights_range->addKid( lights_brightness );
564         lights_transform->addKid( lights_range );
565         lights_transform->setTransform( &sgcoord );
566         // ground->addKid( lights_transform );
567     }
568     /* end of ground light section */
569 }
570
571
572 void
573 FGTileEntry::add_ssg_nodes( ssgBranch* terrain, ssgBranch* ground )
574 {
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 );
579
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() );
585
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 );
591     }
592
593     loaded = true;
594 }
595
596
597 void
598 FGTileEntry::disconnect_ssg_nodes()
599 {
600     SG_LOG( SG_TERRAIN, SG_INFO, "disconnecting ssg nodes" );
601
602     if ( ! loaded ) {
603         SG_LOG( SG_TERRAIN, SG_INFO, "removing a not-fully loaded tile!" );
604     } else {
605         SG_LOG( SG_TERRAIN, SG_INFO, "removing a fully loaded tile!  terra_transform = " << terra_transform );
606     }
607         
608     // find the terrain branch parent
609     int pcount = terra_transform->getNumParents();
610     if ( pcount > 0 ) {
611         // find the first parent (should only be one)
612         ssgBranch *parent = terra_transform->getParent( 0 ) ;
613         if( parent ) {
614             // disconnect the tile (we previously ref()'d it so it
615             // won't get freed now)
616             parent->removeKid( terra_transform );
617         } else {
618             SG_LOG( SG_TERRAIN, SG_ALERT,
619                     "parent pointer is NULL!  Dying" );
620             exit(-1);
621         }
622     } else {
623         SG_LOG( SG_TERRAIN, SG_ALERT,
624                 "Parent count is zero for an ssg tile!  Dying" );
625         exit(-1);
626     }
627
628     // find the terrain lighting branch
629     if ( lights_transform ) {
630         pcount = lights_transform->getNumParents();
631         if ( pcount > 0 ) {
632             // find the first parent (should only be one)
633             ssgBranch *parent = lights_transform->getParent( 0 ) ;
634             if( parent ) {
635                 // disconnect the light branch (we previously ref()'d
636                 // it so it won't get freed now)
637                 parent->removeKid( lights_transform );
638             } else {
639                 SG_LOG( SG_TERRAIN, SG_ALERT,
640                         "parent pointer is NULL!  Dying" );
641                 exit(-1);
642             }
643         } else {
644             SG_LOG( SG_TERRAIN, SG_ALERT,
645                     "Parent count is zero for an ssg light tile!  Dying" );
646             exit(-1);
647         }
648     }
649 }