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