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