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