]> git.mxchange.org Git - flightgear.git/blob - src/Objects/obj.cxx
put a separate LOD node in front of ever random object so we can randomize
[flightgear.git] / src / Objects / obj.cxx
1 // obj.cxx -- routines to handle "sorta" WaveFront .obj format files.
2 //
3 // Written by Curtis Olson, started October 1997.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
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 SG_MATH_EXCEPTION_CLASH
29 #  include <math.h>
30 #endif
31
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <simgear/compiler.h>
36 #include <simgear/io/sg_binobj.hxx>
37
38 #include STL_STRING
39 #include <map>                  // STL
40 #include <vector>               // STL
41 #include <ctype.h>              // isdigit()
42
43 #include <simgear/constants.h>
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/math/point3d.hxx>
46 #include <simgear/math/polar3d.hxx>
47 #include <simgear/math/sg_geodesy.hxx>
48 #include <simgear/math/sg_random.h>
49 #include <simgear/misc/sgstream.hxx>
50 #include <simgear/misc/stopwatch.hxx>
51 #include <simgear/misc/texcoord.hxx>
52
53 #include <Main/globals.hxx>
54 #include <Main/fg_props.hxx>
55 #include <Time/light.hxx>
56 #include <Scenery/tileentry.hxx>
57
58 #include "newmat.hxx"
59 #include "matlib.hxx"
60 #include "obj.hxx"
61
62 SG_USING_STD(string);
63 SG_USING_STD(vector);
64
65
66 typedef vector < int > int_list;
67 typedef int_list::iterator int_list_iterator;
68 typedef int_list::const_iterator int_point_list_iterator;
69
70
71 static double normals[FG_MAX_NODES][3];
72 static double tex_coords[FG_MAX_NODES*3][3];
73
74 static int
75 runway_lights_predraw (ssgEntity * e)
76 {
77                                 // Turn on lights only at night
78     float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
79     return int(sun_angle > 90.0);
80 }
81
82
83 #define FG_TEX_CONSTANT 69.0
84
85 // Calculate texture coordinates for a given point.
86 static Point3D local_calc_tex_coords(const Point3D& node, const Point3D& ref) {
87     Point3D cp;
88     Point3D pp;
89     // double tmplon, tmplat;
90
91     // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
92     // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
93
94     cp = Point3D( node[0] + ref.x(),
95                   node[1] + ref.y(),
96                   node[2] + ref.z() );
97
98     pp = sgCartToPolar3d(cp);
99
100     // tmplon = pp.lon() * SGD_RADIANS_TO_DEGREES;
101     // tmplat = pp.lat() * SGD_RADIANS_TO_DEGREES;
102     // cout << tmplon << " " << tmplat << endl;
103
104     pp.setx( fmod(SGD_RADIANS_TO_DEGREES * FG_TEX_CONSTANT * pp.x(), 11.0) );
105     pp.sety( fmod(SGD_RADIANS_TO_DEGREES * FG_TEX_CONSTANT * pp.y(), 11.0) );
106
107     if ( pp.x() < 0.0 ) {
108         pp.setx( pp.x() + 11.0 );
109     }
110
111     if ( pp.y() < 0.0 ) {
112         pp.sety( pp.y() + 11.0 );
113     }
114
115     // cout << pp << endl;
116
117     return(pp);
118 }
119
120
121 // Generate an ocean tile
122 bool fgGenTile( const string& path, SGBucket b,
123                       Point3D *center,
124                       double *bounding_radius,
125                       ssgBranch* geometry )
126 {
127     FGNewMat *newmat;
128
129     ssgSimpleState *state = NULL;
130
131     geometry -> setName ( (char *)path.c_str() ) ;
132
133     double tex_width = 1000.0;
134     // double tex_height;
135
136     // find Ocean material in the properties list
137     newmat = material_lib.find( "Ocean" );
138     if ( newmat != NULL ) {
139         // set the texture width and height values for this
140         // material
141         tex_width = newmat->get_xsize();
142         // tex_height = newmat->get_ysize();
143         
144         // set ssgState
145         state = newmat->get_state();
146     } else {
147         SG_LOG( SG_TERRAIN, SG_ALERT, 
148                 "Ack! unknown usemtl name = " << "Ocean" 
149                 << " in " << path );
150     }
151
152     // Calculate center point
153     double clon = b.get_center_lon();
154     double clat = b.get_center_lat();
155     double height = b.get_height();
156     double width = b.get_width();
157
158     *center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
159                                     clat*SGD_DEGREES_TO_RADIANS,
160                                     0.0) );
161     // cout << "center = " << center << endl;;
162     
163     // Caculate corner vertices
164     Point3D geod[4];
165     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
166     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
167     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
168     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
169
170     Point3D rad[4];
171     int i;
172     for ( i = 0; i < 4; ++i ) {
173         rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
174                           geod[i].y() * SGD_DEGREES_TO_RADIANS,
175                           geod[i].z() );
176     }
177
178     Point3D cart[4], rel[4];
179     for ( i = 0; i < 4; ++i ) {
180         cart[i] = sgGeodToCart(rad[i]);
181         rel[i] = cart[i] - *center;
182         // cout << "corner " << i << " = " << cart[i] << endl;
183     }
184
185     // Calculate bounding radius
186     *bounding_radius = center->distance3D( cart[0] );
187     // cout << "bounding radius = " << t->bounding_radius << endl;
188
189     // Calculate normals
190     Point3D normals[4];
191     for ( i = 0; i < 4; ++i ) {
192         double length = cart[i].distance3D( Point3D(0.0) );
193         normals[i] = cart[i] / length;
194         // cout << "normal = " << normals[i] << endl;
195     }
196
197     // Calculate texture coordinates
198     point_list geod_nodes;
199     geod_nodes.clear();
200     int_list rectangle;
201     rectangle.clear();
202     for ( i = 0; i < 4; ++i ) {
203         geod_nodes.push_back( geod[i] );
204         rectangle.push_back( i );
205     }
206     point_list texs = calc_tex_coords( b, geod_nodes, rectangle, 
207                                        1000.0 / tex_width );
208
209     // Allocate ssg structure
210     ssgVertexArray   *vl = new ssgVertexArray( 4 );
211     ssgNormalArray   *nl = new ssgNormalArray( 4 );
212     ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
213     ssgColourArray   *cl = new ssgColourArray( 1 );
214
215     sgVec4 color;
216     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
217     cl->add( color );
218
219     // sgVec3 *vtlist = new sgVec3 [ 4 ];
220     // t->vec3_ptrs.push_back( vtlist );
221     // sgVec3 *vnlist = new sgVec3 [ 4 ];
222     // t->vec3_ptrs.push_back( vnlist );
223     // sgVec2 *tclist = new sgVec2 [ 4 ];
224     // t->vec2_ptrs.push_back( tclist );
225
226     sgVec2 tmp2;
227     sgVec3 tmp3;
228     for ( i = 0; i < 4; ++i ) {
229         sgSetVec3( tmp3, 
230                    rel[i].x(), rel[i].y(), rel[i].z() );
231         vl->add( tmp3 );
232
233         sgSetVec3( tmp3, 
234                    normals[i].x(), normals[i].y(), normals[i].z() );
235         nl->add( tmp3 );
236
237         sgSetVec2( tmp2, texs[i].x(), texs[i].y());
238         tl->add( tmp2 );
239     }
240     
241     ssgLeaf *leaf = 
242         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
243
244     leaf->setState( state );
245
246     geometry->addKid( leaf );
247
248     return true;
249 }
250
251
252 static void random_pt_inside_tri( float *res,
253                                   float *n1, float *n2, float *n3 )
254 {
255     sgVec3 p1, p2, p3;
256
257     double a = sg_random();
258     double b = sg_random();
259     if ( a + b > 1.0 ) {
260         a = 1.0 - a;
261         b = 1.0 - b;
262     }
263     double c = 1 - a - b;
264
265     sgScaleVec3( p1, n1, a );
266     sgScaleVec3( p2, n2, b );
267     sgScaleVec3( p3, n3, c );
268
269     sgAddVec3( res, p1, p2 );
270     sgAddVec3( res, p3 );
271 }
272
273
274 static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
275                                        double factor ) {
276     int num = leaf->getNumTriangles();
277     if ( num > 0 ) {
278         short int n1, n2, n3;
279         float *p1, *p2, *p3;
280         sgVec3 result;
281
282         // generate a repeatable random seed
283         p1 = leaf->getVertex( 0 );
284         unsigned int seed = (unsigned int)p1[0];
285         sg_srandom( seed );
286
287         for ( int i = 0; i < num; ++i ) {
288             leaf->getTriangle( i, &n1, &n2, &n3 );
289             p1 = leaf->getVertex(n1);
290             p2 = leaf->getVertex(n2);
291             p3 = leaf->getVertex(n3);
292             double area = sgTriArea( p1, p2, p3 );
293             double num = area / factor;
294
295             // generate a light point for each unit of area
296             while ( num > 1.0 ) {
297                 random_pt_inside_tri( result, p1, p2, p3 );
298                 lights->add( result );
299                 num -= 1.0;
300             }
301             // for partial units of area, use a zombie door method to
302             // create the proper random chance of a light being created
303             // for this triangle
304             if ( num > 0.0 ) {
305                 if ( sg_random() <= num ) {
306                     // a zombie made it through our door
307                     random_pt_inside_tri( result, p1, p2, p3 );
308                     lights->add( result );
309                 }
310             }
311         }
312     }
313 }
314
315
316 static void
317 gen_random_surface_objects (ssgLeaf *leaf,
318                             ssgBranch *branch,
319                             float lon_deg,
320                             float lat_deg,
321                             const string &material_name)
322 {
323     FGNewMat * mat = material_lib.find(material_name);
324     if (mat == 0) {
325       SG_LOG(SG_INPUT, SG_ALERT, "Unknown material " << material_name);
326       return;
327     }
328
329     int num = leaf->getNumTriangles();
330     float hdg_deg = 0.0;        // do something here later
331
332
333     // The object will be aligned for the north pole.  This code
334     // calculates a matrix to rotate it to for the surface of the
335     // earth in the current location.
336     sgVec3 obj_right, obj_up;
337     sgSetVec3(obj_right, 0.0, 1.0, 0.0); // Y axis
338     sgSetVec3(obj_up, 0.0, 0.0, 1.0); // Z axis
339     sgMat4 ROT_lon, ROT_lat, ROT_hdg;
340     sgMakeRotMat4(ROT_lon, lon_deg, obj_up);
341     sgMakeRotMat4(ROT_lat, 90 - lat_deg, obj_right);
342     sgMakeRotMat4(ROT_hdg, hdg_deg, obj_up);
343     sgMat4 ROT;
344     sgCopyMat4(ROT, ROT_hdg);
345     sgPostMultMat4(ROT, ROT_lat);
346     sgPostMultMat4(ROT, ROT_lon);
347
348     if ( num > 0 ) {
349         short int n1, n2, n3;
350         float *p1, *p2, *p3;
351         sgVec3 result;
352
353         // generate a repeatable random seed
354         p1 = leaf->getVertex( 0 );
355         unsigned int seed = (unsigned int)p1[0];
356         sg_srandom( seed );
357
358         int num_objects = mat->get_object_count();
359         for ( int i = 0; i < num; ++i ) {
360             leaf->getTriangle( i, &n1, &n2, &n3 );
361             p1 = leaf->getVertex(n1);
362             p2 = leaf->getVertex(n2);
363             p3 = leaf->getVertex(n3);
364             double area = sgTriArea( p1, p2, p3 );
365                                 // Set up a single center point for LOD
366             sgVec3 center;
367             sgSetVec3(center,
368                       (p1[0] + p2[0] + p3[0]) / 3.0,
369                       (p1[1] + p2[1] + p3[1]) / 3.0,
370                       (p1[2] + p2[2] + p3[2]) / 3.0);
371             ssgTransform * location = new ssgTransform;
372             sgMat4 TRANS;
373             sgMakeTransMat4(TRANS, center);
374             location->setTransform(TRANS);
375
376             for (int j = 0; j < num_objects; j++) {
377               double num = area / mat->get_object_coverage(j);
378               float ranges[] = {0, mat->get_object_group_lod(j)};
379               ssgRangeSelector * lod = new ssgRangeSelector;
380               lod->setRanges(ranges, 2);
381               lod->addKid(location);
382               branch->addKid(lod);
383               
384               // place an object each unit of area
385               while ( num > 1.0 ) {
386                 random_pt_inside_tri( result, p1, p2, p3 );
387                 sgSubVec3(result, center);
388                 sgMat4 OBJ_pos, OBJ;
389                 sgMakeTransMat4(OBJ_pos, result);
390                 sgCopyMat4(OBJ, ROT);
391                 sgPostMultMat4(OBJ, OBJ_pos);
392                 ssgTransform * pos = new ssgTransform;
393                 pos->setTransform(OBJ);
394                 float obj_range = mat->get_object_lod(j);
395                 float range_div = (sg_random() * obj_range);
396                 if (range_div < 0.0000001) {
397                     // avoid a divide by zero error
398                     range_div = 1.0;
399                 }
400                 float random_range = 160.0 * obj_range / range_div + obj_range;
401                 float ranges[] = {0, random_range};
402                 ssgRangeSelector *range = new ssgRangeSelector;
403                 range->setRanges(ranges, 2);
404                 range->addKid(mat->get_object(j));
405                 pos->addKid(range);
406                 location->addKid(pos);
407                 num -= 1.0;
408               }
409               // for partial units of area, use a zombie door method to
410               // create the proper random chance of an object being created
411               // for this triangle
412               if ( num > 0.0 ) {
413                 if ( sg_random() <= num ) {
414                   // a zombie made it through our door
415                   random_pt_inside_tri( result, p1, p2, p3 );
416                   sgSubVec3(result, center);
417                   sgMat4 OBJ_pos, OBJ;
418                   sgMakeTransMat4(OBJ_pos, result);
419                   sgCopyMat4(OBJ, ROT);
420                   sgPostMultMat4(OBJ, OBJ_pos);
421                   ssgTransform * pos = new ssgTransform;
422                   pos->setTransform(OBJ);
423                   float obj_range = mat->get_object_lod(j);
424                   float range_div = (sg_random() * obj_range);
425                   if (range_div < 0.0000001) {
426                       // avoid a divide by zero error
427                       range_div = 1.0;
428                   }
429                   float random_range = 160.0 * obj_range / range_div + obj_range;
430                   float ranges[] = {0, random_range};
431                   ssgRangeSelector *range = new ssgRangeSelector;
432                   range->setRanges(ranges, 2);
433                   range->addKid(mat->get_object(j));
434                   pos->addKid(range);
435                   location->addKid(pos);
436                 }
437               }
438             }
439         }
440     }
441 }
442
443
444 // Load an Ascii obj file
445 ssgBranch *fgAsciiObjLoad( const string& path, FGTileEntry *t,
446                            ssgVertexArray *lights, const bool is_base)
447 {
448     FGNewMat *newmat = NULL;
449     string material;
450     float coverage = -1;
451     Point3D pp;
452     // sgVec3 approx_normal;
453     // double normal[3], scale = 0.0;
454     // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
455     // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
456     // GLint display_list = 0;
457     int shading;
458     bool in_faces = false;
459     int vncount, vtcount;
460     int n1 = 0, n2 = 0, n3 = 0;
461     int tex;
462     // int last1 = 0, last2 = 0;
463     bool odd = false;
464     point_list nodes;
465     Point3D node;
466     Point3D center;
467     double scenery_version = 0.0;
468     double tex_width = 1000.0, tex_height = 1000.0;
469     bool shared_done = false;
470     int_list fan_vertices;
471     int_list fan_tex_coords;
472     int i;
473     ssgSimpleState *state = NULL;
474     sgVec3 *vtlist, *vnlist;
475     sgVec2 *tclist;
476
477     ssgBranch *tile = new ssgBranch () ;
478
479     tile -> setName ( (char *)path.c_str() ) ;
480
481     // Attempt to open "path.gz" or "path"
482     sg_gzifstream in( path );
483     if ( ! in.is_open() ) {
484         SG_LOG( SG_TERRAIN, SG_DEBUG, "Cannot open file: " << path );
485         SG_LOG( SG_TERRAIN, SG_DEBUG, "default to ocean tile: " << path );
486
487         delete tile;
488
489         return NULL;
490     }
491
492     shading = fgGetBool("/sim/rendering/shading");
493
494     if ( is_base ) {
495         t->ncount = 0;
496     }
497     vncount = 0;
498     vtcount = 0;
499     if ( is_base ) {
500         t->bounding_radius = 0.0;
501     }
502     center = t->center;
503
504     // StopWatch stopwatch;
505     // stopwatch.start();
506
507     // ignore initial comments and blank lines. (priming the pump)
508     // in >> skipcomment;
509     // string line;
510
511     string token;
512     char c;
513
514 #ifdef __MWERKS__
515     while ( in.get(c) && c  != '\0' ) {
516         in.putback(c);
517 #else
518     while ( ! in.eof() ) {
519 #endif
520
521         in >> ::skipws;
522
523         if ( in.get( c ) && c == '#' ) {
524             // process a comment line
525
526             // getline( in, line );
527             // cout << "comment = " << line << endl;
528
529             in >> token;
530
531             if ( token == "Version" ) {
532                 // read scenery versions number
533                 in >> scenery_version;
534                 // cout << "scenery_version = " << scenery_version << endl;
535                 if ( scenery_version > 0.4 ) {
536                     SG_LOG( SG_TERRAIN, SG_ALERT, 
537                             "\nYou are attempting to load a tile format that\n"
538                             << "is newer than this version of flightgear can\n"
539                             << "handle.  You should upgrade your copy of\n"
540                             << "FlightGear to the newest version.  For\n"
541                             << "details, please see:\n"
542                             << "\n    http://www.flightgear.org\n" );
543                     exit(-1);
544                 }
545             } else if ( token == "gbs" ) {
546                 // reference point (center offset)
547                 if ( is_base ) {
548                     in >> t->center >> t->bounding_radius;
549                 } else {
550                     Point3D junk1;
551                     double junk2;
552                     in >> junk1 >> junk2;
553                 }
554                 center = t->center;
555                 // cout << "center = " << center 
556                 //      << " radius = " << t->bounding_radius << endl;
557             } else if ( token == "bs" ) {
558                 // reference point (center offset)
559                 // (skip past this)
560                 Point3D junk1;
561                 double junk2;
562                 in >> junk1 >> junk2;
563             } else if ( token == "usemtl" ) {
564                 // material property specification
565
566                 // if first usemtl with shared_done = false, then set
567                 // shared_done true and build the ssg shared lists
568                 if ( ! shared_done ) {
569                     // sanity check
570                     if ( (int)nodes.size() != vncount ) {
571                         SG_LOG( SG_TERRAIN, SG_ALERT, 
572                                 "Tile has mismatched nodes = " << nodes.size()
573                                 << " and normals = " << vncount << " : " 
574                                 << path );
575                         // exit(-1);
576                     }
577                     shared_done = true;
578
579                     vtlist = new sgVec3 [ nodes.size() ];
580                     t->vec3_ptrs.push_back( vtlist );
581                     vnlist = new sgVec3 [ vncount ];
582                     t->vec3_ptrs.push_back( vnlist );
583                     tclist = new sgVec2 [ vtcount ];
584                     t->vec2_ptrs.push_back( tclist );
585
586                     for ( i = 0; i < (int)nodes.size(); ++i ) {
587                         sgSetVec3( vtlist[i], 
588                                    nodes[i][0], nodes[i][1], nodes[i][2] );
589                     }
590                     for ( i = 0; i < vncount; ++i ) {
591                         sgSetVec3( vnlist[i], 
592                                    normals[i][0], 
593                                    normals[i][1],
594                                    normals[i][2] );
595                     }
596                     for ( i = 0; i < vtcount; ++i ) {
597                         sgSetVec2( tclist[i],
598                                    tex_coords[i][0],
599                                    tex_coords[i][1] );
600                     }
601                 }
602
603                 // display_list = xglGenLists(1);
604                 // xglNewList(display_list, GL_COMPILE);
605                 // printf("xglGenLists(); xglNewList();\n");
606                 in_faces = false;
607
608                 // scan the material line
609                 in >> material;
610                 
611                 // find this material in the properties list
612
613                 newmat = material_lib.find( material );
614                 if ( newmat == NULL ) {
615                     // see if this is an on the fly texture
616                     string file = path;
617                     int pos = file.rfind( "/" );
618                     file = file.substr( 0, pos );
619                     // cout << "current file = " << file << endl;
620                     file += "/";
621                     file += material;
622                     // cout << "current file = " << file << endl;
623                     if ( ! material_lib.add_item( file ) ) {
624                         SG_LOG( SG_TERRAIN, SG_ALERT, 
625                                 "Ack! unknown usemtl name = " << material 
626                                 << " in " << path );
627                     } else {
628                         // locate our newly created material
629                         newmat = material_lib.find( material );
630                         if ( newmat == NULL ) {
631                             SG_LOG( SG_TERRAIN, SG_ALERT, 
632                                     "Ack! bad on the fly materia create = "
633                                     << material << " in " << path );
634                         }
635                     }
636                 }
637
638                 if ( newmat != NULL ) {
639                     // set the texture width and height values for this
640                     // material
641                     tex_width = newmat->get_xsize();
642                     tex_height = newmat->get_ysize();
643                     state = newmat->get_state();
644                     coverage = newmat->get_light_coverage();
645                     // cout << "(w) = " << tex_width << " (h) = "
646                     //      << tex_width << endl;
647                 } else {
648                     coverage = -1;
649                 }
650             } else {
651                 // unknown comment, just gobble the input until the
652                 // end of line
653
654                 in >> skipeol;
655             }
656         } else {
657             in.putback( c );
658         
659             in >> token;
660
661             // cout << "token = " << token << endl;
662
663             if ( token == "vn" ) {
664                 // vertex normal
665                 if ( vncount < FG_MAX_NODES ) {
666                     in >> normals[vncount][0]
667                        >> normals[vncount][1]
668                        >> normals[vncount][2];
669                     vncount++;
670                 } else {
671                     SG_LOG( SG_TERRAIN, SG_ALERT, 
672                             "Read too many vertex normals in " << path 
673                             << " ... dying :-(" );
674                     exit(-1);
675                 }
676             } else if ( token == "vt" ) {
677                 // vertex texture coordinate
678                 if ( vtcount < FG_MAX_NODES*3 ) {
679                     in >> tex_coords[vtcount][0]
680                        >> tex_coords[vtcount][1];
681                     vtcount++;
682                 } else {
683                     SG_LOG( SG_TERRAIN, SG_ALERT, 
684                             "Read too many vertex texture coords in " << path
685                             << " ... dying :-("
686                             );
687                     exit(-1);
688                 }
689             } else if ( token == "v" ) {
690                 // node (vertex)
691                 if ( t->ncount < FG_MAX_NODES ) {
692                     /* in >> nodes[t->ncount][0]
693                        >> nodes[t->ncount][1]
694                        >> nodes[t->ncount][2]; */
695                     in >> node;
696                     nodes.push_back(node);
697                     if ( is_base ) {
698                         t->ncount++;
699                     }
700                 } else {
701                     SG_LOG( SG_TERRAIN, SG_ALERT, 
702                             "Read too many nodes in " << path 
703                             << " ... dying :-(");
704                     exit(-1);
705                 }
706             } else if ( (token == "tf") || (token == "ts") || (token == "f") ) {
707                 // triangle fan, strip, or individual face
708                 // SG_LOG( SG_TERRAIN, SG_INFO, "new fan or strip");
709
710                 fan_vertices.clear();
711                 fan_tex_coords.clear();
712                 odd = true;
713
714                 // xglBegin(GL_TRIANGLE_FAN);
715
716                 in >> n1;
717                 fan_vertices.push_back( n1 );
718                 // xglNormal3dv(normals[n1]);
719                 if ( in.get( c ) && c == '/' ) {
720                     in >> tex;
721                     fan_tex_coords.push_back( tex );
722                     if ( scenery_version >= 0.4 ) {
723                         if ( tex_width > 0 ) {
724                             tclist[tex][0] *= (1000.0 / tex_width);
725                         }
726                         if ( tex_height > 0 ) {
727                             tclist[tex][1] *= (1000.0 / tex_height);
728                         }
729                     }
730                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
731                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
732                 } else {
733                     in.putback( c );
734                     pp = local_calc_tex_coords(nodes[n1], center);
735                 }
736                 // xglTexCoord2f(pp.x(), pp.y());
737                 // xglVertex3dv(nodes[n1].get_n());
738
739                 in >> n2;
740                 fan_vertices.push_back( n2 );
741                 // xglNormal3dv(normals[n2]);
742                 if ( in.get( c ) && c == '/' ) {
743                     in >> tex;
744                     fan_tex_coords.push_back( tex );
745                     if ( scenery_version >= 0.4 ) {
746                         if ( tex_width > 0 ) {
747                             tclist[tex][0] *= (1000.0 / tex_width);
748                         }
749                         if ( tex_height > 0 ) {
750                             tclist[tex][1] *= (1000.0 / tex_height);
751                         }
752                     }
753                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
754                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
755                 } else {
756                     in.putback( c );
757                     pp = local_calc_tex_coords(nodes[n2], center);
758                 }
759                 // xglTexCoord2f(pp.x(), pp.y());
760                 // xglVertex3dv(nodes[n2].get_n());
761                 
762                 // read all subsequent numbers until next thing isn't a number
763                 while ( true ) {
764                     in >> ::skipws;
765
766                     char c;
767                     in.get(c);
768                     in.putback(c);
769                     if ( ! isdigit(c) || in.eof() ) {
770                         break;
771                     }
772
773                     in >> n3;
774                     fan_vertices.push_back( n3 );
775                     // cout << "  triangle = "
776                     //      << n1 << "," << n2 << "," << n3
777                     //      << endl;
778                     // xglNormal3dv(normals[n3]);
779                     if ( in.get( c ) && c == '/' ) {
780                         in >> tex;
781                         fan_tex_coords.push_back( tex );
782                         if ( scenery_version >= 0.4 ) {
783                             if ( tex_width > 0 ) {
784                                 tclist[tex][0] *= (1000.0 / tex_width);
785                             }
786                             if ( tex_height > 0 ) {
787                                 tclist[tex][1] *= (1000.0 / tex_height);
788                             }
789                         }
790                         pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
791                         pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
792                     } else {
793                         in.putback( c );
794                         pp = local_calc_tex_coords(nodes[n3], center);
795                     }
796                     // xglTexCoord2f(pp.x(), pp.y());
797                     // xglVertex3dv(nodes[n3].get_n());
798
799                     if ( (token == "tf") || (token == "f") ) {
800                         // triangle fan
801                         n2 = n3;
802                     } else {
803                         // triangle strip
804                         odd = !odd;
805                         n1 = n2;
806                         n2 = n3;
807                     }
808                 }
809
810                 // xglEnd();
811
812                 // build the ssg entity
813                 int size = (int)fan_vertices.size();
814                 ssgVertexArray   *vl = new ssgVertexArray( size );
815                 ssgNormalArray   *nl = new ssgNormalArray( size );
816                 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
817                 ssgColourArray   *cl = new ssgColourArray( 1 );
818
819                 sgVec4 color;
820                 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
821                 cl->add( color );
822
823                 sgVec2 tmp2;
824                 sgVec3 tmp3;
825                 for ( i = 0; i < size; ++i ) {
826                     sgCopyVec3( tmp3, vtlist[ fan_vertices[i] ] );
827                     vl -> add( tmp3 );
828
829                     sgCopyVec3( tmp3, vnlist[ fan_vertices[i] ] );
830                     nl -> add( tmp3 );
831
832                     sgCopyVec2( tmp2, tclist[ fan_tex_coords[i] ] );
833                     tl -> add( tmp2 );
834                 }
835
836                 ssgLeaf *leaf = NULL;
837                 if ( token == "tf" ) {
838                     // triangle fan
839                     leaf = 
840                         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
841                 } else if ( token == "ts" ) {
842                     // triangle strip
843                     leaf = 
844                         new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl );
845                 } else if ( token == "f" ) {
846                     // triangle
847                     leaf = 
848                         new ssgVtxTable ( GL_TRIANGLES, vl, nl, tl, cl );
849                 }
850                 // leaf->makeDList();
851                 leaf->setState( state );
852
853                 tile->addKid( leaf );
854
855                 if ( is_base ) {
856                     if ( coverage > 0.0 ) {
857                         if ( coverage < 10000.0 ) {
858                             SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
859                                    << coverage << ", pushing up to 10000");
860                             coverage = 10000;
861                         }
862                         gen_random_surface_points(leaf, lights, coverage);
863                     }
864                 }
865             } else {
866                 SG_LOG( SG_TERRAIN, SG_WARN, "Unknown token in " 
867                         << path << " = " << token );
868             }
869
870             // eat white space before start of while loop so if we are
871             // done with useful input it is noticed before hand.
872             in >> ::skipws;
873         }
874     }
875
876     if ( is_base ) {
877         t->nodes = nodes;
878     }
879
880     // stopwatch.stop();
881     // SG_LOG( SG_TERRAIN, SG_DEBUG, 
882     //     "Loaded " << path << " in " 
883     //     << stopwatch.elapsedSeconds() << " seconds" );
884
885     return tile;
886 }
887
888
889 ssgLeaf *gen_leaf( const string& path,
890                    const GLenum ty, const string& material,
891                    const point_list& nodes, const point_list& normals,
892                    const point_list& texcoords,
893                    const int_list node_index,
894                    const int_list normal_index,
895                    const int_list& tex_index,
896                    const bool calc_lights, ssgVertexArray *lights )
897 {
898     double tex_width = 1000.0, tex_height = 1000.0;
899     ssgSimpleState *state = NULL;
900     float coverage = -1;
901
902     FGNewMat *newmat = material_lib.find( material );
903     if ( newmat == NULL ) {
904         // see if this is an on the fly texture
905         string file = path;
906         int pos = file.rfind( "/" );
907         file = file.substr( 0, pos );
908         // cout << "current file = " << file << endl;
909         file += "/";
910         file += material;
911         // cout << "current file = " << file << endl;
912         if ( ! material_lib.add_item( file ) ) {
913             SG_LOG( SG_TERRAIN, SG_ALERT, 
914                     "Ack! unknown usemtl name = " << material 
915                     << " in " << path );
916         } else {
917             // locate our newly created material
918             newmat = material_lib.find( material );
919             if ( newmat == NULL ) {
920                 SG_LOG( SG_TERRAIN, SG_ALERT, 
921                         "Ack! bad on the fly material create = "
922                         << material << " in " << path );
923             }
924         }
925     }
926
927     if ( newmat != NULL ) {
928         // set the texture width and height values for this
929         // material
930         tex_width = newmat->get_xsize();
931         tex_height = newmat->get_ysize();
932         state = newmat->get_state();
933         coverage = newmat->get_light_coverage();
934         // cout << "(w) = " << tex_width << " (h) = "
935         //      << tex_width << endl;
936     } else {
937         coverage = -1;
938     }
939
940     sgVec2 tmp2;
941     sgVec3 tmp3;
942     sgVec4 tmp4;
943     int i;
944
945     // vertices
946     int size = node_index.size();
947     if ( size < 1 ) {
948         SG_LOG( SG_TERRAIN, SG_ALERT, "Woh! node list size < 1" );
949         exit(-1);
950     }
951     ssgVertexArray *vl = new ssgVertexArray( size );
952     Point3D node;
953     for ( i = 0; i < size; ++i ) {
954         node = nodes[ node_index[i] ];
955         sgSetVec3( tmp3, node[0], node[1], node[2] );
956         vl -> add( tmp3 );
957     }
958
959     // normals
960     Point3D normal;
961     ssgNormalArray *nl = new ssgNormalArray( size );
962     if ( normal_index.size() ) {
963         // object file specifies normal indices (i.e. normal indices
964         // aren't 'implied'
965         for ( i = 0; i < size; ++i ) {
966             normal = normals[ normal_index[i] ];
967             sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
968             nl -> add( tmp3 );
969         }
970     } else {
971         // use implied normal indices.  normal index = vertex index.
972         for ( i = 0; i < size; ++i ) {
973             normal = normals[ node_index[i] ];
974             sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
975             nl -> add( tmp3 );
976         }
977     }
978
979     // colors
980     ssgColourArray *cl = new ssgColourArray( 1 );
981     sgSetVec4( tmp4, 1.0, 1.0, 1.0, 1.0 );
982     cl->add( tmp4 );
983
984     // texture coordinates
985     size = tex_index.size();
986     Point3D texcoord;
987     ssgTexCoordArray *tl = new ssgTexCoordArray( size );
988     if ( size == 1 ) {
989         texcoord = texcoords[ tex_index[0] ];
990         sgSetVec2( tmp2, texcoord[0], texcoord[1] );
991         sgSetVec2( tmp2, texcoord[0], texcoord[1] );
992         if ( tex_width > 0 ) {
993             tmp2[0] *= (1000.0 / tex_width);
994         }
995         if ( tex_height > 0 ) {
996             tmp2[1] *= (1000.0 / tex_height);
997         }
998         tl -> add( tmp2 );
999     } else if ( size > 1 ) {
1000         for ( i = 0; i < size; ++i ) {
1001             texcoord = texcoords[ tex_index[i] ];
1002             sgSetVec2( tmp2, texcoord[0], texcoord[1] );
1003             if ( tex_width > 0 ) {
1004                 tmp2[0] *= (1000.0 / tex_width);
1005             }
1006             if ( tex_height > 0 ) {
1007                 tmp2[1] *= (1000.0 / tex_height);
1008             }
1009             tl -> add( tmp2 );
1010         }
1011     }
1012
1013     ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
1014
1015     // lookup the state record
1016
1017     leaf->setState( state );
1018
1019     if ( calc_lights ) {
1020         if ( coverage > 0.0 ) {
1021             if ( coverage < 10000.0 ) {
1022                 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
1023                        << coverage << ", pushing up to 10000");
1024                 coverage = 10000;
1025             }
1026             gen_random_surface_points(leaf, lights, coverage);
1027         }
1028     }
1029
1030     return leaf;
1031 }
1032
1033
1034 // Load an Binary obj file
1035 bool fgBinObjLoad( const string& path, const bool is_base,
1036                    Point3D *center,
1037                    double *bounding_radius,
1038                    ssgBranch* geometry,
1039                    ssgBranch* rwy_lights,
1040                    ssgVertexArray *ground_lights )
1041 {
1042     SGBinObject obj;
1043     bool use_dynamic_objects =
1044       fgGetBool("/sim/rendering/dynamic-objects", false);
1045
1046     if ( ! obj.read_bin( path ) ) {
1047         return false;
1048     }
1049
1050     geometry->setName( (char *)path.c_str() );
1051    
1052     double geod_lon = 0.0, geod_lat = 0.0, geod_alt = 0.0,
1053       geod_sl_radius = 0.0;
1054     if ( is_base ) {
1055         // reference point (center offset/bounding sphere)
1056         *center = obj.get_gbs_center();
1057         *bounding_radius = obj.get_gbs_radius();
1058
1059                                 // Calculate the geodetic centre of
1060                                 // the tile, for aligning automatic
1061                                 // objects.
1062         Point3D geoc = sgCartToPolar3d(*center);
1063         geod_lon = geoc.lon();
1064         sgGeocToGeod(geoc.lat(), geoc.radius(),
1065                      &geod_lat, &geod_alt, &geod_sl_radius);
1066         geod_lon *= SGD_RADIANS_TO_DEGREES;
1067         geod_lat *= SGD_RADIANS_TO_DEGREES;
1068     }
1069
1070     point_list nodes = obj.get_wgs84_nodes();
1071     point_list colors = obj.get_colors();
1072     point_list normals = obj.get_normals();
1073     point_list texcoords = obj.get_texcoords();
1074
1075     string material, tmp_mat;
1076     int_list vertex_index;
1077     int_list normal_index;
1078     int_list tex_index;
1079
1080     int i;
1081     bool is_lighting = false;
1082
1083     // generate points
1084     string_list pt_materials = obj.get_pt_materials();
1085     group_list pts_v = obj.get_pts_v();
1086     group_list pts_n = obj.get_pts_n();
1087     for ( i = 0; i < (int)pts_v.size(); ++i ) {
1088         // cout << "pts_v.size() = " << pts_v.size() << endl;
1089         tmp_mat = pt_materials[i];
1090         if ( tmp_mat.substr(0, 3) == "RWY" ) {
1091             material = "LIGHTS";
1092             is_lighting = true;
1093         } else {
1094             material = tmp_mat;
1095         }
1096         vertex_index = pts_v[i];
1097         normal_index = pts_n[i];
1098         tex_index.clear();
1099         ssgLeaf *leaf = gen_leaf( path, GL_POINTS, material,
1100                                   nodes, normals, texcoords,
1101                                   vertex_index, normal_index, tex_index,
1102                                   false, ground_lights );
1103
1104         if ( is_lighting ) {
1105             float ranges[] = { 0, 12000 };
1106             leaf->setCallback(SSG_CALLBACK_PREDRAW, runway_lights_predraw);
1107             ssgRangeSelector * lod = new ssgRangeSelector;
1108             lod->setRanges(ranges, 2);
1109             lod->addKid(leaf);
1110             rwy_lights->addKid(lod);
1111         } else {
1112             geometry->addKid( leaf );
1113         }
1114     }
1115
1116     // generate triangles
1117     string_list tri_materials = obj.get_tri_materials();
1118     group_list tris_v = obj.get_tris_v();
1119     group_list tris_n = obj.get_tris_n();
1120     group_list tris_tc = obj.get_tris_tc();
1121     for ( i = 0; i < (int)tris_v.size(); ++i ) {
1122         material = tri_materials[i];
1123         vertex_index = tris_v[i];
1124         normal_index = tris_n[i];
1125         tex_index = tris_tc[i];
1126         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, material,
1127                                   nodes, normals, texcoords,
1128                                   vertex_index, normal_index, tex_index,
1129                                   is_base, ground_lights );
1130
1131         if (use_dynamic_objects)
1132           gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
1133                                      material);
1134         geometry->addKid( leaf );
1135     }
1136
1137     // generate strips
1138     string_list strip_materials = obj.get_strip_materials();
1139     group_list strips_v = obj.get_strips_v();
1140     group_list strips_n = obj.get_strips_n();
1141     group_list strips_tc = obj.get_strips_tc();
1142     for ( i = 0; i < (int)strips_v.size(); ++i ) {
1143         material = strip_materials[i];
1144         vertex_index = strips_v[i];
1145         normal_index = strips_n[i];
1146         tex_index = strips_tc[i];
1147         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
1148                                   nodes, normals, texcoords,
1149                                   vertex_index, normal_index, tex_index,
1150                                   is_base, ground_lights );
1151
1152         if (use_dynamic_objects)
1153           gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
1154                                      material);
1155         geometry->addKid( leaf );
1156     }
1157
1158     // generate fans
1159     string_list fan_materials = obj.get_fan_materials();
1160     group_list fans_v = obj.get_fans_v();
1161     group_list fans_n = obj.get_fans_n();
1162     group_list fans_tc = obj.get_fans_tc();
1163     for ( i = 0; i < (int)fans_v.size(); ++i ) {
1164         material = fan_materials[i];
1165         vertex_index = fans_v[i];
1166         normal_index = fans_n[i];
1167         tex_index = fans_tc[i];
1168         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, material,
1169                                   nodes, normals, texcoords,
1170                                   vertex_index, normal_index, tex_index,
1171                                   is_base, ground_lights );
1172         if (use_dynamic_objects)
1173           gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
1174                                      material);
1175         geometry->addKid( leaf );
1176     }
1177
1178     return true;
1179 }