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