]> git.mxchange.org Git - flightgear.git/blob - Scenery/tilemgr.cxx
Frist pass at view frustum culling now operational.
[flightgear.git] / Scenery / tilemgr.cxx
1 /*
2  * tilemgr.cxx -- routines to handle dynamic management of scenery tiles
3  *
4  * Written by Curtis Olson, started January 1998.
5  *
6  * Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * $Id$
23  * (Log is kept at end of this file)
24  **************************************************************************/
25
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #ifdef HAVE_WINDOWS_H
32 #  include <windows.h>
33 #endif
34
35 #include <GL/glut.h>
36 #include <XGL/xgl.h>
37
38 #include <Scenery/obj.hxx>
39 #include <Scenery/scenery.hxx>
40 #include <Scenery/tilecache.hxx>
41
42 #include <Aircraft/aircraft.h>
43 #include <Bucket/bucketutils.h>
44 #include <Debug/fg_debug.h>
45 #include <Include/fg_constants.h>
46 #include <Include/fg_types.h>
47 #include <Main/options.hxx>
48 #include <Main/views.hxx>
49 #include <Math/mat3.h>
50
51
52 #define FG_LOCAL_X_Y         81  /* max(o->tile_diameter) ** 2 */
53
54
55 /* closest (potentially viewable) tiles, centered on current tile.
56  * This is an array of pointers to cache indexes. */
57 int tiles[FG_LOCAL_X_Y];
58
59
60 /* Initialize the Tile Manager subsystem */
61 int fgTileMgrInit( void ) {
62     fgPrintf( FG_TERRAIN, FG_INFO, "Initializing Tile Manager subsystem.\n");
63     return 1;
64 }
65
66
67 /* load a tile */
68 void fgTileMgrLoadTile( struct fgBUCKET *p, int *index) {
69     fgPrintf( FG_TERRAIN, FG_DEBUG, "Updating for bucket %d %d %d %d\n", 
70            p->lon, p->lat, p->x, p->y);
71     
72     /* if not in cache, load tile into the next available slot */
73     if ( (*index = fgTileCacheExists(p)) < 0 ) {
74         *index = fgTileCacheNextAvail();
75         fgTileCacheEntryFillIn(*index, p);
76     }
77
78     fgPrintf( FG_TERRAIN, FG_DEBUG, "Selected cache index of %d\n", *index);
79 }
80
81
82 /* given the current lon/lat, fill in the array of local chunks.  If
83  * the chunk isn't already in the cache, then read it from disk. */
84 int fgTileMgrUpdate( void ) {
85     fgFLIGHT *f;
86     fgOPTIONS *o;
87     struct fgBUCKET p1, p2;
88     static struct fgBUCKET p_last = {-1000, 0, 0, 0};
89     int i, j, dw, dh;
90
91     f = current_aircraft.flight;
92     o = &current_options;
93
94     fgBucketFind(FG_Longitude * RAD_TO_DEG, FG_Latitude * RAD_TO_DEG, &p1);
95     dw = o->tile_diameter / 2;
96     dh = o->tile_diameter / 2;
97
98     if ( (p1.lon == p_last.lon) && (p1.lat == p_last.lat) &&
99          (p1.x == p_last.x) && (p1.y == p_last.y) ) {
100         /* same bucket as last time */
101         fgPrintf( FG_TERRAIN, FG_DEBUG, "Same bucket as last time\n");
102     } else if ( p_last.lon == -1000 ) {
103         /* First time through, initialize the system and load all
104          * relavant tiles */
105
106         fgPrintf( FG_TERRAIN, FG_INFO, "  First time through ... ");
107         fgPrintf( FG_TERRAIN, FG_INFO, "  Updating Tile list for %d,%d %d,%d\n",
108                   p1.lon, p1.lat, p1.x, p1.y);
109         fgPrintf( FG_TERRAIN, FG_INFO, "  Loading %d tiles\n", 
110                   o->tile_diameter * o->tile_diameter);
111
112         /* wipe tile cache */
113         fgTileCacheInit();
114
115         /* build the local area list and update cache */
116         for ( j = 0; j < o->tile_diameter; j++ ) {
117             for ( i = 0; i < o->tile_diameter; i++ ) {
118                 fgBucketOffset(&p1, &p2, i - dw, j - dh);
119                 fgTileMgrLoadTile(&p2, &tiles[(j*o->tile_diameter) + i]);
120             }
121         }
122     } else {
123         /* We've moved to a new bucket, we need to scroll our
124          * structures, and load in the new tiles */
125
126         /* CURRENTLY THIS ASSUMES WE CAN ONLY MOVE TO ADJACENT TILES.
127            AT ULTRA HIGH SPEEDS THIS ASSUMPTION MAY NOT BE VALID IF
128            THE AIRCRAFT CAN SKIP A TILE IN A SINGLE ITERATION. */
129
130         fgPrintf( FG_TERRAIN, FG_INFO, "Updating Tile list for %d,%d %d,%d\n",
131                   p1.lon, p1.lat, p1.x, p1.y);
132
133         if ( (p1.lon > p_last.lon) ||
134              ( (p1.lon == p_last.lon) && (p1.x > p_last.x) ) ) {
135             fgPrintf( FG_TERRAIN, FG_INFO, "  Loading %d tiles\n", 
136                       o->tile_diameter);
137             for ( j = 0; j < o->tile_diameter; j++ ) {
138                 /* scrolling East */
139                 for ( i = 0; i < o->tile_diameter - 1; i++ ) {
140                     tiles[(j*o->tile_diameter) + i] = tiles[(j*o->tile_diameter) + i + 1];
141                 }
142                 /* load in new column */
143                 fgBucketOffset(&p_last, &p2, dw + 1, j - dh);
144                 fgTileMgrLoadTile(&p2, &tiles[(j*o->tile_diameter) + o->tile_diameter - 1]);
145             }
146         } else if ( (p1.lon < p_last.lon) ||
147                     ( (p1.lon == p_last.lon) && (p1.x < p_last.x) ) ) {
148             fgPrintf( FG_TERRAIN, FG_INFO, "  Loading %d tiles\n", 
149                       o->tile_diameter);
150             for ( j = 0; j < o->tile_diameter; j++ ) {
151                 /* scrolling West */
152                 for ( i = o->tile_diameter - 1; i > 0; i-- ) {
153                     tiles[(j*o->tile_diameter) + i] = tiles[(j*o->tile_diameter) + i - 1];
154                 }
155                 /* load in new column */
156                 fgBucketOffset(&p_last, &p2, -dw - 1, j - dh);
157                 fgTileMgrLoadTile(&p2, &tiles[(j*o->tile_diameter) + 0]);
158             }
159         }
160
161         if ( (p1.lat > p_last.lat) ||
162              ( (p1.lat == p_last.lat) && (p1.y > p_last.y) ) ) {
163             fgPrintf( FG_TERRAIN, FG_INFO, "  Loading %d tiles\n", 
164                       o->tile_diameter);
165             for ( i = 0; i < o->tile_diameter; i++ ) {
166                 /* scrolling North */
167                 for ( j = 0; j < o->tile_diameter - 1; j++ ) {
168                     tiles[(j * o->tile_diameter) + i] =
169                         tiles[((j+1) * o->tile_diameter) + i];
170                 }
171                 /* load in new column */
172                 fgBucketOffset(&p_last, &p2, i - dw, dh + 1);
173                 fgTileMgrLoadTile(&p2, &tiles[((o->tile_diameter-1)*o->tile_diameter) + i]);
174             }
175         } else if ( (p1.lat < p_last.lat) ||
176                     ( (p1.lat == p_last.lat) && (p1.y < p_last.y) ) ) {
177             fgPrintf( FG_TERRAIN, FG_INFO, "  Loading %d tiles\n", 
178                       o->tile_diameter);
179             for ( i = 0; i < o->tile_diameter; i++ ) {
180                 /* scrolling South */
181                 for ( j = o->tile_diameter - 1; j > 0; j-- ) {
182                     tiles[(j * o->tile_diameter) + i] = 
183                         tiles[((j-1) * o->tile_diameter) + i];
184                 }
185                 /* load in new column */
186                 fgBucketOffset(&p_last, &p2, i - dw, -dh - 1);
187                 fgTileMgrLoadTile(&p2, &tiles[0 + i]);
188             }
189         }
190     }
191     p_last.lon = p1.lon;
192     p_last.lat = p1.lat;
193     p_last.x = p1.x;
194     p_last.y = p1.y;
195     return 1;
196 }
197
198
199 // Calculate if point/radius is inside view frustum
200 // 
201 static int viewable( fgCartesianPoint3d *cp, double radius ) {
202     fgVIEW *v;
203     MAT3hvec world, eye;
204     int viewable = 1; // start by assuming it's viewable
205     double x0, x1, y1, slope;
206
207     v = &current_view;
208
209     MAT3_SET_HVEC(world, cp->x, cp->y, cp->z, 1.0);
210     MAT3mult_vec(eye, world, v->WORLD_TO_EYE);
211     // printf( "\nworld -> eye = %.2f %.2f %.2f  radius = %.2f\n", 
212     //         eye[0], eye[1], eye[2], radius);
213
214     // Check near clip plane
215     if ( eye[2] - radius > 0.0 ) {
216         return(0);
217     }
218
219     // check right clip plane (from eye perspective)
220     // y = m * (x - x0) = equation of a line intercepting X axis at x0
221     x1 = v->cos_fov_x * radius;
222     y1 = v->sin_fov_x * radius;
223     slope = -1.0 / v->slope_x;
224     x0 = x1 - y1 / slope;
225
226     // printf("(r) x1 = %.2f  y1 = %.2f\n", x1, y1);
227     // printf("eye[0] = %.2f  eye[2] = %.2f\n", eye[0], eye[2]);
228     // printf("(r) x0 = %.2f  slope_x = %.2f  radius = %.2f\n", 
229     //        x0, slope, radius);
230
231     if ( eye[2] > slope * (eye[0] - x0) ) {
232         return(0);
233     }
234
235     // check left clip plane (from eye perspective)
236     x1 = -x1;
237     slope = -slope;
238     x0 = x1 - y1 / slope;
239
240     // printf("(r) x1 = %.2f  y1 = %.2f\n", x1, y1);
241     // printf("eye[0] = %.2f  eye[2] = %.2f\n", eye[0], eye[2]);
242     // printf("(r) x0 = %.2f  slope_x = %.2f  radius = %.2f\n", 
243     //        x0, slope, radius);
244
245     if ( eye[2] > slope * (eye[0] - x0) ) {
246         return(0);
247     }
248
249     // check bottom clip plane (from eye perspective)
250     x1 = -(v->cos_fov_y) * radius;
251     y1 = v->sin_fov_y * radius;
252     slope = 1.0 / v->slope_y;
253     x0 = x1 - y1 / slope;
254
255     // printf("(r) x1 = %.2f  y1 = %.2f\n", x1, y1);
256     // printf("eye[1] = %.2f  eye[2] = %.2f\n", eye[1], eye[2]);
257     // printf("(r) x0 = %.2f  slope_y = %.2f  radius = %.2f\n", 
258     //       x0, slope, radius);
259
260     if ( eye[2] > slope * (eye[1] - x0) ) {
261         return(0);
262     }
263
264     // check top clip plane (from eye perspective)
265     x1 = -x1;
266     slope = -slope;
267     x0 = x1 - y1 / slope;
268
269     // printf("(r) x1 = %.2f  y1 = %.2f\n", x1, y1);
270     // printf("eye[1] = %.2f  eye[2] = %.2f\n", eye[1], eye[2]);
271     // printf("(r) x0 = %.2f  slope_y = %.2f  radius = %.2f\n", 
272     //        x0, slope, radius);
273
274     if ( eye[2] > slope * (eye[1] - x0) ) {
275         return(0);
276     }
277
278     return(viewable);
279 }
280
281
282 /* Render the local tiles */
283 void fgTileMgrRender( void ) {
284     fgFLIGHT *f;
285     fgOPTIONS *o;
286     fgVIEW *v;
287     struct fgBUCKET p;
288     fgCartesianPoint3d local_ref, offset;
289     GLint display_list;
290     double radius;
291     int i;
292     int index;
293     int culled = 0;
294     int drawn = 0;
295
296     f = current_aircraft.flight;
297     o = &current_options;
298     v = &current_view;
299
300     /* Find current translation offset */
301     fgBucketFind(FG_Longitude * RAD_TO_DEG, FG_Latitude * RAD_TO_DEG, &p);
302     index = fgTileCacheExists(&p);
303     fgTileCacheEntryInfo(index, &display_list, &scenery.next_center, &radius );
304
305     fgPrintf( FG_TERRAIN, FG_DEBUG, 
306               "Pos = (%.2f, %.2f) Current bucket = %d %d %d %d  Index = %ld\n", 
307               FG_Longitude * RAD_TO_DEG, FG_Latitude * RAD_TO_DEG,
308               p.lon, p.lat, p.x, p.y, fgBucketGenIndex(&p) );
309
310     for ( i = 0; i < (o->tile_diameter * o->tile_diameter); i++ ) {
311         index = tiles[i];
312         /* fgPrintf( FG_TERRAIN, FG_DEBUG, "Index = %d\n", index); */
313         fgTileCacheEntryInfo(index, &display_list, &local_ref, &radius );
314
315         if ( display_list >= 0 ) {
316
317             offset.x = local_ref.x - scenery.center.x;
318             offset.y = local_ref.y - scenery.center.y;
319             offset.z = local_ref.z - scenery.center.z;
320
321             if ( viewable(&offset, radius) ) {
322                 drawn++;
323                 xglPushMatrix();
324                 xglTranslatef(offset.x, offset.y, offset.z);
325                 xglCallList(display_list);
326                 xglPopMatrix();
327             } else {
328                 culled++;
329             }
330         }
331     }
332
333     v->vfc_ratio = (double)culled / (double)(drawn + culled);
334     // printf("drawn = %d  culled = %d  saved = %.2f\n", drawn, culled, 
335     //        v->vfc_ratio);
336 }
337
338
339 /* $Log$
340 /* Revision 1.10  1998/05/17 16:59:34  curt
341 /* Frist pass at view frustum culling now operational.
342 /*
343  * Revision 1.9  1998/05/16 13:09:58  curt
344  * Beginning to add support for view frustum culling.
345  * Added some temporary code to calculate bouding radius, until the
346  *   scenery generation tools and scenery can be updated.
347  *
348  * Revision 1.8  1998/05/07 23:15:21  curt
349  * Fixed a glTexImage2D() usage bug where width and height were mis-swapped.
350  * Added support for --tile-radius=n option.
351  *
352  * Revision 1.7  1998/05/06 03:16:42  curt
353  * Added an option to control square tile radius.
354  *
355  * Revision 1.6  1998/05/02 01:52:18  curt
356  * Playing around with texture coordinates.
357  *
358  * Revision 1.5  1998/04/30 12:35:32  curt
359  * Added a command line rendering option specify smooth/flat shading.
360  *
361  * Revision 1.4  1998/04/27 03:30:14  curt
362  * Minor transformation adjustments to try to keep scenery tiles closer to
363  * (0, 0, 0)  GLfloats run out of precision at the distances we need to model
364  * the earth, but we can do a bunch of pre-transformations using double math
365  * and then cast to GLfloat once everything is close in where we have less
366  * precision problems.
367  *
368  * Revision 1.3  1998/04/25 22:06:32  curt
369  * Edited cvs log messages in source files ... bad bad bad!
370  *
371  * Revision 1.2  1998/04/24 00:51:09  curt
372  * Wrapped "#include <config.h>" in "#ifdef HAVE_CONFIG_H"
373  * Tweaked the scenery file extentions to be "file.obj" (uncompressed)
374  * or "file.obz" (compressed.)
375  *
376  * Revision 1.1  1998/04/22 13:22:48  curt
377  * C++ - ifing the code a bit.
378  *
379  * Revision 1.25  1998/04/18 04:14:07  curt
380  * Moved fg_debug.c to it's own library.
381  *
382  * Revision 1.24  1998/04/14 02:23:18  curt
383  * Code reorganizations.  Added a Lib/ directory for more general libraries.
384  *
385  * Revision 1.23  1998/04/08 23:30:08  curt
386  * Adopted Gnu automake/autoconf system.
387  *
388  * Revision 1.22  1998/04/03 22:11:38  curt
389  * Converting to Gnu autoconf system.
390  *
391  * Revision 1.21  1998/03/23 21:23:05  curt
392  * Debugging output tweaks.
393  *
394  * Revision 1.20  1998/03/14 00:30:51  curt
395  * Beginning initial terrain texturing experiments.
396  *
397  * Revision 1.19  1998/02/20 00:16:25  curt
398  * Thursday's tweaks.
399  *
400  * Revision 1.18  1998/02/19 13:05:54  curt
401  * Incorporated some HUD tweaks from Michelle America.
402  * Tweaked the sky's sunset/rise colors.
403  * Other misc. tweaks.
404  *
405  * Revision 1.17  1998/02/16 13:39:46  curt
406  * Miscellaneous weekend tweaks.  Fixed? a cache problem that caused whole
407  * tiles to occasionally be missing.
408  *
409  * Revision 1.16  1998/02/12 21:59:53  curt
410  * Incorporated code changes contributed by Charlie Hotchkiss
411  * <chotchkiss@namg.us.anritsu.com>
412  *
413  * Revision 1.14  1998/02/09 21:30:19  curt
414  * Fixed a nagging problem with terrain tiles not "quite" matching up perfectly.
415  *
416  * Revision 1.13  1998/02/07 15:29:46  curt
417  * Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss
418  * <chotchkiss@namg.us.anritsu.com>
419  *
420  * Revision 1.12  1998/02/01 03:39:55  curt
421  * Minor tweaks.
422  *
423  * Revision 1.11  1998/01/31 00:43:27  curt
424  * Added MetroWorks patches from Carmen Volpe.
425  *
426  * Revision 1.10  1998/01/29 00:51:40  curt
427  * First pass at tile cache, dynamic tile loading and tile unloading now works.
428  *
429  * Revision 1.9  1998/01/27 03:26:44  curt
430  * Playing with new fgPrintf command.
431  *
432  * Revision 1.8  1998/01/27 00:48:04  curt
433  * Incorporated Paul Bleisch's <pbleisch@acm.org> new debug message
434  * system and commandline/config file processing code.
435  *
436  * Revision 1.7  1998/01/26 15:55:25  curt
437  * Progressing on building dynamic scenery system.
438  *
439  * Revision 1.6  1998/01/24 00:03:30  curt
440  * Initial revision.
441  *
442  * Revision 1.5  1998/01/19 19:27:18  curt
443  * Merged in make system changes from Bob Kuehne <rpk@sgi.com>
444  * This should simplify things tremendously.
445  *
446  * Revision 1.4  1998/01/19 18:40:38  curt
447  * Tons of little changes to clean up the code and to remove fatal errors
448  * when building with the c++ compiler.
449  *
450  * Revision 1.3  1998/01/13 00:23:11  curt
451  * Initial changes to support loading and management of scenery tiles.  Note,
452  * there's still a fair amount of work left to be done.
453  *
454  * Revision 1.2  1998/01/08 02:22:27  curt
455  * Continue working on basic features.
456  *
457  * Revision 1.1  1998/01/07 23:50:51  curt
458  * "area" renamed to "tile"
459  *
460  * Revision 1.2  1998/01/07 03:29:29  curt
461  * Given an arbitrary lat/lon, we can now:
462  *   generate a unique index for the chunk containing the lat/lon
463  *   generate a path name to the chunk file
464  *   build a list of the indexes of all the nearby areas.
465  *
466  * Revision 1.1  1998/01/07 02:05:48  curt
467  * Initial revision.
468  * */
469
470