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