]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
0a7799702932aee536a35cc17d8ee45774501d36
[simgear.git] / simgear / io / sg_binobj.cxx
1 // sg_binobj.cxx -- routines to read and write low level flightgear 3d objects
2 //
3 // Written by Curtis Olson, started January 2000.
4 //
5 // Copyright (C) 2000  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU 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
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include <stdio.h>
32 #include <time.h>
33 #include <zlib.h>
34
35 #include <list>
36 #include STL_STRING
37
38 #include <simgear/bucket/newbucket.hxx>
39
40 #include "lowlevel.hxx"
41 #include "sg_binobj.hxx"
42
43 #ifdef _MSC_VER
44 #  include <Win32/mkdir.hpp>
45 #endif
46
47 FG_USING_STD( cout );
48 FG_USING_STD( endl );
49
50 enum {
51     SG_BOUNDING_SPHERE = 0,
52
53     SG_VERTEX_LIST = 1,
54     SG_NORMAL_LIST = 2,
55     SG_TEXCOORD_LIST = 3,
56
57     SG_TRIANGLE_FACES = 10,
58     SG_TRIANGLE_STRIPS = 11,
59     SG_TRIANGLE_FANS = 12
60 } tgObjectTypes;
61
62 enum {
63     SG_MATERIAL = 0
64 } tgPropertyTypes;
65
66
67 // calculate the center of a list of points, by taking the halfway
68 // point between the min and max points.
69 static Point3D calc_center( point_list& wgs84_nodes ) {
70     Point3D p, min, max;
71
72     if ( wgs84_nodes.size() ) {
73         min = max = wgs84_nodes[0];
74     } else {
75         min = max = Point3D( 0 );
76     }
77
78     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
79         p = wgs84_nodes[i];
80
81         if ( p.x() < min.x() ) { min.setx( p.x() ); }
82         if ( p.y() < min.y() ) { min.sety( p.y() ); }
83         if ( p.z() < min.z() ) { min.setz( p.z() ); }
84
85         if ( p.x() > max.x() ) { max.setx( p.x() ); }
86         if ( p.y() > max.y() ) { max.sety( p.y() ); }
87         if ( p.z() > max.z() ) { max.setz( p.z() ); }
88     }
89
90     return ( min + max ) / 2.0;
91 }
92
93 // calculate the bounding sphere.  Center is the center of the
94 // tile and zero elevation
95 double sgCalcBoundingRadius( Point3D center, point_list& wgs84_nodes ) {
96     double dist_squared;
97     double radius_squared = 0;
98     
99     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
100         dist_squared = center.distance3Dsquared( wgs84_nodes[i] );
101         if ( dist_squared > radius_squared ) {
102             radius_squared = dist_squared;
103         }
104     }
105
106     return sqrt(radius_squared);
107 }
108
109
110 // write out the structures to an ASCII file.  We assume that the
111 // groups come to us sorted by material property.  If not, things
112 // don't break, but the result won't be as optimal.
113 void sgWriteAsciiObj( const string& base, const string& name, const SGBucket& b,
114                       Point3D gbs_center, float gbs_radius,
115                       const point_list& wgs84_nodes, const point_list& normals,
116                       const point_list& texcoords, 
117                       const group_list& tris_v, const group_list& tris_tc, 
118                       const string_list& tri_materials,
119                       const group_list& strips_v, const group_list& strips_tc, 
120                       const string_list& strip_materials,
121                       const group_list& fans_v, const group_list& fans_tc,
122                       const string_list& fan_materials )
123 {
124     Point3D p;
125     int i, j;
126
127     string dir = base + "/" + b.gen_base_path();
128     string command = "mkdir -p " + dir;
129 #ifdef _MSC_VER
130     fg_mkdir( dir.c_str() );
131 #else
132     system(command.c_str());
133 #endif
134
135     // string file = dir + "/" + b.gen_index_str();
136     string file = dir + "/" + name;
137     cout << "Output file = " << file << endl;
138
139     FILE *fp;
140     if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
141         cout << "ERROR: opening " << file << " for writing!" << endl;
142         exit(-1);
143     }
144
145     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
146          << tri_materials.size() << endl;
147     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
148          << strip_materials.size() << endl;
149     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
150          << fan_materials.size() << endl;
151
152     cout << "points = " << wgs84_nodes.size() << endl;
153     cout << "tex coords = " << texcoords.size() << endl;
154     // write headers
155     fprintf(fp, "# FGFS Scenery\n");
156     fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
157
158     time_t calendar_time = time(NULL);
159     struct tm *local_tm;
160     local_tm = localtime( &calendar_time );
161     char time_str[256];
162     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
163     fprintf(fp, "# Created %s\n", time_str );
164     fprintf(fp, "\n");
165
166     // write bounding sphere
167     fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
168             gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
169     fprintf(fp, "\n");
170
171     // dump vertex list
172     fprintf(fp, "# vertex list\n");
173     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
174         p = wgs84_nodes[i] - gbs_center;
175         
176         fprintf(fp,  "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
177     }
178     fprintf(fp, "\n");
179
180     fprintf(fp, "# vertex normal list\n");
181     for ( i = 0; i < (int)normals.size(); ++i ) {
182         p = normals[i];
183         fprintf(fp,  "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
184     }
185     fprintf(fp, "\n");
186
187     // dump texture coordinates
188     fprintf(fp, "# texture coordinate list\n");
189     for ( i = 0; i < (int)texcoords.size(); ++i ) {
190         p = texcoords[i];
191         fprintf(fp,  "vt %.5f %.5f\n", p.x(), p.y() );
192     }
193     fprintf(fp, "\n");
194
195     // dump individual triangles if they exist
196     if ( tris_v.size() > 0 ) {
197         fprintf(fp, "# triangle groups\n");
198
199         int start = 0;
200         int end = 1;
201         string material;
202         while ( start < (int)tri_materials.size() ) {
203             // find next group
204             material = tri_materials[start];
205             while ( (end < (int)tri_materials.size()) && 
206                     (material == tri_materials[end]) )
207             {
208                 // cout << "end = " << end << endl;
209                 end++;
210             }
211             // cout << "group = " << start << " to " << end - 1 << endl;
212
213             // make a list of points for the group
214             point_list group_nodes;
215             group_nodes.clear();
216             Point3D bs_center;
217             double bs_radius = 0;
218             for ( i = start; i < end; ++i ) {
219                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
220                     group_nodes.push_back( wgs84_nodes[ tris_v[i][j] ] );
221                     bs_center = calc_center( group_nodes );
222                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
223                 }
224             }
225
226             // write group headers
227             fprintf(fp, "\n");
228             fprintf(fp, "# usemtl %s\n", material.c_str());
229             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
230                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
231
232             // write groups
233             for ( i = start; i < end; ++i ) {
234                 fprintf(fp, "f");
235                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
236                     fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
237                 }
238                 fprintf(fp, "\n");
239             }
240
241             start = end;
242             end = start + 1;
243         }
244     }
245
246     // dump triangle groups
247     if ( strips_v.size() > 0 ) {
248         fprintf(fp, "# triangle strips\n");
249
250         int start = 0;
251         int end = 1;
252         string material;
253         while ( start < (int)strip_materials.size() ) {
254             // find next group
255             material = strip_materials[start];
256             while ( (end < (int)strip_materials.size()) && 
257                     (material == strip_materials[end]) )
258                 {
259                     // cout << "end = " << end << endl;
260                     end++;
261                 }
262             // cout << "group = " << start << " to " << end - 1 << endl;
263
264             // make a list of points for the group
265             point_list group_nodes;
266             group_nodes.clear();
267             Point3D bs_center;
268             double bs_radius = 0;
269             for ( i = start; i < end; ++i ) {
270                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
271                     group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] );
272                     bs_center = calc_center( group_nodes );
273                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
274                 }
275             }
276
277             // write group headers
278             fprintf(fp, "\n");
279             fprintf(fp, "# usemtl %s\n", material.c_str());
280             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
281                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
282
283             // write groups
284             for ( i = start; i < end; ++i ) {
285                 fprintf(fp, "ts");
286                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
287                     fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
288                 }
289                 fprintf(fp, "\n");
290             }
291             
292             start = end;
293             end = start + 1;
294         }
295     }
296
297     // close the file
298     fclose(fp);
299
300     command = "gzip --force --best " + file;
301     system(command.c_str());
302 }
303
304
305 // read a binary file and populate the provided structures.
306 void sgReadBinObj( const string& file,
307                    Point3D &gbs_center, float *gbs_radius,
308                    point_list& wgs84_nodes, point_list& normals,
309                    point_list& texcoords, 
310                    group_list& tris_v, group_list& tris_tc, 
311                    string_list& tri_materials,
312                    group_list& strips_v, group_list& strips_tc, 
313                    string_list& strip_materials,
314                    group_list& fans_v, group_list& fans_tc,
315                    string_list& fan_materials )
316 {
317     Point3D p;
318     int i, j, k, m;
319
320     // zero out structures
321     wgs84_nodes.clear();
322     normals.clear();
323     texcoords.clear();
324
325     tris_v.clear();
326     tris_tc.clear();
327     tri_materials.clear();
328
329     strips_v.clear();
330     strips_tc.clear();
331     strip_materials.clear();
332
333     fans_v.clear();
334     fans_tc.clear();
335     fan_materials.clear();
336    
337     cout << "Loading binary input file = " << file << endl;
338
339     gzFile fp;
340     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
341         string filegz = file + ".gz";
342         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
343             cout << "ERROR: opening " << file << " or " << filegz
344                  << "for reading!" << endl;
345             exit(-1);
346         }
347     }
348
349     // read headers
350     int header, version;
351     sgReadInt( fp, &header );
352     if ( ((header & 0xFF000000) >> 24) == 'T' &&
353          ((header & 0x00FF0000) >> 16) == 'G' ) {
354         cout << "Good header" << endl;
355         // read file version
356         version = (header & 0x0000FFFF);
357         cout << "File version = " << version << endl;
358     }
359
360     // read creation time
361     time_t calendar_time;
362     sgReadLong( fp, &calendar_time );
363     struct tm *local_tm;
364     local_tm = localtime( &calendar_time );
365     char time_str[256];
366     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
367     cout << "File created on " << time_str << endl;
368
369     // read number of top level objects
370     short nobjects;
371     sgReadShort( fp, &nobjects );
372     cout << "Total objects to read = " << nobjects << endl;
373
374     // read in objects
375     for ( i = 0; i < nobjects; ++i ) {
376         // read object header
377         char obj_type;
378         short nproperties, nelements;
379         sgReadChar( fp, &obj_type );
380         sgReadShort( fp, &nproperties );
381         sgReadShort( fp, &nelements );
382
383         cout << "object " << i << " = " << (int)obj_type << " props = "
384              << nproperties << " elements = " << nelements << endl;
385             
386         if ( obj_type == SG_BOUNDING_SPHERE ) {
387             // read bounding sphere properties
388             for ( j = 0; j < nproperties; ++j ) {
389                 char prop_type;
390                 sgReadChar( fp, &prop_type );
391
392                 int nbytes;
393                 sgReadInt( fp, &nbytes );
394                 cout << "property size = " << nbytes << endl;
395                 char buf[nbytes];
396                 char *ptr = buf;
397                 sgReadBytes( fp, nbytes, ptr );
398             }
399
400             // read bounding sphere elements
401             for ( j = 0; j < nelements; ++j ) {
402                 int nbytes;
403                 sgReadInt( fp, &nbytes );
404                 cout << "element size = " << nbytes << endl;
405                 char buf[nbytes];
406                 char *ptr = buf;
407                 sgReadBytes( fp, nbytes, ptr );
408
409                 double *dptr = (double *)ptr;
410                 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
411                 cout << "Center = " << gbs_center << endl;
412                 ptr += sizeof(double) * 3;
413                 
414                 float *fptr = (float *)ptr;
415                 *gbs_radius = fptr[0];
416                 cout << "Bounding radius = " << *gbs_radius << endl;
417             }
418         } else if ( obj_type == SG_VERTEX_LIST ) {
419             // read vertex list properties
420             for ( j = 0; j < nproperties; ++j ) {
421                 char prop_type;
422                 sgReadChar( fp, &prop_type );
423
424                 int nbytes;
425                 sgReadInt( fp, &nbytes );
426                 cout << "property size = " << nbytes << endl;
427                 char buf[nbytes];
428                 char *ptr = buf;
429                 sgReadBytes( fp, nbytes, ptr );
430             }
431
432             // read vertex list elements
433             for ( j = 0; j < nelements; ++j ) {
434                 int nbytes;
435                 sgReadInt( fp, &nbytes );
436                 cout << "element size = " << nbytes << endl;
437                 char buf[nbytes];
438                 char *ptr = buf;
439                 sgReadBytes( fp, nbytes, ptr );
440                 int count = nbytes / (sizeof(float) * 3);
441                 float *fptr = (float *)ptr;
442                 for ( k = 0; k < count; ++k ) {
443                     p = Point3D( fptr[0], fptr[1], fptr[2] );
444                     cout << "node = " << p << endl;
445                     wgs84_nodes.push_back( p );
446                     fptr += 3;
447                 }
448             }
449         } else if ( obj_type == SG_NORMAL_LIST ) {
450             // read normal list properties
451             for ( j = 0; j < nproperties; ++j ) {
452                 char prop_type;
453                 sgReadChar( fp, &prop_type );
454
455                 int nbytes;
456                 sgReadInt( fp, &nbytes );
457                 cout << "property size = " << nbytes << endl;
458                 char buf[nbytes];
459                 char *ptr = buf;
460                 sgReadBytes( fp, nbytes, ptr );
461             }
462
463             // read normal list elements
464             for ( j = 0; j < nelements; ++j ) {
465                 int nbytes;
466                 sgReadInt( fp, &nbytes );
467                 cout << "element size = " << nbytes << endl;
468                 char buf[nbytes];
469                 char *ptr = buf;
470                 sgReadBytes( fp, nbytes, ptr );
471                 int count = nbytes / 3;
472                 for ( k = 0; k < count; ++k ) {
473                     p = Point3D( ptr[0] / 256.0, ptr[1] / 256.0,
474                                  ptr[2] / 256.0 );
475                     cout << "normal = " << p << endl;
476                     normals.push_back( p );
477                     ptr += 3;
478                 }
479             }
480         } else if ( obj_type == SG_TEXCOORD_LIST ) {
481             // read texcoord list properties
482             for ( j = 0; j < nproperties; ++j ) {
483                 char prop_type;
484                 sgReadChar( fp, &prop_type );
485
486                 int nbytes;
487                 sgReadInt( fp, &nbytes );
488                 cout << "property size = " << nbytes << endl;
489                 char buf[nbytes];
490                 char *ptr = buf;
491                 sgReadBytes( fp, nbytes, ptr );
492             }
493
494             // read texcoord list elements
495             for ( j = 0; j < nelements; ++j ) {
496                 int nbytes;
497                 sgReadInt( fp, &nbytes );
498                 cout << "element size = " << nbytes << endl;
499                 char buf[nbytes];
500                 char *ptr = buf;
501                 sgReadBytes( fp, nbytes, ptr );
502                 int count = nbytes / (sizeof(float) * 2);
503                 float *fptr = (float *)ptr;
504                 for ( k = 0; k < count; ++k ) {
505                     p = Point3D( fptr[0], fptr[1], 0 );
506                     cout << "texcoord = " << p << endl;
507                     texcoords.push_back( p );
508                     fptr += 2;
509                 }
510             }
511         } else if ( obj_type == SG_TRIANGLE_FACES ) {
512             // read triangle face properties
513             for ( j = 0; j < nproperties; ++j ) {
514                 char prop_type;
515                 sgReadChar( fp, &prop_type );
516
517                 int nbytes;
518                 sgReadInt( fp, &nbytes );
519                 cout << "property size = " << nbytes << endl;
520                 char buf[nbytes];
521                 char *ptr = buf;
522                 sgReadBytes( fp, nbytes, ptr );
523                 if ( prop_type == SG_MATERIAL ) {
524                     char material[256];
525                     strncpy( material, ptr, nbytes );
526                     material[nbytes] = '\0';
527                     cout << "material type = " << material << endl;
528                     tri_materials.push_back( material );
529                 }
530             }
531
532             // read triangle face elements
533             for ( j = 0; j < nelements; ++j ) {
534                 int nbytes;
535                 sgReadInt( fp, &nbytes );
536                 cout << "element size = " << nbytes << endl;
537                 char buf[nbytes];
538                 char *ptr = buf;
539                 sgReadBytes( fp, nbytes, ptr );
540                 int count = nbytes / (sizeof(short) * 2 * 3);
541                 short *sptr = (short *)ptr;
542                 int_list vs, tcs;
543                 for ( k = 0; k < count; ++k ) {
544                     vs.clear(); tcs.clear();
545                     for ( m = 0; m < 3; ++m ) {
546                         vs.push_back( sptr[0] );
547                         tcs.push_back( sptr[1] );
548                         cout << sptr[0] << "/" << sptr[1] << " ";
549                         sptr += 2;
550                     }
551                     cout << endl;
552                     tris_v.push_back( vs );
553                     tris_tc.push_back( tcs );
554                 }
555             }
556         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
557             // read triangle strip properties
558             for ( j = 0; j < nproperties; ++j ) {
559                 char prop_type;
560                 sgReadChar( fp, &prop_type );
561
562                 int nbytes;
563                 sgReadInt( fp, &nbytes );
564                 cout << "property size = " << nbytes << endl;
565                 char buf[nbytes];
566                 char *ptr = buf;
567                 sgReadBytes( fp, nbytes, ptr );
568                 if ( prop_type == SG_MATERIAL ) {
569                     char material[256];
570                     strncpy( material, ptr, nbytes );
571                     material[nbytes] = '\0';
572                     cout << "material type = " << material << endl;
573                     strip_materials.push_back( material );
574                 }
575             }
576
577             // read triangle strip elements
578             for ( j = 0; j < nelements; ++j ) {
579                 int nbytes;
580                 sgReadInt( fp, &nbytes );
581                 cout << "element size = " << nbytes << endl;
582                 char buf[nbytes];
583                 char *ptr = buf;
584                 sgReadBytes( fp, nbytes, ptr );
585                 int count = nbytes / (sizeof(short) * 2 * 3);
586                 short *sptr = (short *)ptr;
587                 int_list vs, tcs;
588                 vs.clear(); tcs.clear();
589                 for ( k = 0; k < count; ++k ) {
590                     vs.push_back( sptr[0] );
591                     tcs.push_back( sptr[1] );
592                     cout << sptr[0] << "/" << sptr[1] << " ";
593                     sptr += 2;
594                 }
595                 cout << endl;
596                 strips_v.push_back( vs );
597                 strips_tc.push_back( tcs );
598             }
599         } else if ( obj_type == SG_TRIANGLE_FANS ) {
600             // read triangle fan properties
601             for ( j = 0; j < nproperties; ++j ) {
602                 char prop_type;
603                 sgReadChar( fp, &prop_type );
604
605                 int nbytes;
606                 sgReadInt( fp, &nbytes );
607                 cout << "property size = " << nbytes << endl;
608                 char buf[nbytes];
609                 char *ptr = buf;
610                 sgReadBytes( fp, nbytes, ptr );
611                 if ( prop_type == SG_MATERIAL ) {
612                     char material[256];
613                     strncpy( material, ptr, nbytes );
614                     material[nbytes] = '\0';
615                     cout << "material type = " << material << endl;
616                     fan_materials.push_back( material );
617                 }
618             }
619
620             // read triangle fan elements
621             for ( j = 0; j < nelements; ++j ) {
622                 int nbytes;
623                 sgReadInt( fp, &nbytes );
624                 cout << "element size = " << nbytes << endl;
625                 char buf[nbytes];
626                 char *ptr = buf;
627                 sgReadBytes( fp, nbytes, ptr );
628                 int count = nbytes / (sizeof(short) * 2 * 3);
629                 short *sptr = (short *)ptr;
630                 int_list vs, tcs;
631                 vs.clear(); tcs.clear();
632                 for ( k = 0; k < count; ++k ) {
633                     vs.push_back( sptr[0] );
634                     tcs.push_back( sptr[1] );
635                     cout << sptr[0] << "/" << sptr[1] << " ";
636                     sptr += 2;
637                 }
638                 cout << endl;
639                 fans_v.push_back( vs );
640                 fans_tc.push_back( tcs );
641             }
642         } else {
643             // unknown object type, just skip
644
645             // read properties
646             for ( j = 0; j < nproperties; ++j ) {
647                 char prop_type;
648                 sgReadChar( fp, &prop_type );
649
650                 int nbytes;
651                 sgReadInt( fp, &nbytes );
652                 cout << "property size = " << nbytes << endl;
653                 char buf[nbytes];
654                 char *ptr = buf;
655                 sgReadBytes( fp, nbytes, ptr );
656             }
657
658             // read elements
659             for ( j = 0; j < nelements; ++j ) {
660                 int nbytes;
661                 sgReadInt( fp, &nbytes );
662                 cout << "element size = " << nbytes << endl;
663                 char buf[nbytes];
664                 char *ptr = buf;
665                 sgReadBytes( fp, nbytes, ptr );
666             }
667         }
668     }
669
670     // close the file
671     gzclose(fp);
672 }
673
674
675 // write out the structures to a binary file.  We assume that the
676 // groups come to us sorted by material property.  If not, things
677 // don't break, but the result won't be as optimal.
678 void sgWriteBinObj( const string& base, const string& name, const SGBucket& b,
679                     Point3D gbs_center, float gbs_radius,
680                     const point_list& wgs84_nodes, const point_list& normals,
681                     const point_list& texcoords, 
682                     const group_list& tris_v, const group_list& tris_tc, 
683                     const string_list& tri_materials,
684                     const group_list& strips_v, const group_list& strips_tc, 
685                     const string_list& strip_materials,
686                     const group_list& fans_v, const group_list& fans_tc,
687                     const string_list& fan_materials )
688 {
689     Point3D p;
690     sgVec2 t;
691     sgVec3 pt;
692     int i, j;
693
694     string dir = base + "/" + b.gen_base_path();
695     string command = "mkdir -p " + dir;
696 #ifdef _MSC_VER
697     fg_mkdir( dir.c_str() );
698 #else
699     system(command.c_str());
700 #endif
701
702     string file = dir + "/" + name + ".gz";
703     cout << "Output file = " << file << endl;
704
705     gzFile fp;
706     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
707         cout << "ERROR: opening " << file << " for writing!" << endl;
708         exit(-1);
709     }
710
711     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
712          << tri_materials.size() << endl;
713     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
714          << strip_materials.size() << endl;
715     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
716          << fan_materials.size() << endl;
717
718     cout << "points = " << wgs84_nodes.size() << endl;
719     cout << "tex coords = " << texcoords.size() << endl;
720
721     // write header magic
722     sgWriteInt( fp, SG_FILE_MAGIC_NUMBER );
723     time_t calendar_time = time(NULL);
724     sgWriteLong( fp, (long int)calendar_time );
725
726     // calculate and write number of top level objects
727     string material;
728     int start;
729     int end;
730     short nobjects = 0;
731     nobjects++;                 // for gbs
732     nobjects++;                 // for vertices
733     nobjects++;                 // for normals
734     nobjects++;                 // for texcoords
735
736     // tris
737     short ntris = 0;
738     start = 0; end = 1;
739     while ( start < (int)tri_materials.size() ) {
740         material = tri_materials[start];
741         while ( (end < (int)tri_materials.size()) &&
742                 (material == tri_materials[end]) ) {
743             end++;
744         }
745         ntris++;
746         start = end; end = start + 1;
747     }
748     nobjects += ntris;
749
750     // strips
751     short nstrips = 0;
752     start = 0; end = 1;
753     while ( start < (int)strip_materials.size() ) {
754         material = strip_materials[start];
755         while ( (end < (int)strip_materials.size()) &&
756                 (material == strip_materials[end]) ) {
757             end++;
758         }
759         nstrips++;
760         start = end; end = start + 1;
761     }
762     nobjects += nstrips;
763
764     // fans
765     short nfans = 0;
766     start = 0; end = 1;
767     while ( start < (int)fan_materials.size() ) {
768         material = fan_materials[start];
769         while ( (end < (int)fan_materials.size()) &&
770                 (material == fan_materials[end]) ) {
771             end++;
772         }
773         nfans++;
774         start = end; end = start + 1;
775     }
776     nobjects += nfans;
777
778     cout << "total top level objects = " << nobjects << endl;
779     sgWriteShort( fp, nobjects );
780
781     // write bounding sphere
782     sgWriteChar( fp, (char)SG_BOUNDING_SPHERE );        // type
783     sgWriteShort( fp, 0 );                              // nproperties
784     sgWriteShort( fp, 1 );                              // nelements
785
786     sgWriteInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
787     sgdVec3 center;
788     sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
789     sgWritedVec3( fp, center );
790     sgWriteFloat( fp, gbs_radius );
791
792     // dump vertex list
793     sgWriteChar( fp, (char)SG_VERTEX_LIST );             // type
794     sgWriteShort( fp, 0 );                               // nproperties
795     sgWriteShort( fp, 1 );                               // nelements
796     sgWriteInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
797     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
798         p = wgs84_nodes[i] - gbs_center;
799         sgSetVec3( pt, p.x(), p.y(), p.z() );
800         sgWriteVec3( fp, pt );
801     }
802
803     // dump vertex normal list
804     sgWriteChar( fp, (char)SG_NORMAL_LIST );            // type
805     sgWriteShort( fp, 0 );                              // nproperties
806     sgWriteShort( fp, 1 );                              // nelements
807     sgWriteInt( fp, normals.size() * 3 );               // nbytes
808     char normal[3];
809     for ( i = 0; i < (int)normals.size(); ++i ) {
810         p = normals[i];
811         normal[0] = (char)(p.x() * 256);
812         normal[1] = (char)(p.y() * 256);
813         normal[2] = (char)(p.z() * 256);
814         sgWriteBytes( fp, 3, normal );
815     }
816
817     // dump texture coordinates
818     sgWriteChar( fp, (char)SG_TEXCOORD_LIST );          // type
819     sgWriteShort( fp, 0 );                              // nproperties
820     sgWriteShort( fp, 1 );                              // nelements
821     sgWriteInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
822     for ( i = 0; i < (int)texcoords.size(); ++i ) {
823         p = texcoords[i];
824         sgSetVec2( t, p.x(), p.y() );
825         sgWriteVec2( fp, t );
826     }
827
828     // dump individual triangles if they exist
829     if ( tris_v.size() > 0 ) {
830         int start = 0;
831         int end = 1;
832         string material;
833         while ( start < (int)tri_materials.size() ) {
834             // find next group
835             material = tri_materials[start];
836             while ( (end < (int)tri_materials.size()) && 
837                     (material == tri_materials[end]) )
838             {
839                 // cout << "end = " << end << endl;
840                 end++;
841             }
842             // cout << "group = " << start << " to " << end - 1 << endl;
843
844             // write group headers
845             sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
846             sgWriteShort( fp, 1 );                      // nproperties
847             sgWriteShort( fp, 1 );                      // nelements
848
849             sgWriteChar( fp, (char)SG_MATERIAL );       // property
850             sgWriteInt( fp, material.length() );        // nbytes
851             sgWriteBytes( fp, material.length(), material.c_str() );
852
853             sgWriteInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
854
855             // write group
856             for ( i = start; i < end; ++i ) {
857                 for ( j = 0; j < 3; ++j ) {
858                     sgWriteShort( fp, (short)tris_v[i][j] );
859                     sgWriteShort( fp, (short)tris_tc[i][j] );
860                 }
861             }
862
863             start = end;
864             end = start + 1;
865         }
866     }
867
868     // dump triangle strips
869     if ( strips_v.size() > 0 ) {
870         int start = 0;
871         int end = 1;
872         string material;
873         while ( start < (int)strip_materials.size() ) {
874             // find next group
875             material = strip_materials[start];
876             while ( (end < (int)strip_materials.size()) && 
877                     (material == strip_materials[end]) )
878                 {
879                     // cout << "end = " << end << endl;
880                     end++;
881                 }
882             // cout << "group = " << start << " to " << end - 1 << endl;
883
884             // write group headers
885             sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
886             sgWriteShort( fp, 1 );                       // nproperties
887             sgWriteShort( fp, end - start );             // nelements
888
889             sgWriteChar( fp, (char)SG_MATERIAL );       // property
890             sgWriteInt( fp, material.length() );        // nbytes
891             sgWriteBytes( fp, material.length(), material.c_str() );
892
893             // write strips
894             for ( i = start; i < end; ++i ) {
895                 // nbytes
896                 sgWriteInt( fp, strips_v[i].size() * 2 * sizeof(short) );
897                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
898                     sgWriteShort( fp, (short)strips_v[i][j] );
899                     sgWriteShort( fp, (short)strips_tc[i][j] );
900                 }
901             }
902             
903             start = end;
904             end = start + 1;
905         }
906     }
907
908     // dump triangle fans
909     if ( fans_v.size() > 0 ) {
910         int start = 0;
911         int end = 1;
912         string material;
913         while ( start < (int)fan_materials.size() ) {
914             // find next group
915             material = fan_materials[start];
916             while ( (end < (int)fan_materials.size()) && 
917                     (material == fan_materials[end]) )
918                 {
919                     // cout << "end = " << end << endl;
920                     end++;
921                 }
922             // cout << "group = " << start << " to " << end - 1 << endl;
923
924             // write group headers
925             sgWriteChar( fp, (char)SG_TRIANGLE_FANS );   // type
926             sgWriteShort( fp, 1 );                       // nproperties
927             sgWriteShort( fp, end - start );             // nelements
928
929             sgWriteChar( fp, (char)SG_MATERIAL );       // property
930             sgWriteInt( fp, material.length() );        // nbytes
931             sgWriteBytes( fp, material.length(), material.c_str() );
932
933             // write fans
934             for ( i = start; i < end; ++i ) {
935                 // nbytes
936                 sgWriteInt( fp, fans_v[i].size() * 2 * sizeof(short) );
937                 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
938                     sgWriteShort( fp, (short)fans_v[i][j] );
939                     sgWriteShort( fp, (short)fans_tc[i][j] );
940                 }
941             }
942             
943             start = end;
944             end = start + 1;
945         }
946     }
947
948     // close the file
949     gzclose(fp);
950 }