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