]> git.mxchange.org Git - flightgear.git/blob - src/Objects/obj.cxx
Changes to properly handle new style tri-strips.
[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 <Include/compiler.h>
41
42 #include STL_STRING
43 #include <map>                  // STL
44 #include <vector>               // STL
45 #include <ctype.h>              // isdigit()
46
47 #include <Debug/logstream.hxx>
48 #include <Misc/fgstream.hxx>
49 #include <Include/fg_constants.h>
50 #include <Main/options.hxx>
51 #include <Math/mat3.h>
52 #include <Math/fg_geodesy.hxx>
53 #include <Math/fg_random.h>
54 #include <Math/point3d.hxx>
55 #include <Math/polar3d.hxx>
56 #include <Misc/stopwatch.hxx>
57 #include <Scenery/tileentry.hxx>
58
59 #include "materialmgr.hxx"
60 #include "obj.hxx"
61
62 FG_USING_STD(string);
63 FG_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
75 // given three points defining a triangle, calculate the normal
76 static void calc_normal(Point3D p1, Point3D p2, 
77                         Point3D p3, double normal[3])
78 {
79     double v1[3], v2[3];
80     double temp;
81
82     v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2];
83     v2[0] = p3[0] - p1[0]; v2[1] = p3[1] - p1[1]; v2[2] = p3[2] - p1[2];
84
85     MAT3cross_product(normal, v1, v2);
86     MAT3_NORMALIZE_VEC(normal,temp);
87
88     // fgPrintf( FG_TERRAIN, FG_DEBUG, "  Normal = %.2f %.2f %.2f\n", 
89     //           normal[0], normal[1], normal[2]);
90 }
91
92
93 #define FG_TEX_CONSTANT 69.0
94
95
96 // Calculate texture coordinates for a given point.
97 static Point3D calc_tex_coords(const Point3D& node, const Point3D& ref) {
98     Point3D cp;
99     Point3D pp;
100     // double tmplon, tmplat;
101
102     // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
103     // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
104
105     cp = Point3D( node[0] + ref.x(),
106                   node[1] + ref.y(),
107                   node[2] + ref.z() );
108
109     pp = fgCartToPolar3d(cp);
110
111     // tmplon = pp.lon() * RAD_TO_DEG;
112     // tmplat = pp.lat() * RAD_TO_DEG;
113     // cout << tmplon << " " << tmplat << endl;
114
115     pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
116     pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
117
118     if ( pp.x() < 0.0 ) {
119         pp.setx( pp.x() + 11.0 );
120     }
121
122     if ( pp.y() < 0.0 ) {
123         pp.sety( pp.y() + 11.0 );
124     }
125
126     // cout << pp << endl;
127
128     return(pp);
129 }
130
131
132 // Generate a generic ocean tile on the fly
133 ssgBranch *fgGenTile( const string& path, FGTileEntry *t) {
134     fgFRAGMENT fragment;
135     fragment.init();
136     fragment.tile_ptr = t;
137
138     ssgSimpleState *state = NULL;
139
140     ssgBranch *tile = new ssgBranch () ;
141     tile -> setName ( (char *)path.c_str() ) ;
142
143     // find Ocean material in the properties list
144     if ( ! material_mgr.find( "Ocean", fragment.material_ptr )) {
145         FG_LOG( FG_TERRAIN, FG_ALERT, 
146                 "Ack! unknown usemtl name = " << "Ocean" 
147                 << " in " << path );
148     }
149
150     // set the texture width and height values for this
151     // material
152     FGMaterial m = fragment.material_ptr->get_m();
153     // double tex_width = m.get_xsize();
154     // double tex_height = m.get_ysize();
155
156     // set ssgState
157     state = fragment.material_ptr->get_state();
158
159     // Calculate center point
160     FGBucket b = t->tile_bucket;
161     double clon = b.get_center_lon();
162     double clat = b.get_center_lat();
163     double height = b.get_height();
164     double width = b.get_width();
165
166     Point3D center = fgGeodToCart(Point3D(clon*DEG_TO_RAD,clat*DEG_TO_RAD,0.0));
167     t->center = center;
168     fragment.center = center;
169     // cout << "center = " << center << endl;;
170     
171     // Caculate corner vertices
172     Point3D geod[4];
173     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
174     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
175     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
176     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
177
178     Point3D rad[4];
179     int i;
180     for ( i = 0; i < 4; ++i ) {
181         rad[i] = Point3D( geod[i].x() * DEG_TO_RAD, geod[i].y() * DEG_TO_RAD,
182                           geod[i].z() );
183     }
184
185     Point3D cart[4], rel[4];
186     t->nodes.clear();
187     for ( i = 0; i < 4; ++i ) {
188         cart[i] = fgGeodToCart(rad[i]);
189         rel[i] = cart[i] - center;
190         t->nodes.push_back( rel[i] );
191         // cout << "corner " << i << " = " << cart[i] << endl;
192     }
193
194     t->ncount = 4;
195
196     // Calculate bounding radius
197     t->bounding_radius = center.distance3D( cart[0] );
198     fragment.bounding_radius = t->bounding_radius;
199     // cout << "bounding radius = " << t->bounding_radius << endl;
200
201     // Calculate normals
202     Point3D normals[4];
203     for ( i = 0; i < 4; ++i ) {
204         normals[i] = cart[i];
205         double length = normals[i].distance3D( Point3D(0.0) );
206         normals[i] /= length;
207         // cout << "normal = " << normals[i] << endl;
208     }
209
210     // Calculate texture coordinates
211     Point3D texs[4];
212     for ( i = 0; i < 4; ++i ) {
213         texs[i] = calc_tex_coords( rel[i], center );
214         // cout << "texture coordinate = " << texs[i] << endl;
215     }
216
217     // Build flight gear structure
218     fragment.add_face(0, 1, 2);
219     fragment.add_face(0, 2, 3);
220     t->fragment_list.push_back(fragment);
221
222     // Build ssg structure
223     t->vtlist = new sgVec3 [ 4 ];
224     t->vnlist = new sgVec3 [ 4 ];
225     t->tclist = new sgVec2 [ 4 ];
226
227     for ( i = 0; i < 4; ++i ) {
228         sgSetVec3( t->vtlist[i], 
229                    rel[i].x(), rel[i].y(), rel[i].z() );
230         sgSetVec3( t->vnlist[i], 
231                    normals[i].x(), normals[i].y(), normals[i].z() );
232         sgSetVec2( t->tclist[i], texs[i].x(), texs[i].y() );
233     }
234     
235     unsigned short *vindex = new unsigned short [ 4 ];
236     unsigned short *tindex = new unsigned short [ 4 ];
237     for ( i = 0; i < 4; ++i ) {
238         vindex[i] = i;
239         tindex[i] = i;
240     }
241
242     ssgLeaf *leaf = 
243         new ssgVTable ( GL_TRIANGLE_FAN,
244                         4, vindex, t->vtlist,
245                         4, vindex, t->vnlist,
246                         4, tindex, t->tclist,
247                         0, NULL, NULL ) ;
248     leaf->setState( state );
249
250     tile->addKid( leaf );
251
252     return tile;
253 }
254
255
256 // Load a .obj file and build the fragment list
257 ssgBranch *fgObjLoad( const string& path, FGTileEntry *t) {
258     fgFRAGMENT fragment;
259     Point3D pp;
260     double approx_normal[3] /*, normal[3], scale = 0.0 */;
261     // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
262     // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
263     // GLint display_list = 0;
264     int shading;
265     bool in_fragment = false, in_faces = false;
266     int vncount, vtcount;
267     int n1 = 0, n2 = 0, n3 = 0, n4 = 0;
268     int tex;
269     int last1 = 0, last2 = 0;
270     bool odd = false;
271     point_list nodes;
272     Point3D node;
273     Point3D center;
274     double scenery_version = 0.0;
275     double tex_width = 1000.0, tex_height = 1000.0;
276     bool shared_done = false;
277     int_list fan_vertices;
278     int_list fan_tex_coords;
279     int i;
280     ssgSimpleState *state = NULL;
281
282     ssgBranch *tile = new ssgBranch () ;
283     tile -> setName ( (char *)path.c_str() ) ;
284
285     // Attempt to open "path.gz" or "path"
286     fg_gzifstream in( path );
287     if ( ! in.is_open() ) {
288         FG_LOG( FG_TERRAIN, FG_ALERT, "Cannot open file: " << path );
289         FG_LOG( FG_TERRAIN, FG_ALERT, "default to ocean tile: " << path );
290
291         return fgGenTile( path, t );
292     }
293
294     shading = current_options.get_shading();
295
296     in_fragment = false;
297     t->ncount = 0;
298     vncount = 0;
299     vtcount = 0;
300     t->bounding_radius = 0.0;
301     center = t->center;
302
303     StopWatch stopwatch;
304     stopwatch.start();
305
306     // ignore initial comments and blank lines. (priming the pump)
307     // in >> skipcomment;
308     // string line;
309
310     string token;
311     char c;
312
313 #ifdef __MWERKS__
314     while ( in.get(c) && c  != '\0' ) {
315         in.putback(c);
316 #else
317     while ( ! in.eof() ) {
318 #endif
319
320 #if defined( MACOS )
321         in >> ::skipws;
322 #else
323         in >> skipws;
324 #endif
325
326         if ( in.get( c ) && c == '#' ) {
327             // process a comment line
328
329             // getline( in, line );
330             // cout << "comment = " << line << endl;
331
332             in >> token;
333
334             if ( token == "Version" ) {
335                 // read scenery versions number
336                 in >> scenery_version;
337                 // cout << "scenery_version = " << scenery_version << endl;
338             } else if ( token == "gbs" ) {
339                 // reference point (center offset)
340                 in >> t->center >> t->bounding_radius;
341                 center = t->center;
342                 // cout << "center = " << center 
343                 //      << " radius = " << t->bounding_radius << endl;
344             } else if ( token == "bs" ) {
345                 // reference point (center offset)
346                 in >> fragment.center;
347                 in >> fragment.bounding_radius;
348
349                 // cout << "center = " << fragment.center 
350                 //      << " radius = " << fragment.bounding_radius << endl;
351             } else if ( token == "usemtl" ) {
352                 // material property specification
353
354                 // if first usemtl with shared_done = false, then set
355                 // shared_done true and build the ssg shared lists
356                 if ( ! shared_done ) {
357                     // sanity check
358                     if ( (int)nodes.size() != vncount ) {
359                         FG_LOG( FG_TERRAIN, FG_ALERT, 
360                                 "Tile has mismatched nodes and normals: " 
361                                 << path );
362                         // exit(-1);
363                     }
364                     shared_done = true;
365
366                     t->vtlist = new sgVec3 [ nodes.size() ];
367                     t->vnlist = new sgVec3 [ vncount ];
368                     t->tclist = new sgVec2 [ vtcount ];
369
370                     for ( i = 0; i < (int)nodes.size(); ++i ) {
371                         sgSetVec3( t->vtlist[i], 
372                                    nodes[i][0], nodes[i][1], nodes[i][2] );
373                     }
374                     for ( i = 0; i < vncount; ++i ) {
375                         sgSetVec3( t->vnlist[i], 
376                                    normals[i][0], 
377                                    normals[i][1],
378                                    normals[i][2] );
379                     }
380                     for ( i = 0; i < vtcount; ++i ) {
381                         sgSetVec2( t->tclist[i],
382                                    tex_coords[i][0],
383                                    tex_coords[i][1] );
384                     }
385                 }
386
387                 // series of individual triangles
388                 // if ( in_faces ) {
389                 //     xglEnd();
390                 // }
391
392                 // this also signals the start of a new fragment
393                 if ( in_fragment ) {
394                     // close out the previous structure and start the next
395                     // xglEndList();
396                     // printf("xglEnd(); xglEndList();\n");
397
398                     // update fragment
399                     // fragment.display_list = display_list;
400
401                     // push this fragment onto the tile's object list
402                     t->fragment_list.push_back(fragment);
403                 } else {
404                     in_fragment = true;
405                 }
406
407                 // printf("start of fragment (usemtl)\n");
408
409                 // display_list = xglGenLists(1);
410                 // xglNewList(display_list, GL_COMPILE);
411                 // printf("xglGenLists(); xglNewList();\n");
412                 in_faces = false;
413
414                 // reset the existing face list
415                 // printf("cleaning a fragment with %d faces\n", 
416                 //        fragment.faces.size());
417                 fragment.init();
418                 
419                 // scan the material line
420                 string material;
421                 in >> material;
422                 fragment.tile_ptr = t;
423                 
424                 // find this material in the properties list
425                 if ( ! material_mgr.find( material, fragment.material_ptr )) {
426                     FG_LOG( FG_TERRAIN, FG_ALERT, 
427                             "Ack! unknown usemtl name = " << material 
428                             << " in " << path );
429                 }
430
431                 // set the texture width and height values for this
432                 // material
433                 FGMaterial m = fragment.material_ptr->get_m();
434                 tex_width = m.get_xsize();
435                 tex_height = m.get_ysize();
436                 state = fragment.material_ptr->get_state();
437                 // cout << "(w) = " << tex_width << " (h) = " 
438                 //      << tex_width << endl;
439
440                 // initialize the fragment transformation matrix
441                 /*
442                  for ( i = 0; i < 16; i++ ) {
443                    fragment.matrix[i] = 0.0;
444                  }
445                  fragment.matrix[0] = fragment.matrix[5] =
446                  fragment.matrix[10] = fragment.matrix[15] = 1.0;
447                 */
448             } else {
449                 // unknown comment, just gobble the input untill the
450                 // end of line
451
452                 in >> skipeol;
453             }
454         } else {
455             in.putback( c );
456         
457             in >> token;
458
459             // cout << "token = " << token << endl;
460
461             if ( token == "vn" ) {
462                 // vertex normal
463                 if ( vncount < FG_MAX_NODES ) {
464                     in >> normals[vncount][0]
465                        >> normals[vncount][1]
466                        >> normals[vncount][2];
467                     vncount++;
468                 } else {
469                     FG_LOG( FG_TERRAIN, FG_ALERT, 
470                             "Read too many vertex normals in " << path 
471                             << " ... dying :-(" );
472                     exit(-1);
473                 }
474             } else if ( token == "vt" ) {
475                 // vertex texture coordinate
476                 if ( vtcount < FG_MAX_NODES*3 ) {
477                     in >> tex_coords[vtcount][0]
478                        >> tex_coords[vtcount][1];
479                     vtcount++;
480                 } else {
481                     FG_LOG( FG_TERRAIN, FG_ALERT, 
482                             "Read too many vertex texture coords in " << path
483                             << " ... dying :-("
484                             );
485                     exit(-1);
486                 }
487             } else if ( token == "v" ) {
488                 // node (vertex)
489                 if ( t->ncount < FG_MAX_NODES ) {
490                     /* in >> nodes[t->ncount][0]
491                        >> nodes[t->ncount][1]
492                        >> nodes[t->ncount][2]; */
493                     in >> node;
494                     nodes.push_back(node);
495                     t->ncount++;
496                 } else {
497                     FG_LOG( FG_TERRAIN, FG_ALERT, 
498                             "Read too many nodes in " << path 
499                             << " ... dying :-(");
500                     exit(-1);
501                 }
502             } else if ( token == "t" ) {
503                 // start a new triangle strip
504
505                 n1 = n2 = n3 = n4 = 0;
506
507                 // fgPrintf( FG_TERRAIN, FG_DEBUG, 
508                 //           "    new tri strip = %s", line);
509                 in >> n1 >> n2 >> n3;
510                 fragment.add_face(n1, n2, n3);
511
512                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "(t) = ");
513
514                 // xglBegin(GL_TRIANGLE_STRIP);
515                 // printf("xglBegin(tristrip) %d %d %d\n", n1, n2, n3);
516
517                 odd = true; 
518                 // scale = 1.0;
519
520                 if ( shading ) {
521                     // Shading model is "GL_SMOOTH" so use precalculated
522                     // (averaged) normals
523                     // MAT3_SCALE_VEC(normal, normals[n1], scale);
524                     // xglNormal3dv(normal);
525                     pp = calc_tex_coords(nodes[n1], center);
526                     // xglTexCoord2f(pp.lon(), pp.lat());
527                     // xglVertex3dv(nodes[n1].get_n());         
528
529                     // MAT3_SCALE_VEC(normal, normals[n2], scale);
530                     // xglNormal3dv(normal);
531                     pp = calc_tex_coords(nodes[n2], center);
532                     // xglTexCoord2f(pp.lon(), pp.lat());
533                     // xglVertex3dv(nodes[n2].get_n());                         
534
535                     // MAT3_SCALE_VEC(normal, normals[n3], scale);
536                     // xglNormal3dv(normal);
537                     pp = calc_tex_coords(nodes[n3], center);
538                     // xglTexCoord2f(pp.lon(), pp.lat());
539                     // xglVertex3dv(nodes[n3].get_n());
540                 } else {
541                     // Shading model is "GL_FLAT" so calculate per face
542                     // normals on the fly.
543                     if ( odd ) {
544                         calc_normal(nodes[n1], nodes[n2], 
545                                     nodes[n3], approx_normal);
546                     } else {
547                         calc_normal(nodes[n2], nodes[n1], 
548                                     nodes[n3], approx_normal);
549                     }
550                     // MAT3_SCALE_VEC(normal, approx_normal, scale);
551                     // xglNormal3dv(normal);
552
553                     pp = calc_tex_coords(nodes[n1], center);
554                     // xglTexCoord2f(pp.lon(), pp.lat());
555                     // xglVertex3dv(nodes[n1].get_n());         
556
557                     pp = calc_tex_coords(nodes[n2], center);
558                     // xglTexCoord2f(pp.lon(), pp.lat());
559                     // xglVertex3dv(nodes[n2].get_n());         
560                     
561                     pp = calc_tex_coords(nodes[n3], center);
562                     // xglTexCoord2f(pp.lon(), pp.lat());
563                     // xglVertex3dv(nodes[n3].get_n());         
564                 }
565                 // printf("some normals, texcoords, and vertices\n");
566
567                 odd = !odd;
568                 last1 = n2;
569                 last2 = n3;
570
571                 // There can be three or four values 
572                 char c;
573                 while ( in.get(c) ) {
574                     if ( c == '\n' ) {
575                         break; // only the one
576                     }
577                     if ( isdigit(c) ){
578                         in.putback(c);
579                         in >> n4;
580                         break;
581                     }
582                 }
583
584                 if ( n4 > 0 ) {
585                     fragment.add_face(n3, n2, n4);
586                     
587                     if ( shading ) {
588                         // Shading model is "GL_SMOOTH"
589                         // MAT3_SCALE_VEC(normal, normals[n4], scale);
590                     } else {
591                         // Shading model is "GL_FLAT"
592                         calc_normal(nodes[n3], nodes[n2], nodes[n4], 
593                                     approx_normal);
594                         // MAT3_SCALE_VEC(normal, approx_normal, scale);
595                     }
596                     // xglNormal3dv(normal);
597                     pp = calc_tex_coords(nodes[n4], center);
598                     // xglTexCoord2f(pp.lon(), pp.lat());
599                     // xglVertex3dv(nodes[n4].get_n());         
600                     
601                     odd = !odd;
602                     last1 = n3;
603                     last2 = n4;
604                     // printf("a normal, texcoord, and vertex (4th)\n");
605                 }
606             } else if ( (token == "tf") || (token == "ts") ) {
607                 // triangle fan
608                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new fan");
609
610                 fan_vertices.clear();
611                 fan_tex_coords.clear();
612                 odd = true;
613
614                 // xglBegin(GL_TRIANGLE_FAN);
615
616                 in >> n1;
617                 fan_vertices.push_back( n1 );
618                 // xglNormal3dv(normals[n1]);
619                 if ( in.get( c ) && c == '/' ) {
620                     in >> tex;
621                     fan_tex_coords.push_back( tex );
622                     if ( scenery_version >= 0.4 ) {
623                         if ( tex_width > 0 ) {
624                             t->tclist[tex][0] *= (1000.0 / tex_width);
625                         }
626                         if ( tex_height > 0 ) {
627                             t->tclist[tex][1] *= (1000.0 / tex_height);
628                         }
629                     }
630                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
631                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
632                 } else {
633                     in.putback( c );
634                     pp = calc_tex_coords(nodes[n1], center);
635                 }
636                 // xglTexCoord2f(pp.x(), pp.y());
637                 // xglVertex3dv(nodes[n1].get_n());
638
639                 in >> n2;
640                 fan_vertices.push_back( n2 );
641                 // xglNormal3dv(normals[n2]);
642                 if ( in.get( c ) && c == '/' ) {
643                     in >> tex;
644                     fan_tex_coords.push_back( tex );
645                     if ( scenery_version >= 0.4 ) {
646                         if ( tex_width > 0 ) {
647                             t->tclist[tex][0] *= (1000.0 / tex_width);
648                         }
649                         if ( tex_height > 0 ) {
650                             t->tclist[tex][1] *= (1000.0 / tex_height);
651                         }
652                     }
653                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
654                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
655                 } else {
656                     in.putback( c );
657                     pp = calc_tex_coords(nodes[n2], center);
658                 }
659                 // xglTexCoord2f(pp.x(), pp.y());
660                 // xglVertex3dv(nodes[n2].get_n());
661                 
662                 // read all subsequent numbers until next thing isn't a number
663                 while ( true ) {
664 #if defined( MACOS )
665                     in >> ::skipws;
666 #else
667                     in >> skipws;
668 #endif
669
670                     char c;
671                     in.get(c);
672                     in.putback(c);
673                     if ( ! isdigit(c) || in.eof() ) {
674                         break;
675                     }
676
677                     in >> n3;
678                     fan_vertices.push_back( n3 );
679                     // cout << "  triangle = " 
680                     //      << n1 << "," << n2 << "," << n3 
681                     //      << endl;
682                     // xglNormal3dv(normals[n3]);
683                     if ( in.get( c ) && c == '/' ) {
684                         in >> tex;
685                         fan_tex_coords.push_back( tex );
686                         if ( scenery_version >= 0.4 ) {
687                             if ( tex_width > 0 ) {
688                                 t->tclist[tex][0] *= (1000.0 / tex_width);
689                             }
690                             if ( tex_height > 0 ) {
691                                 t->tclist[tex][1] *= (1000.0 / tex_height);
692                             }
693                         }
694                         pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
695                         pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
696                     } else {
697                         in.putback( c );
698                         pp = calc_tex_coords(nodes[n3], center);
699                     }
700                     // xglTexCoord2f(pp.x(), pp.y());
701                     // xglVertex3dv(nodes[n3].get_n());
702
703                     if ( odd ) {
704                         fragment.add_face(n1, n2, n3);
705                     } else {
706                         fragment.add_face(n2, n1, n3);
707                     }
708
709                     odd = !odd;
710                     n1 = n2;
711                     n2 = n3;
712                 }
713
714                 // xglEnd();
715
716                 // build the ssg entity
717                 unsigned short *vindex = 
718                     new unsigned short [ fan_vertices.size() ];
719                 unsigned short *tindex = 
720                     new unsigned short [ fan_tex_coords.size() ];
721                 for ( i = 0; i < (int)fan_vertices.size(); ++i ) {
722                     vindex[i] = fan_vertices[i];
723                 }
724                 for ( i = 0; i < (int)fan_tex_coords.size(); ++i ) {
725                     tindex[i] = fan_tex_coords[i];
726                 }
727                 ssgLeaf *leaf;
728                 if ( token == "tf" ) {
729                     // triangle fan
730                     leaf = 
731                         new ssgVTable ( GL_TRIANGLE_FAN,
732                                         fan_vertices.size(), vindex, t->vtlist,
733                                         fan_vertices.size(), vindex, t->vnlist,
734                                         fan_tex_coords.size(), tindex,t->tclist,
735                                         0, NULL, NULL ) ;
736                 } else {
737                     // triangle strip
738                     leaf = 
739                         new ssgVTable ( GL_TRIANGLE_STRIP,
740                                         fan_vertices.size(), vindex, t->vtlist,
741                                         fan_vertices.size(), vindex, t->vnlist,
742                                         fan_tex_coords.size(), tindex,t->tclist,
743                                         0, NULL, NULL ) ;
744                 }
745                 leaf->setState( state );
746
747                 tile->addKid( leaf );
748
749             } else if ( token == "f" ) {
750                 // unoptimized face
751
752                 if ( !in_faces ) {
753                     // xglBegin(GL_TRIANGLES);
754                     // printf("xglBegin(triangles)\n");
755                     in_faces = true;
756                 }
757
758                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new triangle = %s", line);*/
759                 in >> n1 >> n2 >> n3;
760                 fragment.add_face(n1, n2, n3);
761
762                 // xglNormal3d(normals[n1][0], normals[n1][1], normals[n1][2]);
763                 // xglNormal3dv(normals[n1]);
764                 pp = calc_tex_coords(nodes[n1], center);
765                 // xglTexCoord2f(pp.lon(), pp.lat());
766                 // xglVertex3dv(nodes[n1].get_n());
767
768                 // xglNormal3dv(normals[n2]);
769                 pp = calc_tex_coords(nodes[n2], center);
770                 // xglTexCoord2f(pp.lon(), pp.lat());
771                 // xglVertex3dv(nodes[n2].get_n());
772                 
773                 // xglNormal3dv(normals[n3]);
774                 pp = calc_tex_coords(nodes[n3], center);
775                 // xglTexCoord2f(pp.lon(), pp.lat());
776                 // xglVertex3dv(nodes[n3].get_n());
777                 // printf("some normals, texcoords, and vertices (tris)\n");
778             } else if ( token == "q" ) {
779                 // continue a triangle strip
780                 n1 = n2 = 0;
781
782                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "continued tri strip = %s ", 
783                 //           line);
784                 in >> n1;
785
786                 // There can be one or two values 
787                 char c;
788                 while ( in.get(c) ) {
789                     if ( c == '\n' ) {
790                         break; // only the one
791                     }
792
793                     if ( isdigit(c) ) {
794                         in.putback(c);
795                         in >> n2;
796                         break;
797                     }
798                 }
799                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "read %d %d\n", n1, n2);
800
801                 if ( odd ) {
802                     fragment.add_face(last1, last2, n1);
803                 } else {
804                     fragment.add_face(last2, last1, n1);
805                 }
806
807                 if ( shading ) {
808                     // Shading model is "GL_SMOOTH"
809                     // MAT3_SCALE_VEC(normal, normals[n1], scale);
810                 } else {
811                     // Shading model is "GL_FLAT"
812                     if ( odd ) {
813                         calc_normal(nodes[last1], nodes[last2], 
814                                     nodes[n1], approx_normal);
815                     } else {
816                         calc_normal(nodes[last2], nodes[last1], 
817                                     nodes[n1], approx_normal);
818                     }
819                     // MAT3_SCALE_VEC(normal, approx_normal, scale);
820                 }
821                 // xglNormal3dv(normal);
822
823                 pp = calc_tex_coords(nodes[n1], center);
824                 // xglTexCoord2f(pp.lon(), pp.lat());
825                 // xglVertex3dv(nodes[n1].get_n());
826                 // printf("a normal, texcoord, and vertex (4th)\n");
827    
828                 odd = !odd;
829                 last1 = last2;
830                 last2 = n1;
831
832                 if ( n2 > 0 ) {
833                     // fgPrintf( FG_TERRAIN, FG_DEBUG, " (cont)\n");
834
835                     if ( odd ) {
836                         fragment.add_face(last1, last2, n2);
837                     } else {
838                         fragment.add_face(last2, last1, n2);
839                     }
840
841                     if ( shading ) {
842                         // Shading model is "GL_SMOOTH"
843                         // MAT3_SCALE_VEC(normal, normals[n2], scale);
844                     } else {
845                         // Shading model is "GL_FLAT"
846                         if ( odd ) {
847                             calc_normal(nodes[last1], nodes[last2], 
848                                         nodes[n2], approx_normal);
849                         } else {
850                             calc_normal(nodes[last2], nodes[last1], 
851                                         nodes[n2], approx_normal);
852                         }
853                         // MAT3_SCALE_VEC(normal, approx_normal, scale);
854                     }
855                     // xglNormal3dv(normal);
856                 
857                     pp = calc_tex_coords(nodes[n2], center);
858                     // xglTexCoord2f(pp.lon(), pp.lat());
859                     // xglVertex3dv(nodes[n2].get_n());         
860                     // printf("a normal, texcoord, and vertex (4th)\n");
861
862                     odd = !odd;
863                     last1 = last2;
864                     last2 = n2;
865                 }
866             } else {
867                 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in " 
868                         << path << " = " << token );
869             }
870
871             // eat white space before start of while loop so if we are
872             // done with useful input it is noticed before hand.
873 #if defined( MACOS )
874             in >> ::skipws;
875 #else
876             in >> skipws;
877 #endif
878         }
879     }
880
881     if ( in_fragment ) {
882         // close out the previous structure and start the next
883         // xglEnd();
884         // xglEndList();
885         // printf("xglEnd(); xglEndList();\n");
886         
887         // update fragment
888         // fragment.display_list = display_list;
889         
890         // push this fragment onto the tile's object list
891         t->fragment_list.push_back(fragment);
892     }
893
894 #if 0
895     // Draw normal vectors (for visually verifying normals)
896     xglBegin(GL_LINES);
897     xglColor3f(0.0, 0.0, 0.0);
898     for ( i = 0; i < t->ncount; i++ ) {
899         xglVertex3d(nodes[i][0],
900                     nodes[i][1] ,
901                     nodes[i][2]);
902         xglVertex3d(nodes[i][0] + 500*normals[i][0],
903                     nodes[i][1] + 500*normals[i][1],
904                     nodes[i][2] + 500*normals[i][2]);
905     } 
906     xglEnd();
907 #endif
908
909     t->nodes = nodes;
910
911     stopwatch.stop();
912     FG_LOG( FG_TERRAIN, FG_DEBUG, 
913             "Loaded " << path << " in " 
914             << stopwatch.elapsedSeconds() << " seconds" );
915     
916     return tile;
917 }
918
919