]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tileentry.cxx
5ba0900d892383b1cc03fabe8b68434b6b0992e0
[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 #ifdef SG_MATH_EXCEPTION_CLASH
31 #  include <math.h>
32 #endif
33
34 #include STL_FUNCTIONAL
35 #include STL_ALGORITHM
36 #include STL_STRING
37
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>
43
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>
52
53 #include "tileentry.hxx"
54 #include "tilemgr.hxx"
55
56 SG_USING_STD(for_each);
57 SG_USING_STD(mem_fun_ref);
58 SG_USING_STD(string);
59
60
61 // Constructor
62 FGTileEntry::FGTileEntry ( const SGBucket& b )
63     : ncount( 0 ),
64       center( Point3D( 0.0 ) ),
65       tile_bucket( b ),
66       terra_transform( new ssgTransform ),
67       terra_range( new ssgRangeSelector ),
68       loaded(false),
69       pending_models(0)
70 {
71     nodes.clear();
72
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." );
78     //     exit(-1);
79     // }
80 }
81
82
83 // Destructor
84 FGTileEntry::~FGTileEntry () {
85     // cout << "nodes = " << nodes.size() << endl;;
86     // delete[] nodes;
87 }
88
89
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 );
95           k != NULL; 
96           k = branch->getNextKid() )
97     {
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 ) ;
103         }
104     }
105 }
106
107
108 #ifdef WISH_PLIB_WAS_THREADED // but it isn't
109
110 // Schedule tile to be freed/removed
111 void FGTileEntry::sched_removal() {
112     global_tile_mgr.ready_to_delete( this );
113 }
114
115 #endif
116
117
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() {
121     int i;
122     SG_LOG( SG_TERRAIN, SG_INFO,
123             "FREEING TILE = (" << tile_bucket << ")" );
124
125     SG_LOG( SG_TERRAIN, SG_DEBUG,
126             "  deleting " << nodes.size() << " nodes" );
127     nodes.clear();
128
129     // delete the ssg structures
130     SG_LOG( SG_TERRAIN, SG_DEBUG,
131             "  deleting (leaf data) vertex, normal, and "
132             << " texture coordinate arrays" );
133
134     for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
135         delete [] vec3_ptrs[i];
136     }
137     vec3_ptrs.clear();
138
139     for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
140         delete [] vec2_ptrs[i];
141     }
142     vec2_ptrs.clear();
143
144     for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
145         delete index_ptrs[i];
146     }
147     index_ptrs.clear();
148
149     // delete the terrain branch (this should already have been
150     // disconnected from the scene graph)
151     ssgDeRefDelete( terra_transform );
152
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 );
157     }
158 }
159
160
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;
165
166     SetOffset( p );
167
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 );
174 #else
175     float ranges[2];
176     ranges[0] = SG_ZERO;
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 );
182     }
183 #endif
184     sgVec3 sgTrans;
185     sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
186     terra_transform->setTransform( sgTrans );
187
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.
192
193         sgVec3 up;
194         sgCopyVec3( up, globals->get_current_view()->get_world_up() );
195
196         double agl;
197         if ( current_aircraft.fdm_state ) {
198             agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
199                 - scenery.cur_elev;
200         } else {
201             agl = 0.0;
202         }
203
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
207         sgVec3 to;
208         sgCopyVec3( to, sgTrans );
209         double dist = sgLengthVec3( to );
210
211         if ( general.get_glDepthBits() > 16 ) {
212             sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
213         } else {
214             sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
215         }
216         sgAddVec3( sgTrans, up );
217         lights_transform->setTransform( sgTrans );
218
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);
227         } else {
228             lights_brightness->select(0x00);
229         }
230     }
231 }
232
233
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;
238     sg_srandom( *seed );
239
240     int size = lights->getNum() / inc;
241
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 );
247
248     sgVec4 color;
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) );
257
258             // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
259             float factor = sg_random();
260             factor *= factor;
261
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 );
271             } else {
272                 // 5% chance of redish
273                 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
274             }
275             cl->add( color );
276         }
277     }
278
279     // create ssg leaf
280     ssgLeaf *leaf = 
281         new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
282
283     // assign state
284     FGNewMat *newmat = material_lib.find( "LIGHTS" );
285     leaf->setState( newmat->get_state() );
286
287     return leaf;
288 }
289
290
291 ssgBranch*
292 FGTileEntry::obj_load( const std::string& path,
293                        ssgVertexArray* lights, bool is_base )
294 {
295     ssgBranch* result = 0;
296
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 );
305         }
306     }
307
308     return result;
309 }
310
311
312 void
313 FGTileEntry::load( const SGPath& base, bool is_base )
314 {
315     string index_str = tile_bucket.gen_index_str();
316
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();
323
324     SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
325
326     // fgObjLoad will generate ground lighting for us ...
327     ssgVertexArray *light_pts = new ssgVertexArray( 100 );
328
329     ssgBranch* new_tile = obj_load( path, light_pts, is_base );
330     if ( new_tile != NULL ) {
331         terra_range->addKid( new_tile );
332     }
333
334     // load custom objects
335     SG_LOG( SG_TERRAIN, SG_DEBUG, "Checking for custom objects ..." );
336
337     SGPath index_path = tile_path;
338     index_path.append( index_str );
339     index_path.concat( ".ind" );
340
341     SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
342
343     sg_gzifstream in( index_path.str() );
344
345     if ( in.is_open() ) {
346         string token, name;
347
348         while ( ! in.eof() ) {
349             in >> token;
350
351             if ( token == "OBJECT" ) {
352                 in >> name >> ::skipws;
353                 SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
354                         << " name = " << name );
355
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 );
362                 }
363             } else if ( token == "OBJECT_STATIC" ) {
364                 // load object info
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 );
372
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 );
377
378                 // setup transforms
379                 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
380                               lat * SGD_DEGREES_TO_RADIANS,
381                               elev );
382                 Point3D world_pos = sgGeodToCart( geod );
383                 Point3D offset = world_pos - center;
384                 sgMat4 POS;
385                 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
386
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
390
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 );
395         
396                 sgMat4 TUX;
397                 sgCopyMat4( TUX, ROT_hdg );
398                 sgPostMultMat4( TUX, ROT_lat );
399                 sgPostMultMat4( TUX, ROT_lon );
400                 sgPostMultMat4( TUX, POS );
401
402                 sgCoord obj_pos;
403                 sgSetCoord( &obj_pos, TUX );
404                 ssgTransform *obj_trans = new ssgTransform;
405                 obj_trans->setTransform( &obj_pos );
406
407                 // wire as much of the scene graph together as we can
408                 new_tile->addKid( obj_trans );
409
410                 // bump up the pending models count
411                 pending_models++;
412
413                 // push an entry onto the model load queue
414                 FGDeferredModel *dm
415                     = new FGDeferredModel( custom_path.str(), tile_path.str(),
416                                            this, obj_trans );
417                 FGTileMgr::model_ready( dm );
418             } else if ( token == "OBJECT_TAXI_SIGN" ) {
419                 // load object info
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 );
427
428                 // load the object itself
429                 SGPath custom_path = tile_path;
430                 custom_path.append( name );
431
432                 // setup transforms
433                 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
434                               lat * SGD_DEGREES_TO_RADIANS,
435                               elev );
436                 Point3D world_pos = sgGeodToCart( geod );
437                 Point3D offset = world_pos - center;
438                 sgMat4 POS;
439                 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
440
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
444
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 );
449         
450                 sgMat4 TUX;
451                 sgCopyMat4( TUX, ROT_hdg );
452                 sgPostMultMat4( TUX, ROT_lat );
453                 sgPostMultMat4( TUX, ROT_lon );
454                 sgPostMultMat4( TUX, POS );
455
456                 sgCoord obj_pos;
457                 sgSetCoord( &obj_pos, TUX );
458                 ssgTransform *obj_trans = new ssgTransform;
459                 obj_trans->setTransform( &obj_pos );
460
461                 ssgBranch *custom_obj
462                     = gen_taxi_sign( custom_path.str(), name );
463
464                 // wire the pieces together
465                 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
466                     obj_trans -> addKid( custom_obj );
467                 }
468                 new_tile->addKid( obj_trans );
469             } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
470                 // load object info
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 );
478
479                 // load the object itself
480                 SGPath custom_path = tile_path;
481                 custom_path.append( name );
482
483                 // setup transforms
484                 Point3D geod( lon * SGD_DEGREES_TO_RADIANS,
485                               lat * SGD_DEGREES_TO_RADIANS,
486                               elev );
487                 Point3D world_pos = sgGeodToCart( geod );
488                 Point3D offset = world_pos - center;
489                 sgMat4 POS;
490                 sgMakeTransMat4( POS, offset.x(), offset.y(), offset.z() );
491
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
495
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 );
500         
501                 sgMat4 TUX;
502                 sgCopyMat4( TUX, ROT_hdg );
503                 sgPostMultMat4( TUX, ROT_lat );
504                 sgPostMultMat4( TUX, ROT_lon );
505                 sgPostMultMat4( TUX, POS );
506
507                 sgCoord obj_pos;
508                 sgSetCoord( &obj_pos, TUX );
509                 ssgTransform *obj_trans = new ssgTransform;
510                 obj_trans->setTransform( &obj_pos );
511
512                 ssgBranch *custom_obj
513                     = gen_runway_sign( custom_path.str(), name );
514
515                 // wire the pieces together
516                 if ( (new_tile != NULL) && (custom_obj != NULL) ) {
517                     obj_trans -> addKid( custom_obj );
518                 }
519                 new_tile->addKid( obj_trans );
520             } else {
521                 SG_LOG( SG_TERRAIN, SG_ALERT,
522                         "Unknown token " << token << " in "
523                         << index_path.str() );
524                 in >> ::skipws;
525             }
526         }
527     }
528
529     terra_transform->addKid( terra_range );
530
531     // calculate initial tile offset
532     SetOffset( scenery.center );
533     sgCoord sgcoord;
534     sgSetCoord( &sgcoord,
535                 offset.x(), offset.y(), offset.z(),
536                 0.0, 0.0, 0.0 );
537     terra_transform->setTransform( &sgcoord );
538     // terrain->addKid( terra_transform );
539
540     lights_transform = NULL;
541     lights_range = 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;
548         ssgLeaf *lights;
549
550         lights = gen_lights( light_pts, 4, 0.7 );
551         lights_brightness->addKid( lights );
552
553         lights = gen_lights( light_pts, 2, 0.85 );
554         lights_brightness->addKid( lights );
555
556         lights = gen_lights( light_pts, 1, 1.0 );
557         lights_brightness->addKid( lights );
558
559         lights_range->addKid( lights_brightness );
560         lights_transform->addKid( lights_range );
561         lights_transform->setTransform( &sgcoord );
562         // ground->addKid( lights_transform );
563     }
564     /* end of ground light section */
565 }
566
567
568 void
569 FGTileEntry::add_ssg_nodes( ssgBranch* terrain, ssgBranch* ground )
570 {
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 );
575
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 );
581     }
582
583     loaded = true;
584 }
585
586
587 void
588 FGTileEntry::disconnect_ssg_nodes()
589 {
590     cout << "disconnecting ssg nodes" << endl;
591
592     // find the terrain branch parent
593     int pcount = terra_transform->getNumParents();
594     if ( pcount > 0 ) {
595         // find the first parent (should only be one)
596         ssgBranch *parent = terra_transform->getParent( 0 ) ;
597         if( parent ) {
598             // disconnect the tile (we previously ref()'d it so it
599             // won't get freed now)
600             parent->removeKid( terra_transform );
601         } else {
602             SG_LOG( SG_TERRAIN, SG_ALERT,
603                     "parent pointer is NULL!  Dying" );
604             exit(-1);
605         }
606     } else {
607         SG_LOG( SG_TERRAIN, SG_ALERT,
608                 "Parent count is zero for an ssg tile!  Dying" );
609         exit(-1);
610     }
611
612     // find the terrain lighting branch
613     if ( lights_transform ) {
614         pcount = lights_transform->getNumParents();
615         if ( pcount > 0 ) {
616             // find the first parent (should only be one)
617             ssgBranch *parent = lights_transform->getParent( 0 ) ;
618             if( parent ) {
619                 // disconnect the light branch (we previously ref()'d
620                 // it so it won't get freed now)
621                 parent->removeKid( lights_transform );
622             } else {
623                 SG_LOG( SG_TERRAIN, SG_ALERT,
624                         "parent pointer is NULL!  Dying" );
625                 exit(-1);
626             }
627         } else {
628             SG_LOG( SG_TERRAIN, SG_ALERT,
629                     "Parent count is zero for an ssg light tile!  Dying" );
630             exit(-1);
631         }
632     }
633 }