]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tilemgr.cxx
88bf6eb83dc10fe0489360b1d61441b0ab9e9fd9
[flightgear.git] / src / Scenery / tilemgr.cxx
1 // tilemgr.cxx -- routines to handle dynamic management of scenery tiles
2 //
3 // Written by Curtis Olson, started January 1998.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
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 #ifdef HAVE_WINDOWS_H
29 #  include <windows.h>
30 #endif
31
32 #include <GL/glut.h>
33 #include <XGL/xgl.h>
34
35 #include <Aircraft/aircraft.hxx>
36
37 #include <Debug/logstream.hxx>
38 // #include <Bucket/bucketutils.hxx>
39 #include <Include/fg_constants.h>
40 #include <Main/options.hxx>
41 #include <Main/views.hxx>
42 #include <Math/fg_geodesy.hxx>
43 #include <Math/mat3.h>
44 #include <Math/point3d.hxx>
45 #include <Math/polar3d.hxx>
46 #include <Math/vector.hxx>
47 #include <Objects/materialmgr.hxx>
48 #include <Objects/obj.hxx>
49 // #include <Weather/weather.hxx>
50 #include <WeatherCM/FGLocalWeatherDatabase.h>
51
52 #include "scenery.hxx"
53 #include "tilecache.hxx"
54 #include "tileentry.hxx"
55 #include "tilemgr.hxx"
56
57
58 // to test clipping speedup in fgTileMgrRender()
59 #if defined ( USE_FAST_FOV_CLIP )
60 // #define TEST_FOV_CLIP
61 // #define TEST_ELEV
62 #endif
63
64
65 extern ssgRoot *scene;
66
67
68 // the tile manager
69 FGTileMgr global_tile_mgr;
70
71
72 // Constructor
73 FGTileMgr::FGTileMgr ( void ):
74     state( Start )
75 {
76 }
77
78
79 // Destructor
80 FGTileMgr::~FGTileMgr ( void ) {
81 }
82
83
84 // Initialize the Tile Manager subsystem
85 int FGTileMgr::init( void ) {
86     FG_LOG( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem." );
87
88     // load default material library
89     if ( ! material_mgr.loaded() ) {
90         material_mgr.load_lib();
91     }
92
93     state = Inited;
94
95     return 1;
96 }
97
98
99 // schedule a tile for loading
100 static void disable_tile( int cache_index ) {
101     // see if tile already exists in the cache
102     cout << "DISABLING CACHE ENTRY = " << cache_index << endl;
103     FGTileEntry *t = global_tile_cache.get_tile( cache_index );
104     t->ssg_disable();
105 }
106
107
108 // schedule a tile for loading
109 int FGTileMgr::sched_tile( const FGBucket& b ) {
110     // see if tile already exists in the cache
111     int cache_index = global_tile_cache.exists( b );
112
113     if ( cache_index >= 0 ) {
114         // tile exists in cache, reenable it.
115         cout << "REENABLING DISABLED TILE" << endl;
116         FGTileEntry *t = global_tile_cache.get_tile( cache_index );
117         t->select_ptr->select( 1 );
118         t->mark_loaded();
119     } else {
120         // find the next available cache entry and mark it as
121         // scheduled
122         cache_index = global_tile_cache.next_avail();
123         FGTileEntry *t = global_tile_cache.get_tile( cache_index );
124         t->mark_scheduled_for_use();
125
126         // register a load request
127         FGLoadRec request;
128         request.b = b;
129         request.cache_index = cache_index;
130         load_queue.push_back( request );
131     }
132
133     return cache_index;
134 }
135
136
137 // load a tile
138 void FGTileMgr::load_tile( const FGBucket& b, int cache_index) {
139
140     FG_LOG( FG_TERRAIN, FG_DEBUG, "Loading tile " << b );
141     
142     global_tile_cache.fill_in(cache_index, b);
143
144     FG_LOG( FG_TERRAIN, FG_DEBUG, "Loaded for cache index: " << cache_index );
145 }
146
147
148 // Calculate shortest distance from point to line
149 static double point_line_dist_squared( const Point3D& tc, const Point3D& vp, 
150                                        MAT3vec d )
151 {
152     MAT3vec p, p0;
153
154     p[0] = tc.x(); p[1] = tc.y(); p[2] = tc.z();
155     p0[0] = vp.x(); p0[1] = vp.y(); p0[2] = vp.z();
156
157     return fgPointLineSquared(p, p0, d);
158 }
159
160
161 // Determine scenery altitude.  Normally this just happens when we
162 // render the scene, but we'd also like to be able to do this
163 // explicitely.  lat & lon are in radians.  abs_view_pos in meters.
164 // Returns result in meters.
165 double
166 FGTileMgr::current_elev_new( const FGBucket& p ) {
167     FGTileEntry *t;
168     fgFRAGMENT *frag_ptr;
169     Point3D abs_view_pos = current_view.get_abs_view_pos();
170     Point3D earth_center(0.0);
171     Point3D result;
172     MAT3vec local_up;
173     double dist, lat_geod, alt, sea_level_r;
174     int index;
175
176     local_up[0] = abs_view_pos.x();
177     local_up[1] = abs_view_pos.y();
178     local_up[2] = abs_view_pos.z();
179
180     // Find current translation offset
181     // fgBucketFind(lon * RAD_TO_DEG, lat * RAD_TO_DEG, &p);
182     index = global_tile_cache.exists(p);
183     if ( index < 0 ) {
184         FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found" );
185         return 0.0;
186     }
187
188     t = global_tile_cache.get_tile(index);
189
190     scenery.next_center = t->center;
191     
192     FG_LOG( FG_TERRAIN, FG_DEBUG, 
193             "Current bucket = " << p << "  Index = " << p.gen_index_str() );
194     FG_LOG( FG_TERRAIN, FG_DEBUG,
195             "abs_view_pos = " << abs_view_pos );
196
197     // calculate tile offset
198     // x = (t->offset.x = t->center.x - scenery.center.x);
199     // y = (t->offset.y = t->center.y - scenery.center.y);
200     // z = (t->offset.z = t->center.z - scenery.center.z);
201     
202     // calc current terrain elevation calculate distance from
203     // vertical tangent line at current position to center of
204     // tile.
205         
206     /* printf("distance squared = %.2f, bounding radius = %.2f\n", 
207        point_line_dist_squared(&(t->offset), &(v->view_pos), 
208        v->local_up), t->bounding_radius); */
209
210     dist = point_line_dist_squared( t->center, abs_view_pos, local_up );
211     if ( dist < FG_SQUARE(t->bounding_radius) ) {
212
213         // traverse fragment list for tile
214         FGTileEntry::FragmentIterator current = t->begin();
215         FGTileEntry::FragmentIterator last = t->end();
216
217         for ( ; current != last; ++current ) {
218             frag_ptr = &(*current);
219             /* printf("distance squared = %.2f, bounding radius = %.2f\n", 
220                point_line_dist_squared( &(frag_ptr->center), 
221                &abs_view_pos), local_up),
222                frag_ptr->bounding_radius); */
223
224             dist = point_line_dist_squared( frag_ptr->center,
225                                             abs_view_pos,
226                                             local_up);
227             if ( dist <= FG_SQUARE(frag_ptr->bounding_radius) ) {
228                 if ( frag_ptr->intersect( abs_view_pos, 
229                                           earth_center, 0, result ) ) {
230                     FG_LOG( FG_TERRAIN, FG_DEBUG, "intersection point " <<
231                             result );
232                     // compute geocentric coordinates of tile center
233                     Point3D pp = fgCartToPolar3d(result);
234                     FG_LOG( FG_TERRAIN, FG_DEBUG, "  polar form = " << pp );
235                     // convert to geodetic coordinates
236                     fgGeocToGeod(pp.lat(), pp.radius(), &lat_geod, 
237                                  &alt, &sea_level_r);
238
239                     // printf("alt = %.2f\n", alt);
240                     // exit since we found an intersection
241                     if ( alt > -9999.0 ) {
242                         // printf("returning alt\n");
243                         return alt;
244                     } else {
245                         // printf("returning 0\n");
246                         return 0.0;
247                     }
248                 }
249             }
250         }
251     }
252
253     FG_LOG( FG_TERRAIN, FG_INFO, "(new) no terrain intersection found" );
254
255     return 0.0;
256 }
257
258
259 // Determine scenery altitude.  Normally this just happens when we
260 // render the scene, but we'd also like to be able to do this
261 // explicitely.  lat & lon are in radians.  abs_view_pos in meters.
262 // Returns result in meters.
263 double
264 FGTileMgr::current_elev( double lon, double lat, const Point3D& abs_view_pos ) {
265     FGTileCache *c;
266     FGTileEntry *t;
267     fgFRAGMENT *frag_ptr;
268     Point3D earth_center(0.0);
269     Point3D result;
270     MAT3vec local_up;
271     double dist, lat_geod, alt, sea_level_r;
272     int index;
273
274     c = &global_tile_cache;
275
276     local_up[0] = abs_view_pos.x();
277     local_up[1] = abs_view_pos.y();
278     local_up[2] = abs_view_pos.z();
279
280     FG_LOG( FG_TERRAIN, FG_DEBUG, "Absolute view pos = " << abs_view_pos );
281
282     // Find current translation offset
283     FGBucket p( lon * RAD_TO_DEG, lat * RAD_TO_DEG );
284     index = c->exists(p);
285     if ( index < 0 ) {
286         FG_LOG( FG_TERRAIN, FG_WARN, "Tile not found" );
287         return 0.0;
288     }
289
290     t = c->get_tile(index);
291
292     scenery.next_center = t->center;
293     
294     FG_LOG( FG_TERRAIN, FG_DEBUG, 
295             "Pos = (" << lon * RAD_TO_DEG << ", " << lat * RAD_TO_DEG
296             << ")  Current bucket = " << p 
297             << "  Index = " << p.gen_index_str() );
298
299     FG_LOG( FG_TERRAIN, FG_DEBUG, "Tile center " << t->center 
300             << "  bounding radius = " << t->bounding_radius );
301
302     // calculate tile offset
303     // x = (t->offset.x = t->center.x - scenery.center.x);
304     // y = (t->offset.y = t->center.y - scenery.center.y);
305     // z = (t->offset.z = t->center.z - scenery.center.z);
306     
307     // calc current terrain elevation calculate distance from
308     // vertical tangent line at current position to center of
309     // tile.
310         
311     /* printf("distance squared = %.2f, bounding radius = %.2f\n", 
312        point_line_dist_squared(&(t->offset), &(v->view_pos), 
313        v->local_up), t->bounding_radius); */
314
315     dist = point_line_dist_squared( t->center, abs_view_pos, local_up );
316     FG_LOG( FG_TERRAIN, FG_DEBUG, "(gross check) dist squared = " << dist );
317
318     if ( dist < FG_SQUARE(t->bounding_radius) ) {
319
320         // traverse fragment list for tile
321         FGTileEntry::FragmentIterator current = t->begin();
322         FGTileEntry::FragmentIterator last = t->end();
323
324         for ( ; current != last; ++current ) {
325             frag_ptr = &(*current);
326             /* printf("distance squared = %.2f, bounding radius = %.2f\n", 
327                point_line_dist_squared( &(frag_ptr->center), 
328                &abs_view_pos), local_up),
329                frag_ptr->bounding_radius); */
330
331             dist = point_line_dist_squared( frag_ptr->center,
332                                             abs_view_pos,
333                                             local_up);
334             if ( dist <= FG_SQUARE(frag_ptr->bounding_radius) ) {
335                 if ( frag_ptr->intersect( abs_view_pos, 
336                                           earth_center, 0, result ) ) {
337                     FG_LOG( FG_TERRAIN, FG_DEBUG, "intersection point " <<
338                             result );
339                     // compute geocentric coordinates of tile center
340                     Point3D pp = fgCartToPolar3d(result);
341                     FG_LOG( FG_TERRAIN, FG_DEBUG, "  polar form = " << pp );
342                     // convert to geodetic coordinates
343                     fgGeocToGeod(pp.lat(), pp.radius(), &lat_geod, 
344                                  &alt, &sea_level_r);
345
346                     // printf("alt = %.2f\n", alt);
347                     // exit since we found an intersection
348                     if ( alt > -9999.0 ) {
349                         // printf("returning alt\n");
350                         return alt;
351                     } else {
352                         // printf("returning 0\n");
353                         return 0.0;
354                     }
355                 }
356             }
357         }
358     }
359
360     FG_LOG( FG_TERRAIN, FG_INFO, "(old) no terrain intersection found" );
361
362     return 0.0;
363 }
364
365
366 // Determine scenery altitude via ssg.  Normally this just happens
367 // when we render the scene, but we'd also like to be able to do this
368 // explicitely.  lat & lon are in radians.  view_pos in current world
369 // coordinate translated near (0,0,0) (in meters.)  Returns result in
370 // meters.
371 double
372 FGTileMgr::current_elev_ssg( const Point3D& abs_view_pos, 
373                              const Point3D& view_pos )
374 {
375     ssgHit *results ;
376
377     // cout << "view pos = " << view_pos << endl;
378     // cout << "abs view pos = " << abs_view_pos << endl;
379
380     sgMat4 m;
381     sgMakeTransMat4( m, view_pos.x(), view_pos.y(), view_pos.z() );
382
383     sgVec3 s;
384     sgSetVec3(s, -abs_view_pos.x(), -abs_view_pos.y(), -abs_view_pos.z() );
385
386     int num_hits = ssgLOS ( scene, s, m, &results ) ;
387
388     for ( int i = 0 ; i < num_hits ; i++ ) {
389         ssgHit *h = &(results [ i ]) ;
390         cout << "got a hit!" << endl;
391         /* Do something with 'h' */
392     }
393
394     FG_LOG( FG_TERRAIN, FG_INFO, "(ssg) no terrain intersection found" );
395
396     return 0.0;
397 }
398
399
400 // given the current lon/lat, fill in the array of local chunks.  If
401 // the chunk isn't already in the cache, then read it from disk.
402 int FGTileMgr::update( void ) {
403     FGTileCache *c;
404     FGInterface *f;
405     FGBucket p2;
406     static FGBucket p_last(false);
407     static double last_lon = -1000.0;  // in degrees
408     static double last_lat = -1000.0;  // in degrees
409     int tile_diameter;
410     int i, j, dw, dh;
411
412     c = &global_tile_cache;
413     f = current_aircraft.fdm_state;
414
415     tile_diameter = current_options.get_tile_diameter();
416
417     FGBucket p1( f->get_Longitude() * RAD_TO_DEG,
418                  f->get_Latitude() * RAD_TO_DEG );
419     dw = tile_diameter / 2;
420     dh = tile_diameter / 2;
421
422     if ( (p1 == p_last) && (state == Running) ) {
423         // same bucket as last time
424         FG_LOG( FG_TERRAIN, FG_DEBUG, "Same bucket as last time" );
425     } else if ( (state == Start) || (state == Inited) ) {
426         state = Running;
427
428         // First time through or we have teleported, initialize the
429         // system and load all relavant tiles
430
431         FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << p1 );
432         FG_LOG( FG_TERRAIN, FG_INFO, "  First time through ... " );
433         FG_LOG( FG_TERRAIN, FG_INFO, "  Updating Tile list for " << p1 );
434         FG_LOG( FG_TERRAIN, FG_INFO, "  Loading " 
435                 << tile_diameter * tile_diameter << " tiles" );
436
437         // wipe/initialize tile cache
438         c->init();
439         p_last.make_bad();
440
441         // build the local area list and schedule tiles for loading
442
443         // start with the center tile and work out in concentric
444         // "rings"
445
446         p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
447                              f->get_Latitude() * RAD_TO_DEG,
448                              0, 0 );
449         sched_tile( p2 );
450
451         for ( i = 3; i <= tile_diameter; i = i + 2 ) {
452             int span = i / 2;
453
454             // bottom row
455             for ( j = -span; j <= span; ++j ) {
456                 p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
457                                      f->get_Latitude() * RAD_TO_DEG,
458                                      j, -span );
459                 sched_tile( p2 );
460             }
461
462             // top row
463             for ( j = -span; j <= span; ++j ) {
464                 p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
465                                      f->get_Latitude() * RAD_TO_DEG,
466                                      j, span );
467                 sched_tile( p2 );
468             }
469
470             // middle rows
471             for ( j = -span + 1; j <= span - 1; ++j ) {
472                 p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
473                                      f->get_Latitude() * RAD_TO_DEG,
474                                      -span, j );
475                 sched_tile( p2 );
476                 p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
477                                      f->get_Latitude() * RAD_TO_DEG,
478                                      span, j );
479                 sched_tile( p2 );
480             }
481
482         }
483
484         /* for ( j = 0; j < tile_diameter; j++ ) {
485             for ( i = 0; i < tile_diameter; i++ ) {
486                 // fgBucketOffset(&p1, &p2, i - dw, j - dh);
487                 p2 = fgBucketOffset( f->get_Longitude() * RAD_TO_DEG,
488                                      f->get_Latitude() * RAD_TO_DEG,
489                                      i - dw, j -dh );
490                 sched_tile( p2 );
491             }
492         } */
493
494         // Now force a load of the center tile and inner ring so we
495         // have something to see in our first frame.
496         for ( i = 0; i < 9; ++i ) {
497             if ( load_queue.size() ) {
498                 FG_LOG( FG_TERRAIN, FG_INFO, 
499                         "Load queue not empty, loading a tile" );
500             
501                 FGLoadRec pending = load_queue.front();
502                 load_queue.pop_front();
503                 load_tile( pending.b, pending.cache_index );
504             }
505         }
506
507     } else {
508         // We've moved to a new bucket, we need to scroll our
509         // structures, and load in the new tiles
510
511 #if 0 
512         // make sure load queue is flushed before doing shift
513         while ( load_queue.size() ) {
514             FG_LOG( FG_TERRAIN, FG_INFO, 
515                     "Load queue not empty, flushing queue before tile shift." );
516             
517             FGLoadRec pending = load_queue.front();
518             load_queue.pop_front();
519             load_tile( pending.b, pending.index );
520         }
521 #endif
522
523         // CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES.
524         // AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF
525         // THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION.
526
527         FG_LOG( FG_TERRAIN, FG_INFO, "Updating Tile list for " << p1 );
528
529         if ( (p1.get_lon() > p_last.get_lon()) ||
530              ( (p1.get_lon() == p_last.get_lon()) && 
531                (p1.get_x() > p_last.get_x()) ) ) {
532             FG_LOG( FG_TERRAIN, FG_INFO, 
533                     "  (East) Loading " << tile_diameter << " tiles" );
534             for ( j = 0; j < tile_diameter; j++ ) {
535                 // scrolling East
536                 // schedule new column
537                 p2 = fgBucketOffset( last_lon, last_lat, dw + 1, j - dh );
538                 sched_tile( p2 );
539             }
540         } else if ( (p1.get_lon() < p_last.get_lon()) ||
541                     ( (p1.get_lon() == p_last.get_lon()) && 
542                       (p1.get_x() < p_last.get_x()) ) ) {
543             FG_LOG( FG_TERRAIN, FG_INFO, 
544                     "  (West) Loading " << tile_diameter << " tiles" );
545             for ( j = 0; j < tile_diameter; j++ ) {
546                 // scrolling West
547                 // schedule new column
548                 p2 = fgBucketOffset( last_lon, last_lat, -dw - 1, j - dh );
549                 sched_tile( p2 );
550             }
551         }
552
553         if ( (p1.get_lat() > p_last.get_lat()) ||
554              ( (p1.get_lat() == p_last.get_lat()) && 
555                (p1.get_y() > p_last.get_y()) ) ) {
556             FG_LOG( FG_TERRAIN, FG_INFO, 
557                     "  (North) Loading " << tile_diameter << " tiles" );
558             for ( i = 0; i < tile_diameter; i++ ) {
559                 // scrolling North
560                 // schedule new row
561                 p2 = fgBucketOffset( last_lon, last_lat, i - dw, dh + 1);
562                 sched_tile( p2 );
563             }
564         } else if ( (p1.get_lat() < p_last.get_lat()) ||
565                     ( (p1.get_lat() == p_last.get_lat()) && 
566                       (p1.get_y() < p_last.get_y()) ) ) {
567             FG_LOG( FG_TERRAIN, FG_INFO, 
568                     "  (South) Loading " << tile_diameter << " tiles" );
569             for ( i = 0; i < tile_diameter; i++ ) {
570                 // scrolling South
571                 // schedule new row
572                 p2 = fgBucketOffset( last_lon, last_lat, i - dw, -dh - 1);
573                 sched_tile( p2 );
574             }
575         }
576     }
577
578     if ( load_queue.size() ) {
579         FG_LOG( FG_TERRAIN, FG_INFO, "Load queue not empty, loading a tile" );
580
581         FGLoadRec pending = load_queue.front();
582         load_queue.pop_front();
583         load_tile( pending.b, pending.cache_index );
584     }
585
586     // find our current elevation (feed in the current bucket to save work)
587     Point3D geod_pos = Point3D( f->get_Longitude(), f->get_Latitude(), 0.0);
588     Point3D tmp_abs_view_pos = fgGeodToCart(geod_pos);
589
590     scenery.cur_elev = 
591         current_elev( f->get_Longitude(), f->get_Latitude(), tmp_abs_view_pos );
592     // cout << "current elevation == " << scenery.cur_elev << endl;
593     // double junk = current_elev_ssg( current_view.abs_view_pos,
594     //                                 current_view.view_pos );
595     // cout << "current elevation (ssg) == " << 
596         
597     p_last = p1;
598     last_lon = f->get_Longitude() * RAD_TO_DEG;
599     last_lat = f->get_Latitude() * RAD_TO_DEG;
600
601     return 1;
602 }
603
604
605 // NEW 
606
607 // inrange() IS THIS POINT WITHIN POSSIBLE VIEWING RANGE ?
608 //      calculate distance from vertical tangent line at
609 //      current position to center of object.
610 //      this is equivalent to
611 //      dist = point_line_dist_squared( &(t->center), &(v->abs_view_pos), 
612 //                                      v->local_up );
613 //      if ( dist < FG_SQUARE(t->bounding_radius) ) {
614 //
615 // the compiler should inline this for us
616
617 static int
618 inrange( const double radius, const Point3D& center, const Point3D& vp,
619          const MAT3vec up)
620 {
621     MAT3vec u, u1, v;
622     //  double tmp;
623         
624     // u = p - p0
625     u[0] = center.x() - vp.x();
626     u[1] = center.y() - vp.y();
627     u[2] = center.z() - vp.z();
628         
629     // calculate the projection, u1, of u along d.
630     // u1 = ( dot_prod(u, d) / dot_prod(d, d) ) * d;
631         
632     MAT3_SCALE_VEC(u1, up,
633                    (MAT3_DOT_PRODUCT(u, up) / MAT3_DOT_PRODUCT(up, up)) );
634     
635     // v = u - u1 = vector from closest point on line, p1, to the
636     // original point, p.
637     MAT3_SUB_VEC(v, u, u1);
638         
639     return( FG_SQUARE(radius) >= MAT3_DOT_PRODUCT(v, v));
640 }
641
642
643 // NEW for legibility
644
645 // update this tile's geometry for current view
646 // The Compiler should inline this
647 static void
648 update_tile_geometry( FGTileEntry *t, GLdouble *MODEL_VIEW)
649 {
650     GLfloat *m;
651     double x, y, z;
652         
653     // calculate tile offset
654     t->offset = t->center - scenery.center;
655
656     x = t->offset.x();
657     y = t->offset.y();
658     z = t->offset.z();
659         
660     m = t->model_view;
661         
662     // Calculate the model_view transformation matrix for this tile
663     FG_MEM_COPY( m, MODEL_VIEW, 16*sizeof(GLdouble) );
664     
665     // This is equivalent to doing a glTranslatef(x, y, z);
666     m[12] += (m[0]*x + m[4]*y + m[8] *z);
667     m[13] += (m[1]*x + m[5]*y + m[9] *z);
668     m[14] += (m[2]*x + m[6]*y + m[10]*z);
669     // m[15] += (m[3]*x + m[7]*y + m[11]*z);
670     // m[3] m7[] m[11] are 0.0 see LookAt() in views.cxx
671     // so m[15] is unchanged
672 }
673
674
675 // Prepare the ssg nodes ... for each tile, set it's proper
676 // transform and update it's range selector based on current
677 // visibilty
678 void FGTileMgr::prep_ssg_nodes( void ) {
679     FGTileEntry *t;
680
681     float ranges[2];
682     ranges[0] = 0.0f;
683
684     // traverse the potentially viewable tile list and update range
685     // selector and transform
686     for ( int i = 0; i < (int)global_tile_cache.get_size(); i++ ) {
687         t = global_tile_cache.get_tile( i );
688
689         if ( t->is_loaded() ) {
690             // set range selector (LOD trick) to be distance to center
691             // of tile + bounding radius
692             /*
693              * ranges[1] = current_weather.get_visibility()+t->bounding_radius;
694              */
695             ranges[1] = WeatherDatabase->getWeatherVisibility()
696                 + t->bounding_radius;
697             t->range_ptr->setRanges( ranges, 2 );
698
699             // calculate tile offset
700             t->SetOffset( scenery.center );
701
702             // calculate ssg transform
703             sgCoord sgcoord;
704             sgSetCoord( &sgcoord,
705                         t->offset.x(), t->offset.y(), t->offset.z(),
706                         0.0, 0.0, 0.0 );
707             t->transform_ptr->setTransform( &sgcoord );
708         }
709     }
710 }