]> git.mxchange.org Git - flightgear.git/blob - src/Objects/obj.cxx
Fixes to vor/ils/adf range pickup.
[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 FG_MATH_EXCEPTION_CLASH
29 #  include <math.h>
30 #endif
31
32 #include <stdio.h>
33 #include <string.h>
34
35 // #if defined ( __sun__ )
36 // extern "C" void *memmove(void *, const void *, size_t);
37 // extern "C" void *memset(void *, int, size_t);
38 // #endif
39
40 #include <simgear/compiler.h>
41 #include <simgear/io/sg_binobj.hxx>
42
43 #include STL_STRING
44 #include <map>                  // STL
45 #include <vector>               // STL
46 #include <ctype.h>              // isdigit()
47
48 #include <simgear/constants.h>
49 #include <simgear/debug/logstream.hxx>
50 #include <simgear/math/point3d.hxx>
51 #include <simgear/math/polar3d.hxx>
52 #include <simgear/math/sg_geodesy.hxx>
53 #include <simgear/math/sg_random.h>
54 #include <simgear/misc/fgstream.hxx>
55 #include <simgear/misc/stopwatch.hxx>
56 #include <simgear/misc/texcoord.hxx>
57
58 #include <Main/globals.hxx>
59 #include <Main/fg_props.hxx>
60 #include <Scenery/tileentry.hxx>
61
62 #include "matlib.hxx"
63 #include "obj.hxx"
64
65 FG_USING_STD(string);
66 FG_USING_STD(vector);
67
68
69 typedef vector < int > int_list;
70 typedef int_list::iterator int_list_iterator;
71 typedef int_list::const_iterator int_point_list_iterator;
72
73
74 static double normals[FG_MAX_NODES][3];
75 static double tex_coords[FG_MAX_NODES*3][3];
76
77
78 #define FG_TEX_CONSTANT 69.0
79
80 // Calculate texture coordinates for a given point.
81 static Point3D local_calc_tex_coords(const Point3D& node, const Point3D& ref) {
82     Point3D cp;
83     Point3D pp;
84     // double tmplon, tmplat;
85
86     // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
87     // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
88
89     cp = Point3D( node[0] + ref.x(),
90                   node[1] + ref.y(),
91                   node[2] + ref.z() );
92
93     pp = sgCartToPolar3d(cp);
94
95     // tmplon = pp.lon() * RAD_TO_DEG;
96     // tmplat = pp.lat() * RAD_TO_DEG;
97     // cout << tmplon << " " << tmplat << endl;
98
99     pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
100     pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
101
102     if ( pp.x() < 0.0 ) {
103         pp.setx( pp.x() + 11.0 );
104     }
105
106     if ( pp.y() < 0.0 ) {
107         pp.sety( pp.y() + 11.0 );
108     }
109
110     // cout << pp << endl;
111
112     return(pp);
113 }
114
115
116 // Generate a generic ocean tile on the fly
117 ssgBranch *fgGenTile( const string& path, FGTileEntry *t) {
118     FGNewMat *newmat;
119
120     ssgSimpleState *state = NULL;
121
122     ssgBranch *tile = new ssgBranch () ;
123     tile -> setName ( (char *)path.c_str() ) ;
124
125     double tex_width = 1000.0;
126     // double tex_height;
127
128     // find Ocean material in the properties list
129     newmat = material_lib.find( "Ocean" );
130     if ( newmat != NULL ) {
131         // set the texture width and height values for this
132         // material
133         tex_width = newmat->get_xsize();
134         // tex_height = newmat->get_ysize();
135         
136         // set ssgState
137         state = newmat->get_state();
138     } else {
139         FG_LOG( FG_TERRAIN, FG_ALERT, 
140                 "Ack! unknown usemtl name = " << "Ocean" 
141                 << " in " << path );
142     }
143
144     // Calculate center point
145     SGBucket b = t->tile_bucket;
146     double clon = b.get_center_lon();
147     double clat = b.get_center_lat();
148     double height = b.get_height();
149     double width = b.get_width();
150
151     Point3D center = sgGeodToCart(Point3D(clon*DEG_TO_RAD,clat*DEG_TO_RAD,0.0));
152     t->center = center;
153     // cout << "center = " << center << endl;;
154     
155     // Caculate corner vertices
156     Point3D geod[4];
157     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
158     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
159     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
160     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
161
162     Point3D rad[4];
163     int i;
164     for ( i = 0; i < 4; ++i ) {
165         rad[i] = Point3D( geod[i].x() * DEG_TO_RAD, geod[i].y() * DEG_TO_RAD,
166                           geod[i].z() );
167     }
168
169     Point3D cart[4], rel[4];
170     t->nodes.clear();
171     for ( i = 0; i < 4; ++i ) {
172         cart[i] = sgGeodToCart(rad[i]);
173         rel[i] = cart[i] - center;
174         t->nodes.push_back( rel[i] );
175         // cout << "corner " << i << " = " << cart[i] << endl;
176     }
177
178     t->ncount = 4;
179
180     // Calculate bounding radius
181     t->bounding_radius = center.distance3D( cart[0] );
182     // cout << "bounding radius = " << t->bounding_radius << endl;
183
184     // Calculate normals
185     Point3D normals[4];
186     for ( i = 0; i < 4; ++i ) {
187         normals[i] = cart[i];
188         double length = normals[i].distance3D( Point3D(0.0) );
189         normals[i] /= length;
190         // cout << "normal = " << normals[i] << endl;
191     }
192
193     // Calculate texture coordinates
194     point_list geod_nodes;
195     geod_nodes.clear();
196     for ( i = 0; i < 4; ++i ) {
197         geod_nodes.push_back( geod[i] );
198     }
199     int_list rectangle;
200     rectangle.clear();
201     for ( i = 0; i < 4; ++i ) {
202         rectangle.push_back( i );
203     }
204     point_list texs = calc_tex_coords( b, geod_nodes, rectangle, 
205                                        1000.0 / tex_width );
206
207     // Allocate ssg structure
208     ssgVertexArray   *vl = new ssgVertexArray( 4 );
209     ssgNormalArray   *nl = new ssgNormalArray( 4 );
210     ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
211     ssgColourArray   *cl = new ssgColourArray( 1 );
212
213     sgVec4 color;
214     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
215     cl->add( color );
216
217     // sgVec3 *vtlist = new sgVec3 [ 4 ];
218     // t->vec3_ptrs.push_back( vtlist );
219     // sgVec3 *vnlist = new sgVec3 [ 4 ];
220     // t->vec3_ptrs.push_back( vnlist );
221     // sgVec2 *tclist = new sgVec2 [ 4 ];
222     // t->vec2_ptrs.push_back( tclist );
223
224     sgVec2 tmp2;
225     sgVec3 tmp3;
226     for ( i = 0; i < 4; ++i ) {
227         sgSetVec3( tmp3, 
228                    rel[i].x(), rel[i].y(), rel[i].z() );
229         vl->add( tmp3 );
230
231         sgSetVec3( tmp3, 
232                    normals[i].x(), normals[i].y(), normals[i].z() );
233         nl->add( tmp3 );
234
235         sgSetVec2( tmp2, texs[i].x(), texs[i].y());
236         tl->add( tmp2 );
237     }
238     
239     ssgLeaf *leaf = 
240         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
241
242     leaf->setState( state );
243
244     tile->addKid( leaf );
245
246     return tile;
247 }
248
249
250 static float fgTriArea( sgVec3 p0, sgVec3 p1, sgVec3 p2 ) {
251     /* 
252        From comp.graph.algorithms FAQ
253        2A(P) = abs(N.(sum_{i=0}^{n-1}(v_i x v_{i+1})))
254      */
255     sgVec3 sum;
256     sgZeroVec3( sum );
257         
258     sgVec3 norm;
259     sgMakeNormal( norm, p0, p1, p2 );
260         
261     float *vv[3];
262     vv[0] = p0;
263     vv[1] = p1;
264     vv[2] = p2;
265         
266     for( int i=0; i<3; i++ ) {
267         int ii = (i+1) % 3;
268         sum[0] += (vv[i][1] * vv[ii][2] - vv[i][2] * vv[ii][1]) ;
269         sum[1] += (vv[i][2] * vv[ii][0] - vv[i][0] * vv[ii][2]) ;
270         sum[2] += (vv[i][0] * vv[ii][1] - vv[i][1] * vv[ii][0]) ;
271     }
272
273     return( sgAbs(sgScalarProductVec3( norm, sum )) * SG_HALF );
274 }
275
276
277 static void random_pt_inside_tri( float *res,
278                                   float *n1, float *n2, float *n3 )
279 {
280     sgVec3 p1, p2, p3;
281
282     double a = sg_random();
283     double b = sg_random();
284     if ( a + b > 1.0 ) {
285         a = 1.0 - a;
286         b = 1.0 - b;
287     }
288     double c = 1 - a - b;
289
290     sgScaleVec3( p1, n1, a );
291     sgScaleVec3( p2, n2, b );
292     sgScaleVec3( p3, n3, c );
293
294     sgAddVec3( res, p1, p2 );
295     sgAddVec3( res, p3 );
296 }
297
298
299 static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
300                                        double factor ) {
301     int num = leaf->getNumTriangles();
302     if ( num > 0 ) {
303         short int n1, n2, n3;
304         float *p1, *p2, *p3;
305         sgVec3 result;
306
307         // generate a repeatable random seed
308         p1 = leaf->getVertex( 0 );
309         unsigned int *seed = (unsigned int *)p1;
310         sg_srandom( *seed );
311
312         for ( int i = 0; i < num; ++i ) {
313             leaf->getTriangle( i, &n1, &n2, &n3 );
314             p1 = leaf->getVertex(n1);
315             p2 = leaf->getVertex(n2);
316             p3 = leaf->getVertex(n3);
317             double area = fgTriArea( p1, p2, p3 );
318             double num = area / factor;
319
320             // generate a light point for each unit of area
321             while ( num > 1.0 ) {
322                 random_pt_inside_tri( result, p1, p2, p3 );
323                 lights->add( result );
324                 num -= 1.0;
325             }
326             // for partial units of area, use a zombie door method to
327             // create the proper random chance of a light being created
328             // for this triangle
329             if ( num > 0.0 ) {
330                 if ( sg_random() <= num ) {
331                     // a zombie made it through our door
332                     random_pt_inside_tri( result, p1, p2, p3 );
333                     lights->add( result );
334                 }
335             }
336         }
337     }
338 }
339
340
341 // Load an Ascii obj file
342 static ssgBranch *fgAsciiObjLoad( const string& path, FGTileEntry *t,
343                                   ssgVertexArray *lights, const bool is_base)
344 {
345     FGNewMat *newmat = NULL;
346     string material;
347     float coverage = -1;
348     Point3D pp;
349     // sgVec3 approx_normal;
350     // double normal[3], scale = 0.0;
351     // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
352     // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
353     // GLint display_list = 0;
354     int shading;
355     bool in_faces = false;
356     int vncount, vtcount;
357     int n1 = 0, n2 = 0, n3 = 0;
358     int tex;
359     // int last1 = 0, last2 = 0;
360     bool odd = false;
361     point_list nodes;
362     Point3D node;
363     Point3D center;
364     double scenery_version = 0.0;
365     double tex_width = 1000.0, tex_height = 1000.0;
366     bool shared_done = false;
367     int_list fan_vertices;
368     int_list fan_tex_coords;
369     int i;
370     ssgSimpleState *state = NULL;
371     sgVec3 *vtlist, *vnlist;
372     sgVec2 *tclist;
373
374     ssgBranch *tile = new ssgBranch () ;
375
376     tile -> setName ( (char *)path.c_str() ) ;
377
378     // Attempt to open "path.gz" or "path"
379     fg_gzifstream in( path );
380     if ( ! in.is_open() ) {
381         FG_LOG( FG_TERRAIN, FG_DEBUG, "Cannot open file: " << path );
382         FG_LOG( FG_TERRAIN, FG_DEBUG, "default to ocean tile: " << path );
383
384         return NULL;
385     }
386
387     shading = fgGetBool("/sim/rendering/shading");
388
389     if ( is_base ) {
390         t->ncount = 0;
391     }
392     vncount = 0;
393     vtcount = 0;
394     if ( is_base ) {
395         t->bounding_radius = 0.0;
396     }
397     center = t->center;
398
399     StopWatch stopwatch;
400     stopwatch.start();
401
402     // ignore initial comments and blank lines. (priming the pump)
403     // in >> skipcomment;
404     // string line;
405
406     string token;
407     char c;
408
409 #ifdef __MWERKS__
410     while ( in.get(c) && c  != '\0' ) {
411         in.putback(c);
412 #else
413     while ( ! in.eof() ) {
414 #endif
415
416 #if defined( macintosh ) || defined( _MSC_VER )
417         in >> ::skipws;
418 #else
419         in >> skipws;
420 #endif
421
422         if ( in.get( c ) && c == '#' ) {
423             // process a comment line
424
425             // getline( in, line );
426             // cout << "comment = " << line << endl;
427
428             in >> token;
429
430             if ( token == "Version" ) {
431                 // read scenery versions number
432                 in >> scenery_version;
433                 // cout << "scenery_version = " << scenery_version << endl;
434                 if ( scenery_version > 0.4 ) {
435                     FG_LOG( FG_TERRAIN, FG_ALERT, 
436                             "\nYou are attempting to load a tile format that\n"
437                             << "is newer than this version of flightgear can\n"
438                             << "handle.  You should upgrade your copy of\n"
439                             << "FlightGear to the newest version.  For\n"
440                             << "details, please see:\n"
441                             << "\n    http://www.flightgear.org\n" );
442                     exit(-1);
443                 }
444             } else if ( token == "gbs" ) {
445                 // reference point (center offset)
446                 if ( is_base ) {
447                     in >> t->center >> t->bounding_radius;
448                 } else {
449                     Point3D junk1;
450                     double junk2;
451                     in >> junk1 >> junk2;
452                 }
453                 center = t->center;
454                 // cout << "center = " << center 
455                 //      << " radius = " << t->bounding_radius << endl;
456             } else if ( token == "bs" ) {
457                 // reference point (center offset)
458                 // (skip past this)
459                 Point3D junk1;
460                 double junk2;
461                 in >> junk1 >> junk2;
462             } else if ( token == "usemtl" ) {
463                 // material property specification
464
465                 // if first usemtl with shared_done = false, then set
466                 // shared_done true and build the ssg shared lists
467                 if ( ! shared_done ) {
468                     // sanity check
469                     if ( (int)nodes.size() != vncount ) {
470                         FG_LOG( FG_TERRAIN, FG_ALERT, 
471                                 "Tile has mismatched nodes = " << nodes.size()
472                                 << " and normals = " << vncount << " : " 
473                                 << path );
474                         // exit(-1);
475                     }
476                     shared_done = true;
477
478                     vtlist = new sgVec3 [ nodes.size() ];
479                     t->vec3_ptrs.push_back( vtlist );
480                     vnlist = new sgVec3 [ vncount ];
481                     t->vec3_ptrs.push_back( vnlist );
482                     tclist = new sgVec2 [ vtcount ];
483                     t->vec2_ptrs.push_back( tclist );
484
485                     for ( i = 0; i < (int)nodes.size(); ++i ) {
486                         sgSetVec3( vtlist[i], 
487                                    nodes[i][0], nodes[i][1], nodes[i][2] );
488                     }
489                     for ( i = 0; i < vncount; ++i ) {
490                         sgSetVec3( vnlist[i], 
491                                    normals[i][0], 
492                                    normals[i][1],
493                                    normals[i][2] );
494                     }
495                     for ( i = 0; i < vtcount; ++i ) {
496                         sgSetVec2( tclist[i],
497                                    tex_coords[i][0],
498                                    tex_coords[i][1] );
499                     }
500                 }
501
502                 // display_list = xglGenLists(1);
503                 // xglNewList(display_list, GL_COMPILE);
504                 // printf("xglGenLists(); xglNewList();\n");
505                 in_faces = false;
506
507                 // scan the material line
508                 in >> material;
509                 
510                 // find this material in the properties list
511
512                 newmat = material_lib.find( material );
513                 if ( newmat == NULL ) {
514                     // see if this is an on the fly texture
515                     string file = path;
516                     int pos = file.rfind( "/" );
517                     file = file.substr( 0, pos );
518                     cout << "current file = " << file << endl;
519                     file += "/";
520                     file += material;
521                     cout << "current file = " << file << endl;
522                     if ( ! material_lib.add_item( file ) ) {
523                         FG_LOG( FG_TERRAIN, FG_ALERT, 
524                                 "Ack! unknown usemtl name = " << material 
525                                 << " in " << path );
526                     } else {
527                         // locate our newly created material
528                         newmat = material_lib.find( material );
529                         if ( newmat == NULL ) {
530                             FG_LOG( FG_TERRAIN, FG_ALERT, 
531                                     "Ack! bad on the fly materia create = "
532                                     << material << " in " << path );
533                         }
534                     }
535                 }
536
537                 if ( newmat != NULL ) {
538                     // set the texture width and height values for this
539                     // material
540                     tex_width = newmat->get_xsize();
541                     tex_height = newmat->get_ysize();
542                     state = newmat->get_state();
543                     coverage = newmat->get_light_coverage();
544                     // cout << "(w) = " << tex_width << " (h) = " 
545                     //      << tex_width << endl;
546                 } else {
547                     coverage = -1;
548                 }
549             } else {
550                 // unknown comment, just gobble the input until the
551                 // end of line
552
553                 in >> skipeol;
554             }
555         } else {
556             in.putback( c );
557         
558             in >> token;
559
560             // cout << "token = " << token << endl;
561
562             if ( token == "vn" ) {
563                 // vertex normal
564                 if ( vncount < FG_MAX_NODES ) {
565                     in >> normals[vncount][0]
566                        >> normals[vncount][1]
567                        >> normals[vncount][2];
568                     vncount++;
569                 } else {
570                     FG_LOG( FG_TERRAIN, FG_ALERT, 
571                             "Read too many vertex normals in " << path 
572                             << " ... dying :-(" );
573                     exit(-1);
574                 }
575             } else if ( token == "vt" ) {
576                 // vertex texture coordinate
577                 if ( vtcount < FG_MAX_NODES*3 ) {
578                     in >> tex_coords[vtcount][0]
579                        >> tex_coords[vtcount][1];
580                     vtcount++;
581                 } else {
582                     FG_LOG( FG_TERRAIN, FG_ALERT, 
583                             "Read too many vertex texture coords in " << path
584                             << " ... dying :-("
585                             );
586                     exit(-1);
587                 }
588             } else if ( token == "v" ) {
589                 // node (vertex)
590                 if ( t->ncount < FG_MAX_NODES ) {
591                     /* in >> nodes[t->ncount][0]
592                        >> nodes[t->ncount][1]
593                        >> nodes[t->ncount][2]; */
594                     in >> node;
595                     nodes.push_back(node);
596                     if ( is_base ) {
597                         t->ncount++;
598                     }
599                 } else {
600                     FG_LOG( FG_TERRAIN, FG_ALERT, 
601                             "Read too many nodes in " << path 
602                             << " ... dying :-(");
603                     exit(-1);
604                 }
605             } else if ( (token == "tf") || (token == "ts") || (token == "f") ) {
606                 // triangle fan, strip, or individual face
607                 // FG_LOG( FG_TERRAIN, FG_INFO, "new fan or strip");
608
609                 fan_vertices.clear();
610                 fan_tex_coords.clear();
611                 odd = true;
612
613                 // xglBegin(GL_TRIANGLE_FAN);
614
615                 in >> n1;
616                 fan_vertices.push_back( n1 );
617                 // xglNormal3dv(normals[n1]);
618                 if ( in.get( c ) && c == '/' ) {
619                     in >> tex;
620                     fan_tex_coords.push_back( tex );
621                     if ( scenery_version >= 0.4 ) {
622                         if ( tex_width > 0 ) {
623                             tclist[tex][0] *= (1000.0 / tex_width);
624                         }
625                         if ( tex_height > 0 ) {
626                             tclist[tex][1] *= (1000.0 / tex_height);
627                         }
628                     }
629                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
630                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
631                 } else {
632                     in.putback( c );
633                     pp = local_calc_tex_coords(nodes[n1], center);
634                 }
635                 // xglTexCoord2f(pp.x(), pp.y());
636                 // xglVertex3dv(nodes[n1].get_n());
637
638                 in >> n2;
639                 fan_vertices.push_back( n2 );
640                 // xglNormal3dv(normals[n2]);
641                 if ( in.get( c ) && c == '/' ) {
642                     in >> tex;
643                     fan_tex_coords.push_back( tex );
644                     if ( scenery_version >= 0.4 ) {
645                         if ( tex_width > 0 ) {
646                             tclist[tex][0] *= (1000.0 / tex_width);
647                         }
648                         if ( tex_height > 0 ) {
649                             tclist[tex][1] *= (1000.0 / tex_height);
650                         }
651                     }
652                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
653                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
654                 } else {
655                     in.putback( c );
656                     pp = local_calc_tex_coords(nodes[n2], center);
657                 }
658                 // xglTexCoord2f(pp.x(), pp.y());
659                 // xglVertex3dv(nodes[n2].get_n());
660                 
661                 // read all subsequent numbers until next thing isn't a number
662                 while ( true ) {
663 #if defined( macintosh ) || defined( _MSC_VER )
664                     in >> ::skipws;
665 #else
666                     in >> skipws;
667 #endif
668
669                     char c;
670                     in.get(c);
671                     in.putback(c);
672                     if ( ! isdigit(c) || in.eof() ) {
673                         break;
674                     }
675
676                     in >> n3;
677                     fan_vertices.push_back( n3 );
678                     // cout << "  triangle = " 
679                     //      << n1 << "," << n2 << "," << n3 
680                     //      << endl;
681                     // xglNormal3dv(normals[n3]);
682                     if ( in.get( c ) && c == '/' ) {
683                         in >> tex;
684                         fan_tex_coords.push_back( tex );
685                         if ( scenery_version >= 0.4 ) {
686                             if ( tex_width > 0 ) {
687                                 tclist[tex][0] *= (1000.0 / tex_width);
688                             }
689                             if ( tex_height > 0 ) {
690                                 tclist[tex][1] *= (1000.0 / tex_height);
691                             }
692                         }
693                         pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
694                         pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
695                     } else {
696                         in.putback( c );
697                         pp = local_calc_tex_coords(nodes[n3], center);
698                     }
699                     // xglTexCoord2f(pp.x(), pp.y());
700                     // xglVertex3dv(nodes[n3].get_n());
701
702                     if ( (token == "tf") || (token == "f") ) {
703                         // triangle fan
704                         n2 = n3;
705                     } else {
706                         // triangle strip
707                         odd = !odd;
708                         n1 = n2;
709                         n2 = n3;
710                     }
711                 }
712
713                 // xglEnd();
714
715                 // build the ssg entity
716                 int size = (int)fan_vertices.size();
717                 ssgVertexArray   *vl = new ssgVertexArray( size );
718                 ssgNormalArray   *nl = new ssgNormalArray( size );
719                 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
720                 ssgColourArray   *cl = new ssgColourArray( 1 );
721
722                 sgVec4 color;
723                 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
724                 cl->add( color );
725
726                 sgVec2 tmp2;
727                 sgVec3 tmp3;
728                 for ( i = 0; i < size; ++i ) {
729                     sgCopyVec3( tmp3, vtlist[ fan_vertices[i] ] );
730                     vl -> add( tmp3 );
731
732                     sgCopyVec3( tmp3, vnlist[ fan_vertices[i] ] );
733                     nl -> add( tmp3 );
734
735                     sgCopyVec2( tmp2, tclist[ fan_tex_coords[i] ] );
736                     tl -> add( tmp2 );
737                 }
738
739                 ssgLeaf *leaf = NULL;
740                 if ( token == "tf" ) {
741                     // triangle fan
742                     leaf = 
743                         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
744                 } else if ( token == "ts" ) {
745                     // triangle strip
746                     leaf = 
747                         new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl );
748                 } else if ( token == "f" ) {
749                     // triangle
750                     leaf = 
751                         new ssgVtxTable ( GL_TRIANGLES, vl, nl, tl, cl );
752                 }
753                 // leaf->makeDList();
754                 leaf->setState( state );
755
756                 tile->addKid( leaf );
757
758                 if ( is_base ) {
759                     if ( coverage > 0.0 ) {
760                         if ( coverage < 10000.0 ) {
761                             FG_LOG(FG_INPUT, FG_ALERT, "Light coverage is "
762                                    << coverage << ", pushing up to 10000");
763                             coverage = 10000;
764                         }
765                         gen_random_surface_points(leaf, lights, coverage);
766                     }
767                 }
768             } else {
769                 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in " 
770                         << path << " = " << token );
771             }
772
773             // eat white space before start of while loop so if we are
774             // done with useful input it is noticed before hand.
775 #if defined( macintosh ) || defined( _MSC_VER )
776             in >> ::skipws;
777 #else
778             in >> skipws;
779 #endif
780         }
781     }
782
783     if ( is_base ) {
784         t->nodes = nodes;
785     }
786
787     stopwatch.stop();
788     FG_LOG( FG_TERRAIN, FG_DEBUG, 
789             "Loaded " << path << " in " 
790             << stopwatch.elapsedSeconds() << " seconds" );
791
792     return tile;
793 }
794
795
796 static ssgLeaf *gen_leaf( const string& path,
797                           const GLenum ty, const string& material,
798                           const point_list& nodes, const point_list& normals,
799                           const point_list& texcoords,
800                           const int_list node_index,
801                           const int_list& tex_index,
802                           const bool calc_lights, ssgVertexArray *lights )
803 {
804     double tex_width = 1000.0, tex_height = 1000.0;
805     ssgSimpleState *state = NULL;
806     float coverage = -1;
807
808     FGNewMat *newmat = material_lib.find( material );
809     if ( newmat == NULL ) {
810         // see if this is an on the fly texture
811         string file = path;
812         int pos = file.rfind( "/" );
813         file = file.substr( 0, pos );
814         cout << "current file = " << file << endl;
815         file += "/";
816         file += material;
817         cout << "current file = " << file << endl;
818         if ( ! material_lib.add_item( file ) ) {
819             FG_LOG( FG_TERRAIN, FG_ALERT, 
820                     "Ack! unknown usemtl name = " << material 
821                     << " in " << path );
822         } else {
823             // locate our newly created material
824             newmat = material_lib.find( material );
825             if ( newmat == NULL ) {
826                 FG_LOG( FG_TERRAIN, FG_ALERT, 
827                         "Ack! bad on the fly materia create = "
828                         << material << " in " << path );
829             }
830         }
831     }
832
833     if ( newmat != NULL ) {
834         // set the texture width and height values for this
835         // material
836         tex_width = newmat->get_xsize();
837         tex_height = newmat->get_ysize();
838         state = newmat->get_state();
839         coverage = newmat->get_light_coverage();
840         // cout << "(w) = " << tex_width << " (h) = " 
841         //      << tex_width << endl;
842     } else {
843         coverage = -1;
844     }
845
846     int size = node_index.size();
847     ssgVertexArray   *vl = new ssgVertexArray( size );
848     ssgNormalArray   *nl = new ssgNormalArray( size );
849     ssgTexCoordArray *tl = new ssgTexCoordArray( size );
850     ssgColourArray   *cl = new ssgColourArray( 1 );
851
852     sgVec4 color;
853     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
854     cl->add( color );
855
856     sgVec2 tmp2;
857     sgVec3 tmp3;
858     int i;
859     for ( i = 0; i < size; ++i ) {
860         Point3D node = nodes[ node_index[i] ];
861         sgSetVec3( tmp3, node[0], node[1], node[2] );
862         vl -> add( tmp3 );
863
864         Point3D normal = normals[ node_index[i] ];
865         sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
866         nl -> add( tmp3 );
867
868         Point3D texcoord = texcoords[ tex_index[i] ];
869         sgSetVec2( tmp2, texcoord[0], texcoord[1] );
870         if ( tex_width > 0 ) {
871             tmp2[0] *= (1000.0 / tex_width);
872         }
873         if ( tex_height > 0 ) {
874             tmp2[1] *= (1000.0 / tex_height);
875         }
876         tl -> add( tmp2 );
877     }
878
879     // cout << "before leaf create" << endl;
880     ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
881     // cout << "after leaf create" << endl;
882
883     // lookup the state record
884     // cout << "looking up material = " << endl;
885     // cout << material << endl;
886     // cout << "'" << endl;
887
888     leaf->setState( state );
889
890     if ( calc_lights ) {
891         if ( coverage > 0.0 ) {
892             if ( coverage < 10000.0 ) {
893                 FG_LOG(FG_INPUT, FG_ALERT, "Light coverage is "
894                        << coverage << ", pushing up to 10000");
895                 coverage = 10000;
896             }
897             gen_random_surface_points(leaf, lights, coverage);
898         }
899     }
900
901     return leaf;
902 }
903
904
905 // Load an Binary obj file
906 static ssgBranch *fgBinObjLoad( const string& path, FGTileEntry *t,
907                                 ssgVertexArray *lights, const bool is_base)
908 {
909     int i;
910
911     SGBinObject obj;
912     bool result = obj.read_bin( path );
913
914     if ( !result ) {
915         return NULL;
916     }
917
918     // cout << "fans size = " << obj.get_fans_v().size()
919     //      << " fan_mats size = " << obj.get_fan_materials().size() << endl;
920
921     ssgBranch *object = new ssgBranch();
922     object->setName( (char *)path.c_str() );
923    
924     if ( is_base && t != NULL ) {
925         // reference point (center offset/bounding sphere)
926         t->center = obj.get_gbs_center();
927         t->bounding_radius = obj.get_gbs_radius();
928     }
929
930     point_list nodes = obj.get_wgs84_nodes();
931     point_list normals = obj.get_normals();
932     point_list texcoords = obj.get_texcoords();
933
934     string material;
935     int_list vertex_index;
936     int_list tex_index;
937
938     // generate triangles
939     string_list tri_materials = obj.get_tri_materials();
940     group_list tris_v = obj.get_tris_v();
941     group_list tris_tc = obj.get_tris_tc();
942     for ( i = 0; i < (int)tris_v.size(); ++i ) {
943         material = tri_materials[i];
944         vertex_index = tris_v[i];
945         tex_index = tris_tc[i];
946         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, material,
947                                   nodes, normals, texcoords,
948                                   vertex_index, tex_index,
949                                   is_base, lights );
950
951         object->addKid( leaf );
952     }
953
954     // generate strips
955     string_list strip_materials = obj.get_strip_materials();
956     group_list strips_v = obj.get_strips_v();
957     group_list strips_tc = obj.get_strips_tc();
958     for ( i = 0; i < (int)strips_v.size(); ++i ) {
959         material = strip_materials[i];
960         vertex_index = strips_v[i];
961         tex_index = strips_tc[i];
962         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
963                                   nodes, normals, texcoords,
964                                   vertex_index, tex_index,
965                                   is_base, lights );
966
967         object->addKid( leaf );
968     }
969
970     // generate fans
971     string_list fan_materials = obj.get_fan_materials();
972     group_list fans_v = obj.get_fans_v();
973     group_list fans_tc = obj.get_fans_tc();
974     for ( i = 0; i < (int)fans_v.size(); ++i ) {
975         material = fan_materials[i];
976         vertex_index = fans_v[i];
977         tex_index = fans_tc[i];
978         ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, material,
979                                   nodes, normals, texcoords,
980                                   vertex_index, tex_index,
981                                   is_base, lights );
982
983         object->addKid( leaf );
984     }
985
986     return object;
987 }
988
989
990 // Load an obj file
991 ssgBranch *fgObjLoad( const string& path, FGTileEntry *t,
992                       ssgVertexArray *lights, const bool is_base)
993 {
994     ssgBranch *result = NULL;
995
996     // try loading binary format
997     result = fgBinObjLoad( path, t, lights, is_base );
998     if ( result == NULL ) {
999         // next try the older ascii format
1000         result = fgAsciiObjLoad( path, t, lights, is_base );
1001         if ( result == NULL ) {
1002             // default to an ocean tile
1003             result = fgGenTile( path, t );
1004         }
1005     }
1006
1007     return result;
1008 }