]> git.mxchange.org Git - flightgear.git/blob - src/Scenery/tileentry.cxx
Modified FlightGear/src/Scenery. The tile loader thread no longer adds
[flightgear.git] / src / Scenery / tileentry.cxx
1 // tile.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998, 1999  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 #include <simgear/compiler.h>
25
26 #ifdef SG_MATH_EXCEPTION_CLASH
27 #  include <math.h>
28 #endif
29
30 #include STL_FUNCTIONAL
31 #include STL_ALGORITHM
32 #include STL_STRING
33
34 #include <simgear/bucket/newbucket.hxx>
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/math/sg_random.h>
37 #include <simgear/misc/sgstream.hxx>
38
39 #include <Aircraft/aircraft.hxx>
40 #include <Include/general.hxx>
41 #include <Main/globals.hxx>
42 #include <Scenery/scenery.hxx>
43 #include <Time/light.hxx>
44 #include <Objects/matlib.hxx>
45 #include <Objects/newmat.hxx>
46 #include <Objects/obj.hxx>
47
48 #include "tileentry.hxx"
49
50 SG_USING_STD(for_each);
51 SG_USING_STD(mem_fun_ref);
52 SG_USING_STD(string);
53
54
55 // Constructor
56 FGTileEntry::FGTileEntry ( const SGBucket& b )
57     : ncount( 0 ),
58       center( Point3D( 0.0 ) ),
59       tile_bucket( b ),
60       terra_transform( new ssgTransform ),
61       terra_range( new ssgRangeSelector ),
62       loaded(false)
63 {
64     nodes.clear();
65
66     // update the contents
67     // if ( vec3_ptrs.size() || vec2_ptrs.size() || index_ptrs.size() ) {
68     //     SG_LOG( SG_TERRAIN, SG_ALERT, 
69     //             "Attempting to overwrite existing or"
70     //             << " not properly freed leaf data." );
71     //     exit(-1);
72     // }
73 }
74
75
76 // Destructor
77 FGTileEntry::~FGTileEntry () {
78     // cout << "nodes = " << nodes.size() << endl;;
79     // delete[] nodes;
80 }
81
82
83 // recurse an ssg tree and call removeKid() on every node from the
84 // bottom up.  Leaves the original branch in existance, but empty so
85 // it can be removed by the calling routine.
86 static void my_remove_branch( ssgBranch * branch ) {
87     for ( ssgEntity *k = branch->getKid( 0 );
88           k != NULL; 
89           k = branch->getNextKid() )
90     {
91         if ( k -> isAKindOf ( ssgTypeBranch() ) ) {
92             my_remove_branch( (ssgBranch *)k );
93             branch -> removeKid ( k );
94         } else if ( k -> isAKindOf ( ssgTypeLeaf() ) ) {
95             branch -> removeKid ( k ) ;
96         }
97     }
98 }
99
100
101 // Clean up the memory used by this tile and delete the arrays used by
102 // ssg as well as the whole ssg branch
103 void FGTileEntry::free_tile() {
104     int i;
105     SG_LOG( SG_TERRAIN, SG_INFO,
106             "FREEING TILE = (" << tile_bucket << ")" );
107
108     SG_LOG( SG_TERRAIN, SG_DEBUG,
109             "  deleting " << nodes.size() << " nodes" );
110     nodes.clear();
111
112     // delete the ssg structures
113     SG_LOG( SG_TERRAIN, SG_DEBUG,
114             "  deleting (leaf data) vertex, normal, and "
115             << " texture coordinate arrays" );
116
117     for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
118         delete [] vec3_ptrs[i];
119     }
120     vec3_ptrs.clear();
121
122     for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
123         delete [] vec2_ptrs[i];
124     }
125     vec2_ptrs.clear();
126
127     for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
128         delete index_ptrs[i];
129     }
130     index_ptrs.clear();
131
132     // delete the terrain branch
133     int pcount = terra_transform->getNumParents();
134     if ( pcount > 0 ) {
135         // find the first parent (should only be one)
136         ssgBranch *parent = terra_transform->getParent( 0 ) ;
137         if( parent ) {
138             // my_remove_branch( select_ptr );
139             parent->removeKid( terra_transform );
140             terra_transform = NULL;
141         } else {
142             SG_LOG( SG_TERRAIN, SG_ALERT,
143                     "parent pointer is NULL!  Dying" );
144             exit(-1);
145         }
146     } else {
147         SG_LOG( SG_TERRAIN, SG_ALERT,
148                 "Parent count is zero for an ssg tile!  Dying" );
149         exit(-1);
150     }
151
152     if ( lights_transform ) {
153         // delete the terrain lighting branch
154         pcount = lights_transform->getNumParents();
155         if ( pcount > 0 ) {
156             // find the first parent (should only be one)
157             ssgBranch *parent = lights_transform->getParent( 0 ) ;
158             if( parent ) {
159                 parent->removeKid( lights_transform );
160                 lights_transform = NULL;
161             } else {
162                 SG_LOG( SG_TERRAIN, SG_ALERT,
163                         "parent pointer is NULL!  Dying" );
164                 exit(-1);
165             }
166         } else {
167             SG_LOG( SG_TERRAIN, SG_ALERT,
168                     "Parent count is zero for an ssg light tile!  Dying" );
169             exit(-1);
170         }
171     }
172 }
173
174
175 // Update the ssg transform node for this tile so it can be
176 // properly drawn relative to our (0,0,0) point
177 void FGTileEntry::prep_ssg_node( const Point3D& p, float vis) {
178     if ( !loaded ) return;
179
180     SetOffset( p );
181
182 // #define USE_UP_AND_COMING_PLIB_FEATURE
183 #ifdef USE_UP_AND_COMING_PLIB_FEATURE
184     terra_range->setRange( 0, SG_ZERO );
185     terra_range->setRange( 1, vis + bounding_radius );
186     lights_range->setRange( 0, SG_ZERO );
187     lights_range->setRange( 1, vis * 1.5 + bounding_radius );
188 #else
189     float ranges[2];
190     ranges[0] = SG_ZERO;
191     ranges[1] = vis + bounding_radius;
192     terra_range->setRanges( ranges, 2 );
193     if ( lights_range ) {
194         ranges[1] = vis * 1.5 + bounding_radius;
195         lights_range->setRanges( ranges, 2 );
196     }
197 #endif
198     sgVec3 sgTrans;
199     sgSetVec3( sgTrans, offset.x(), offset.y(), offset.z() );
200     terra_transform->setTransform( sgTrans );
201
202     if ( lights_transform ) {
203         // we need to lift the lights above the terrain to avoid
204         // z-buffer fighting.  We do this based on our altitude and
205         // the distance this tile is away from scenery center.
206
207         sgVec3 up;
208         sgCopyVec3( up, globals->get_current_view()->get_world_up() );
209
210         double agl;
211         if ( current_aircraft.fdm_state ) {
212             agl = current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER
213                 - scenery.cur_elev;
214         } else {
215             agl = 0.0;
216         }
217
218         // sgTrans just happens to be the
219         // vector from scenery center to the center of this tile which
220         // is what we want to calculate the distance of
221         sgVec3 to;
222         sgCopyVec3( to, sgTrans );
223         double dist = sgLengthVec3( to );
224
225         if ( general.get_glDepthBits() > 16 ) {
226             sgScaleVec3( up, 10.0 + agl / 100.0 + dist / 10000 );
227         } else {
228             sgScaleVec3( up, 10.0 + agl / 20.0 + dist / 5000 );
229         }
230         sgAddVec3( sgTrans, up );
231         lights_transform->setTransform( sgTrans );
232
233         // select which set of lights based on sun angle
234         float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
235         if ( sun_angle > 95 ) {
236             lights_brightness->select(0x04);
237         } else if ( sun_angle > 92 ) {
238             lights_brightness->select(0x02);
239         } else if ( sun_angle > 89 ) {
240             lights_brightness->select(0x01);
241         } else {
242             lights_brightness->select(0x00);
243         }
244     }
245 }
246
247
248 ssgLeaf* FGTileEntry::gen_lights( ssgVertexArray *lights, int inc, float bright ) {
249     // generate a repeatable random seed
250     float *p1 = lights->get( 0 );
251     unsigned int *seed = (unsigned int *)p1;
252     sg_srandom( *seed );
253
254     int size = lights->getNum() / inc;
255
256     // Allocate ssg structure
257     ssgVertexArray   *vl = new ssgVertexArray( size + 1 );
258     ssgNormalArray   *nl = NULL;
259     ssgTexCoordArray *tl = NULL;
260     ssgColourArray   *cl = new ssgColourArray( size + 1 );
261
262     sgVec4 color;
263     for ( int i = 0; i < lights->getNum(); ++i ) {
264         // this loop is slightly less efficient than it otherwise
265         // could be, but we want a red light to always be red, and a
266         // yellow light to always be yellow, etc. so we are trying to
267         // preserve the random sequence.
268         float zombie = sg_random();
269         if ( i % inc == 0 ) {
270             vl->add( lights->get(i) );
271
272             // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
273             float factor = sg_random();
274             factor *= factor;
275
276             if ( zombie > 0.5 ) {
277                 // 50% chance of yellowish
278                 sgSetVec4( color, 0.9, 0.9, 0.3, bright - factor * 0.2 );
279             } else if ( zombie > 0.15 ) {
280                 // 35% chance of whitish
281                 sgSetVec4( color, 0.9, 0.9, 0.8, bright - factor * 0.2 );
282             } else if ( zombie > 0.05 ) {
283                 // 10% chance of orangish
284                 sgSetVec4( color, 0.9, 0.6, 0.2, bright - factor * 0.2 );
285             } else {
286                 // 5% chance of redish
287                 sgSetVec4( color, 0.9, 0.2, 0.2, bright - factor * 0.2 );
288             }
289             cl->add( color );
290         }
291     }
292
293     // create ssg leaf
294     ssgLeaf *leaf = 
295         new ssgVtxTable ( GL_POINTS, vl, nl, tl, cl );
296
297     // assign state
298     FGNewMat *newmat = material_lib.find( "LIGHTS" );
299     leaf->setState( newmat->get_state() );
300
301     return leaf;
302 }
303
304
305 ssgBranch*
306 FGTileEntry::obj_load( const std::string& path,
307                        ssgVertexArray* lights, bool is_base )
308 {
309     ssgBranch* result = 0;
310
311     // try loading binary format
312     result = fgBinObjLoad( path, this, lights, is_base );
313     if ( result == NULL ) {
314         // next try the older ascii format
315         result = fgAsciiObjLoad( path, this, lights, is_base );
316         if ( result == NULL ) {
317             // default to an ocean tile
318             result = fgGenTile( path, this );
319         }
320     }
321
322     return result;
323 }
324
325
326 void
327 FGTileEntry::load( const SGPath& base, bool is_base )
328 {
329     string index_str = tile_bucket.gen_index_str();
330
331     SGPath tile_path = base;
332     // Generate name of file to load.
333     tile_path.append( tile_bucket.gen_base_path() );
334     SGPath basename = tile_path;
335     basename.append( index_str );
336     string path = basename.str();
337
338     SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << path );
339
340     // fgObjLoad will generate ground lighting for us ...
341     ssgVertexArray *light_pts = new ssgVertexArray( 100 );
342
343     ssgBranch* new_tile = obj_load( path, light_pts, is_base );
344     if ( new_tile != NULL ) {
345         terra_range->addKid( new_tile );
346     }
347
348     // load custom objects
349     SG_LOG( SG_TERRAIN, SG_DEBUG, "CUSTOM OBJECTS" );
350
351     SGPath index_path = tile_path;
352     index_path.append( index_str );
353     index_path.concat( ".ind" );
354
355     SG_LOG( SG_TERRAIN, SG_DEBUG, "Looking in " << index_path.str() );
356
357     sg_gzifstream in( index_path.str() );
358
359     if ( in.is_open() ) {
360         string token, name;
361
362         while ( ! in.eof() ) {
363             in >> token >> name >> ::skipws;
364             SG_LOG( SG_TERRAIN, SG_DEBUG, "token = " << token
365                     << " name = " << name );
366
367             SGPath custom_path = tile_path;
368             custom_path.append( name );
369             ssgBranch *custom_obj = obj_load( custom_path.str(), NULL, false );
370             if ( (new_tile != NULL) && (custom_obj != NULL) ) {
371                 new_tile -> addKid( custom_obj );
372             }
373         }
374     }
375
376     terra_transform->addKid( terra_range );
377
378     // calculate initial tile offset
379     SetOffset( scenery.center );
380     sgCoord sgcoord;
381     sgSetCoord( &sgcoord,
382                 offset.x(), offset.y(), offset.z(),
383                 0.0, 0.0, 0.0 );
384     terra_transform->setTransform( &sgcoord );
385     // terrain->addKid( terra_transform );
386
387     lights_transform = NULL;
388     lights_range = NULL;
389     /* uncomment this section for testing ground lights */
390     if ( light_pts->getNum() ) {
391         SG_LOG( SG_TERRAIN, SG_DEBUG, "generating lights" );
392         lights_transform = new ssgTransform;
393         lights_range = new ssgRangeSelector;
394         lights_brightness = new ssgSelector;
395         ssgLeaf *lights;
396
397         lights = gen_lights( light_pts, 4, 0.7 );
398         lights_brightness->addKid( lights );
399
400         lights = gen_lights( light_pts, 2, 0.85 );
401         lights_brightness->addKid( lights );
402
403         lights = gen_lights( light_pts, 1, 1.0 );
404         lights_brightness->addKid( lights );
405
406         lights_range->addKid( lights_brightness );
407         lights_transform->addKid( lights_range );
408         lights_transform->setTransform( &sgcoord );
409         // ground->addKid( lights_transform );
410     }
411     /* end of ground light section */
412 }
413
414 void
415 FGTileEntry::add_ssg_nodes( ssgBranch* terrain, ssgBranch* ground )
416 {
417     terrain->addKid( terra_transform );
418     if (lights_transform != 0)
419         ground->addKid( lights_transform );
420
421     loaded = true;
422 }