]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/newcache.cxx
Rewrote the tile scheme to use a "map" structure rather than "vector"
[flightgear.git] / src / Scenery / newcache.cxx
1 // newcache.cxx -- routines to handle scenery tile caching
2 //
3 // Written by Curtis Olson, started December 2000.
4 //
5 // Copyright (C) 2000  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 #ifdef HAVE_WINDOWS_H
29 #  include <windows.h>
30 #endif
31
32 #include <GL/glut.h>
33 #include <simgear/xgl/xgl.h>
34
35 #include <plib/ssg.h>           // plib include
36
37 #include <simgear/bucket/newbucket.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/misc/fgstream.hxx>
40 #include <simgear/misc/fgpath.hxx>
41
42 #include <Main/globals.hxx>
43 #include <Objects/obj.hxx>
44 #include <Scenery/scenery.hxx>  // for scenery.center
45
46 #include "newcache.hxx"
47 #include "tileentry.hxx"
48
49 FG_USING_NAMESPACE(std);
50
51 // a cheesy hack (to be fixed later)
52 extern ssgBranch *terrain;
53 extern ssgEntity *penguin;
54
55
56 // the tile cache
57 FGNewCache global_tile_cache;
58
59
60 // Constructor
61 FGNewCache::FGNewCache( void ) {
62     tile_cache.clear();
63 }
64
65
66 // Destructor
67 FGNewCache::~FGNewCache( void ) {
68 }
69
70
71 // Free a tile cache entry
72 void FGNewCache::entry_free( long cache_index ) {
73     FG_LOG( FG_TERRAIN, FG_INFO, "FREEING CACHE ENTRY = " << cache_index );
74     FGTileEntry *e = tile_cache[cache_index];
75     e->free_tile();
76     delete( e );
77     tile_cache.erase( cache_index );
78 }
79
80
81 // Initialize the tile cache subsystem
82 void FGNewCache::init( void ) {
83     FG_LOG( FG_TERRAIN, FG_INFO, "Initializing the tile cache." );
84
85     // expand cache if needed.  For best results ... i.e. to avoid
86     // tile load problems and blank areas: 
87     max_cache_size = 50;        // a random number to start with
88     FG_LOG( FG_TERRAIN, FG_INFO, "  max cache size = " 
89             << max_cache_size );
90     FG_LOG( FG_TERRAIN, FG_INFO, "  current cache size = " 
91             << tile_cache.size() );
92     
93     tile_map_iterator current = tile_cache.begin();
94     tile_map_iterator end = tile_cache.end();
95     
96     for ( ; current != end; ++current ) {
97         long index = current->first;
98         cout << "clearing " << index << endl;
99         FGTileEntry *e = current->second;
100         e->tile_bucket.make_bad();
101         entry_free(index);
102     }
103
104     // and ... just in case we missed something ... 
105     terrain->removeAllKids();
106
107     FG_LOG( FG_TERRAIN, FG_INFO, "  done with init()"  );
108 }
109
110
111 // Search for the specified "bucket" in the cache
112 bool FGNewCache::exists( const FGBucket& b ) {
113     long tile_index = b.gen_index();
114     tile_map_iterator it = tile_cache.find( tile_index );
115
116     return ( it != tile_cache.end() );
117 }
118
119
120 #if 0
121 static void print_refs( ssgSelector *sel, ssgTransform *trans, 
122                  ssgRangeSelector *range) 
123 {
124     cout << "selector -> " << sel->getRef()
125          << "  transform -> " << trans->getRef()
126          << "  range -> " << range->getRef() << endl;
127 }
128 #endif
129
130
131 // Fill in a tile cache entry with real data for the specified bucket
132 void FGNewCache::fill_in( const FGBucket& b ) {
133     FG_LOG( FG_TERRAIN, FG_INFO, "FILL IN CACHE ENTRY = " << b.gen_index() );
134
135     // clear out a distant entry in the cache if needed.
136     make_space();
137
138     // create the entry
139     FGTileEntry *e = new FGTileEntry;
140
141     // register it in the cache
142     long tile_index = b.gen_index();
143     tile_cache[tile_index] = e;
144
145     // update the contents
146     e->center = Point3D( 0.0 );
147     if ( e->vec3_ptrs.size() || e->vec2_ptrs.size() || e->index_ptrs.size() ) {
148         FG_LOG( FG_TERRAIN, FG_ALERT, 
149                 "Attempting to overwrite existing or"
150                 << " not properly freed leaf data." );
151         exit(-1);
152     }
153
154     e->select_ptr = new ssgSelector;
155     e->transform_ptr = new ssgTransform;
156     e->range_ptr = new ssgRangeSelector;
157     e->tile_bucket = b;
158
159     FGPath tile_path;
160     if ( globals->get_options()->get_fg_scenery() != "" ) {
161         tile_path.set( globals->get_options()->get_fg_scenery() );
162     } else {
163         tile_path.set( globals->get_options()->get_fg_root() );
164         tile_path.append( "Scenery" );
165     }
166     tile_path.append( b.gen_base_path() );
167     
168     // Load the appropriate data file
169     FGPath tile_base = tile_path;
170     tile_base.append( b.gen_index_str() );
171     ssgBranch *new_tile = fgObjLoad( tile_base.str(), e, true );
172
173     if ( new_tile != NULL ) {
174         e->range_ptr->addKid( new_tile );
175     }
176   
177     // load custom objects
178     cout << "CUSTOM OBJECTS" << endl;
179
180     FGPath index_path = tile_path;
181     index_path.append( b.gen_index_str() );
182     index_path.concat( ".ind" );
183
184     cout << "Looking in " << index_path.str() << endl;
185
186     fg_gzifstream in( index_path.str() );
187
188     if ( in.is_open() ) {
189         string token, name;
190
191         while ( ! in.eof() ) {
192             in >> token;
193             in >> name;
194 #if defined ( macintosh ) || defined ( _MSC_VER )
195             in >> ::skipws;
196 #else
197             in >> skipws;
198 #endif
199             cout << "token = " << token << " name = " << name << endl;
200
201             FGPath custom_path = tile_path;
202             custom_path.append( name );
203             ssgBranch *custom_obj = fgObjLoad( custom_path.str(), e, false );
204             if ( (new_tile != NULL) && (custom_obj != NULL) ) {
205                 new_tile -> addKid( custom_obj );
206             }
207         }
208     }
209
210     e->transform_ptr->addKid( e->range_ptr );
211
212     // calculate initial tile offset
213     e->SetOffset( scenery.center );
214     sgCoord sgcoord;
215     sgSetCoord( &sgcoord,
216                 e->offset.x(), e->offset.y(), e->offset.z(),
217                 0.0, 0.0, 0.0 );
218     e->transform_ptr->setTransform( &sgcoord );
219
220     e->select_ptr->addKid( e->transform_ptr );
221     terrain->addKid( e->select_ptr );
222
223     e->select_ptr->select(1);
224 }
225
226
227 // Ensure at least one entry is free in the cache
228 void FGNewCache::make_space() {
229     FG_LOG( FG_TERRAIN, FG_INFO, "Make space in cache" );
230
231     
232     cout << "cache size = " << tile_cache.size() << endl;
233     cout << "max size = " << max_cache_size << endl;
234
235     if ( (int)tile_cache.size() < max_cache_size ) {
236         // space in the cache, return
237         return;
238     }
239
240     while ( (int)tile_cache.size() >= max_cache_size ) {
241         sgdVec3 abs_view_pos;
242         float dist;
243         float max_dist = 0.0;
244         int max_index = -1;
245
246         // we need to free the furthest entry
247         tile_map_iterator current = tile_cache.begin();
248         tile_map_iterator end = tile_cache.end();
249     
250         for ( ; current != end; ++current ) {
251             long index = current->first;
252             FGTileEntry *e = current->second;
253
254             // calculate approximate distance from view point
255             sgdCopyVec3( abs_view_pos,
256                          globals->get_current_view()->get_abs_view_pos() );
257
258             FG_LOG( FG_TERRAIN, FG_DEBUG, "DIST Abs view pos = " 
259                     << abs_view_pos[0] << ","
260                     << abs_view_pos[1] << ","
261                     << abs_view_pos[2] );
262             FG_LOG( FG_TERRAIN, FG_DEBUG,
263                     "    ref point = " << e->center );
264
265             sgdVec3 center;
266             sgdSetVec3( center, e->center.x(), e->center.y(), e->center.z() );
267             dist = sgdDistanceVec3( center, abs_view_pos );
268
269             FG_LOG( FG_TERRAIN, FG_DEBUG, "    distance = " << dist );
270
271             if ( dist > max_dist ) {
272                 max_dist = dist;
273                 max_index = index;
274             }
275         }
276
277         // If we made it this far, then there were no open cache entries.
278         // We will instead free the furthest cache entry and return it's
279         // index.
280
281         if ( max_index >= 0 ) {
282             FG_LOG( FG_TERRAIN, FG_DEBUG, "    max_dist = " << max_dist );
283             FG_LOG( FG_TERRAIN, FG_DEBUG, "    index = " << max_index );
284             entry_free( max_index );
285         } else {
286             FG_LOG( FG_TERRAIN, FG_ALERT, "WHOOPS!!! Dying in next_avail()" );
287             exit( -1 );
288         }
289     }
290 }