]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
41c4707243a66d2b610f362463934a6e60f26eab
[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 bool 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         return false;
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     return true;
304 }
305
306
307 // read a binary file and populate the provided structures.
308 bool sgReadBinObj( const string& file,
309                    Point3D &gbs_center, float *gbs_radius,
310                    point_list& wgs84_nodes, point_list& normals,
311                    point_list& texcoords, 
312                    group_list& tris_v, group_list& tris_tc, 
313                    string_list& tri_materials,
314                    group_list& strips_v, group_list& strips_tc, 
315                    string_list& strip_materials,
316                    group_list& fans_v, group_list& fans_tc,
317                    string_list& fan_materials )
318 {
319     Point3D p;
320     int i, j, k;
321     char material[256];
322
323     // zero out structures
324     wgs84_nodes.clear();
325     normals.clear();
326     texcoords.clear();
327
328     tris_v.clear();
329     tris_tc.clear();
330     tri_materials.clear();
331
332     strips_v.clear();
333     strips_tc.clear();
334     strip_materials.clear();
335
336     fans_v.clear();
337     fans_tc.clear();
338     fan_materials.clear();
339    
340     cout << "Loading binary input file = " << file << endl;
341
342     gzFile fp;
343     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
344         string filegz = file + ".gz";
345         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
346             cout << "ERROR: opening " << file << " or " << filegz
347                  << "for reading!" << endl;
348             return false;
349         }
350     }
351
352     sgClearReadError();
353
354     // read headers
355     int header, version;
356     sgReadInt( fp, &header );
357     if ( ((header & 0xFF000000) >> 24) == 'S' &&
358          ((header & 0x00FF0000) >> 16) == 'G' ) {
359         cout << "Good header" << endl;
360         // read file version
361         version = (header & 0x0000FFFF);
362         cout << "File version = " << version << endl;
363     } else {
364         return false;
365     }
366
367     // read creation time
368     time_t calendar_time;
369     sgReadLong( fp, &calendar_time );
370     struct tm *local_tm;
371     local_tm = localtime( &calendar_time );
372     char time_str[256];
373     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
374     cout << "File created on " << time_str << endl;
375
376     // read number of top level objects
377     short nobjects;
378     sgReadShort( fp, &nobjects );
379     cout << "Total objects to read = " << nobjects << endl;
380
381     // read in objects
382     for ( i = 0; i < nobjects; ++i ) {
383         // read object header
384         char obj_type;
385         short nproperties, nelements;
386         sgReadChar( fp, &obj_type );
387         sgReadShort( fp, &nproperties );
388         sgReadShort( fp, &nelements );
389
390         cout << "object " << i << " = " << (int)obj_type << " props = "
391              << nproperties << " elements = " << nelements << endl;
392             
393         if ( obj_type == SG_BOUNDING_SPHERE ) {
394             // read bounding sphere properties
395             for ( j = 0; j < nproperties; ++j ) {
396                 char prop_type;
397                 sgReadChar( fp, &prop_type );
398
399                 int nbytes;
400                 sgReadInt( fp, &nbytes );
401                 cout << "property size = " << nbytes << endl;
402                 char buf[nbytes];
403                 char *ptr = buf;
404                 sgReadBytes( fp, nbytes, ptr );
405             }
406
407             // read bounding sphere elements
408             for ( j = 0; j < nelements; ++j ) {
409                 int nbytes;
410                 sgReadInt( fp, &nbytes );
411                 cout << "element size = " << nbytes << endl;
412                 char buf[nbytes];
413                 char *ptr = buf;
414                 sgReadBytes( fp, nbytes, ptr );
415
416                 double *dptr = (double *)ptr;
417                 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
418                 cout << "Center = " << gbs_center << endl;
419                 ptr += sizeof(double) * 3;
420                 
421                 float *fptr = (float *)ptr;
422                 *gbs_radius = fptr[0];
423                 cout << "Bounding radius = " << *gbs_radius << endl;
424             }
425         } else if ( obj_type == SG_VERTEX_LIST ) {
426             // read vertex list properties
427             for ( j = 0; j < nproperties; ++j ) {
428                 char prop_type;
429                 sgReadChar( fp, &prop_type );
430
431                 int nbytes;
432                 sgReadInt( fp, &nbytes );
433                 cout << "property size = " << nbytes << endl;
434                 char buf[nbytes];
435                 char *ptr = buf;
436                 sgReadBytes( fp, nbytes, ptr );
437             }
438
439             // read vertex list elements
440             for ( j = 0; j < nelements; ++j ) {
441                 int nbytes;
442                 sgReadInt( fp, &nbytes );
443                 cout << "element size = " << nbytes << endl;
444                 char buf[nbytes];
445                 char *ptr = buf;
446                 sgReadBytes( fp, nbytes, ptr );
447                 int count = nbytes / (sizeof(float) * 3);
448                 float *fptr = (float *)ptr;
449                 for ( k = 0; k < count; ++k ) {
450                     p = Point3D( fptr[0], fptr[1], fptr[2] );
451                     cout << "node = " << p << endl;
452                     wgs84_nodes.push_back( p );
453                     fptr += 3;
454                 }
455             }
456         } else if ( obj_type == SG_NORMAL_LIST ) {
457             // read normal list properties
458             for ( j = 0; j < nproperties; ++j ) {
459                 char prop_type;
460                 sgReadChar( fp, &prop_type );
461
462                 int nbytes;
463                 sgReadInt( fp, &nbytes );
464                 cout << "property size = " << nbytes << endl;
465                 char buf[nbytes];
466                 char *ptr = buf;
467                 sgReadBytes( fp, nbytes, ptr );
468             }
469
470             // read normal list elements
471             for ( j = 0; j < nelements; ++j ) {
472                 int nbytes;
473                 sgReadInt( fp, &nbytes );
474                 cout << "element size = " << nbytes << endl;
475                 char buf[nbytes];
476                 char *ptr = buf;
477                 sgReadBytes( fp, nbytes, ptr );
478                 int count = nbytes / 3;
479                 for ( k = 0; k < count; ++k ) {
480                     p = Point3D( ptr[0] / 128.0 - 1.0,
481                                  ptr[1] / 128.0 - 1.0,
482                                  ptr[2] / 128.0 - 1.0 );
483                     cout << "normal = " << p << endl;
484                     normals.push_back( p );
485                     ptr += 3;
486                 }
487             }
488         } else if ( obj_type == SG_TEXCOORD_LIST ) {
489             // read texcoord list properties
490             for ( j = 0; j < nproperties; ++j ) {
491                 char prop_type;
492                 sgReadChar( fp, &prop_type );
493
494                 int nbytes;
495                 sgReadInt( fp, &nbytes );
496                 cout << "property size = " << nbytes << endl;
497                 char buf[nbytes];
498                 char *ptr = buf;
499                 sgReadBytes( fp, nbytes, ptr );
500             }
501
502             // read texcoord list elements
503             for ( j = 0; j < nelements; ++j ) {
504                 int nbytes;
505                 sgReadInt( fp, &nbytes );
506                 cout << "element size = " << nbytes << endl;
507                 char buf[nbytes];
508                 char *ptr = buf;
509                 sgReadBytes( fp, nbytes, ptr );
510                 int count = nbytes / (sizeof(float) * 2);
511                 float *fptr = (float *)ptr;
512                 for ( k = 0; k < count; ++k ) {
513                     p = Point3D( fptr[0], fptr[1], 0 );
514                     cout << "texcoord = " << p << endl;
515                     texcoords.push_back( p );
516                     fptr += 2;
517                 }
518             }
519         } else if ( obj_type == SG_TRIANGLE_FACES ) {
520             // read triangle face properties
521             for ( j = 0; j < nproperties; ++j ) {
522                 char prop_type;
523                 sgReadChar( fp, &prop_type );
524
525                 int nbytes;
526                 sgReadInt( fp, &nbytes );
527                 cout << "property size = " << nbytes << endl;
528                 char buf[nbytes];
529                 char *ptr = buf;
530                 sgReadBytes( fp, nbytes, ptr );
531                 if ( prop_type == SG_MATERIAL ) {
532                     strncpy( material, ptr, nbytes );
533                     material[nbytes] = '\0';
534                     cout << "material type = " << material << endl;
535                 }
536             }
537
538             // read triangle face elements
539             for ( j = 0; j < nelements; ++j ) {
540                 int nbytes;
541                 sgReadInt( fp, &nbytes );
542                 cout << "element size = " << nbytes << endl;
543                 char buf[nbytes];
544                 char *ptr = buf;
545                 sgReadBytes( fp, nbytes, ptr );
546                 int count = nbytes / (sizeof(short) * 2);
547                 short *sptr = (short *)ptr;
548                 int_list vs, tcs;
549                 vs.clear(); tcs.clear();
550                 for ( k = 0; k < count; ++k ) {
551                     vs.push_back( sptr[0] );
552                     tcs.push_back( sptr[1] );
553                     cout << sptr[0] << "/" << sptr[1] << " ";
554                     sptr += 2;
555                 }
556                 cout << endl;
557                 tris_v.push_back( vs );
558                 tris_tc.push_back( tcs );
559                 tri_materials.push_back( material );
560             }
561         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
562             // read triangle strip properties
563             for ( j = 0; j < nproperties; ++j ) {
564                 char prop_type;
565                 sgReadChar( fp, &prop_type );
566
567                 int nbytes;
568                 sgReadInt( fp, &nbytes );
569                 cout << "property size = " << nbytes << endl;
570                 char buf[nbytes];
571                 char *ptr = buf;
572                 sgReadBytes( fp, nbytes, ptr );
573                 if ( prop_type == SG_MATERIAL ) {
574                     strncpy( material, ptr, nbytes );
575                     material[nbytes] = '\0';
576                     cout << "material type = " << material << endl;
577                 }
578             }
579
580             // read triangle strip elements
581             for ( j = 0; j < nelements; ++j ) {
582                 int nbytes;
583                 sgReadInt( fp, &nbytes );
584                 cout << "element size = " << nbytes << endl;
585                 char buf[nbytes];
586                 char *ptr = buf;
587                 sgReadBytes( fp, nbytes, ptr );
588                 int count = nbytes / (sizeof(short) * 2);
589                 short *sptr = (short *)ptr;
590                 int_list vs, tcs;
591                 vs.clear(); tcs.clear();
592                 for ( k = 0; k < count; ++k ) {
593                     vs.push_back( sptr[0] );
594                     tcs.push_back( sptr[1] );
595                     cout << sptr[0] << "/" << sptr[1] << " ";
596                     sptr += 2;
597                 }
598                 cout << endl;
599                 strips_v.push_back( vs );
600                 strips_tc.push_back( tcs );
601                 strip_materials.push_back( material );
602             }
603         } else if ( obj_type == SG_TRIANGLE_FANS ) {
604             // read triangle fan properties
605             for ( j = 0; j < nproperties; ++j ) {
606                 char prop_type;
607                 sgReadChar( fp, &prop_type );
608
609                 int nbytes;
610                 sgReadInt( fp, &nbytes );
611                 cout << "property size = " << nbytes << endl;
612                 char buf[nbytes];
613                 char *ptr = buf;
614                 sgReadBytes( fp, nbytes, ptr );
615                 if ( prop_type == SG_MATERIAL ) {
616                     strncpy( material, ptr, nbytes );
617                     material[nbytes] = '\0';
618                     cout << "material type = " << material << endl;
619                 }
620             }
621
622             // read triangle fan elements
623             for ( j = 0; j < nelements; ++j ) {
624                 int nbytes;
625                 sgReadInt( fp, &nbytes );
626                 cout << "element size = " << nbytes << endl;
627                 char buf[nbytes];
628                 char *ptr = buf;
629                 sgReadBytes( fp, nbytes, ptr );
630                 int count = nbytes / (sizeof(short) * 2);
631                 short *sptr = (short *)ptr;
632                 int_list vs, tcs;
633                 vs.clear(); tcs.clear();
634                 for ( k = 0; k < count; ++k ) {
635                     vs.push_back( sptr[0] );
636                     tcs.push_back( sptr[1] );
637                     cout << sptr[0] << "/" << sptr[1] << " ";
638                     sptr += 2;
639                 }
640                 cout << endl;
641                 fans_v.push_back( vs );
642                 fans_tc.push_back( tcs );
643                 fan_materials.push_back( material );
644             }
645         } else {
646             // unknown object type, just skip
647
648             // read properties
649             for ( j = 0; j < nproperties; ++j ) {
650                 char prop_type;
651                 sgReadChar( fp, &prop_type );
652
653                 int nbytes;
654                 sgReadInt( fp, &nbytes );
655                 cout << "property size = " << nbytes << endl;
656                 char buf[nbytes];
657                 char *ptr = buf;
658                 sgReadBytes( fp, nbytes, ptr );
659             }
660
661             // read elements
662             for ( j = 0; j < nelements; ++j ) {
663                 int nbytes;
664                 sgReadInt( fp, &nbytes );
665                 cout << "element size = " << nbytes << endl;
666                 char buf[nbytes];
667                 char *ptr = buf;
668                 sgReadBytes( fp, nbytes, ptr );
669             }
670         }
671     }
672
673     // close the file
674     gzclose(fp);
675
676     if ( sgReadError() ) {
677         cout << "We detected an error while reading the file." << endl;
678         return false;
679     }
680
681     return true;
682 }
683
684
685 // write out the structures to a binary file.  We assume that the
686 // groups come to us sorted by material property.  If not, things
687 // don't break, but the result won't be as optimal.
688 bool sgWriteBinObj( const string& base, const string& name, const SGBucket& b,
689                     Point3D gbs_center, float gbs_radius,
690                     const point_list& wgs84_nodes, const point_list& normals,
691                     const point_list& texcoords, 
692                     const group_list& tris_v, const group_list& tris_tc, 
693                     const string_list& tri_materials,
694                     const group_list& strips_v, const group_list& strips_tc, 
695                     const string_list& strip_materials,
696                     const group_list& fans_v, const group_list& fans_tc,
697                     const string_list& fan_materials )
698 {
699     Point3D p;
700     sgVec2 t;
701     sgVec3 pt;
702     int i, j;
703
704     string dir = base + "/" + b.gen_base_path();
705     string command = "mkdir -p " + dir;
706 #ifdef _MSC_VER
707     fg_mkdir( dir.c_str() );
708 #else
709     system(command.c_str());
710 #endif
711
712     string file = dir + "/" + name + ".gz";
713     cout << "Output file = " << file << endl;
714
715     gzFile fp;
716     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
717         cout << "ERROR: opening " << file << " for writing!" << endl;
718         return false;
719     }
720
721     sgClearWriteError();
722
723     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
724          << tri_materials.size() << endl;
725     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
726          << strip_materials.size() << endl;
727     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
728          << fan_materials.size() << endl;
729
730     cout << "points = " << wgs84_nodes.size() << endl;
731     cout << "tex coords = " << texcoords.size() << endl;
732
733     // write header magic
734     sgWriteInt( fp, SG_FILE_MAGIC_NUMBER );
735     time_t calendar_time = time(NULL);
736     sgWriteLong( fp, (long int)calendar_time );
737
738     // calculate and write number of top level objects
739     string material;
740     int start;
741     int end;
742     short nobjects = 0;
743     nobjects++;                 // for gbs
744     nobjects++;                 // for vertices
745     nobjects++;                 // for normals
746     nobjects++;                 // for texcoords
747
748     // tris
749     short ntris = 0;
750     start = 0; end = 1;
751     while ( start < (int)tri_materials.size() ) {
752         material = tri_materials[start];
753         while ( (end < (int)tri_materials.size()) &&
754                 (material == tri_materials[end]) ) {
755             end++;
756         }
757         ntris++;
758         start = end; end = start + 1;
759     }
760     nobjects += ntris;
761
762     // strips
763     short nstrips = 0;
764     start = 0; end = 1;
765     while ( start < (int)strip_materials.size() ) {
766         material = strip_materials[start];
767         while ( (end < (int)strip_materials.size()) &&
768                 (material == strip_materials[end]) ) {
769             end++;
770         }
771         nstrips++;
772         start = end; end = start + 1;
773     }
774     nobjects += nstrips;
775
776     // fans
777     short nfans = 0;
778     start = 0; end = 1;
779     while ( start < (int)fan_materials.size() ) {
780         material = fan_materials[start];
781         while ( (end < (int)fan_materials.size()) &&
782                 (material == fan_materials[end]) ) {
783             end++;
784         }
785         nfans++;
786         start = end; end = start + 1;
787     }
788     nobjects += nfans;
789
790     cout << "total top level objects = " << nobjects << endl;
791     sgWriteShort( fp, nobjects );
792
793     // write bounding sphere
794     sgWriteChar( fp, (char)SG_BOUNDING_SPHERE );        // type
795     sgWriteShort( fp, 0 );                              // nproperties
796     sgWriteShort( fp, 1 );                              // nelements
797
798     sgWriteInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
799     sgdVec3 center;
800     sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
801     sgWritedVec3( fp, center );
802     sgWriteFloat( fp, gbs_radius );
803
804     // dump vertex list
805     sgWriteChar( fp, (char)SG_VERTEX_LIST );             // type
806     sgWriteShort( fp, 0 );                               // nproperties
807     sgWriteShort( fp, 1 );                               // nelements
808     sgWriteInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
809     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
810         p = wgs84_nodes[i] - gbs_center;
811         sgSetVec3( pt, p.x(), p.y(), p.z() );
812         sgWriteVec3( fp, pt );
813     }
814
815     // dump vertex normal list
816     sgWriteChar( fp, (char)SG_NORMAL_LIST );            // type
817     sgWriteShort( fp, 0 );                              // nproperties
818     sgWriteShort( fp, 1 );                              // nelements
819     sgWriteInt( fp, normals.size() * 3 );               // nbytes
820     char normal[3];
821     for ( i = 0; i < (int)normals.size(); ++i ) {
822         p = normals[i];
823         normal[0] = (char)((p.x() + 1.0) * 128);
824         normal[1] = (char)((p.y() + 1.0) * 128);
825         normal[2] = (char)((p.z() + 1.0) * 128);
826         sgWriteBytes( fp, 3, normal );
827     }
828
829     // dump texture coordinates
830     sgWriteChar( fp, (char)SG_TEXCOORD_LIST );          // type
831     sgWriteShort( fp, 0 );                              // nproperties
832     sgWriteShort( fp, 1 );                              // nelements
833     sgWriteInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
834     for ( i = 0; i < (int)texcoords.size(); ++i ) {
835         p = texcoords[i];
836         sgSetVec2( t, p.x(), p.y() );
837         sgWriteVec2( fp, t );
838     }
839
840     // dump individual triangles if they exist
841     if ( tris_v.size() > 0 ) {
842         int start = 0;
843         int end = 1;
844         string material;
845         while ( start < (int)tri_materials.size() ) {
846             // find next group
847             material = tri_materials[start];
848             while ( (end < (int)tri_materials.size()) && 
849                     (material == tri_materials[end]) )
850             {
851                 // cout << "end = " << end << endl;
852                 end++;
853             }
854             // cout << "group = " << start << " to " << end - 1 << endl;
855
856             // write group headers
857             sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
858             sgWriteShort( fp, 1 );                      // nproperties
859             sgWriteShort( fp, 1 );                      // nelements
860
861             sgWriteChar( fp, (char)SG_MATERIAL );       // property
862             sgWriteInt( fp, material.length() );        // nbytes
863             sgWriteBytes( fp, material.length(), material.c_str() );
864
865             sgWriteInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
866
867             // write group
868             for ( i = start; i < end; ++i ) {
869                 for ( j = 0; j < 3; ++j ) {
870                     sgWriteShort( fp, (short)tris_v[i][j] );
871                     sgWriteShort( fp, (short)tris_tc[i][j] );
872                 }
873             }
874
875             start = end;
876             end = start + 1;
877         }
878     }
879
880     // dump triangle strips
881     if ( strips_v.size() > 0 ) {
882         int start = 0;
883         int end = 1;
884         string material;
885         while ( start < (int)strip_materials.size() ) {
886             // find next group
887             material = strip_materials[start];
888             while ( (end < (int)strip_materials.size()) && 
889                     (material == strip_materials[end]) )
890                 {
891                     // cout << "end = " << end << endl;
892                     end++;
893                 }
894             // cout << "group = " << start << " to " << end - 1 << endl;
895
896             // write group headers
897             sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
898             sgWriteShort( fp, 1 );                       // nproperties
899             sgWriteShort( fp, end - start );             // nelements
900
901             sgWriteChar( fp, (char)SG_MATERIAL );       // property
902             sgWriteInt( fp, material.length() );        // nbytes
903             sgWriteBytes( fp, material.length(), material.c_str() );
904
905             // write strips
906             for ( i = start; i < end; ++i ) {
907                 // nbytes
908                 sgWriteInt( fp, strips_v[i].size() * 2 * sizeof(short) );
909                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
910                     sgWriteShort( fp, (short)strips_v[i][j] );
911                     sgWriteShort( fp, (short)strips_tc[i][j] );
912                 }
913             }
914             
915             start = end;
916             end = start + 1;
917         }
918     }
919
920     // dump triangle fans
921     if ( fans_v.size() > 0 ) {
922         int start = 0;
923         int end = 1;
924         string material;
925         while ( start < (int)fan_materials.size() ) {
926             // find next group
927             material = fan_materials[start];
928             while ( (end < (int)fan_materials.size()) && 
929                     (material == fan_materials[end]) )
930                 {
931                     // cout << "end = " << end << endl;
932                     end++;
933                 }
934             // cout << "group = " << start << " to " << end - 1 << endl;
935
936             // write group headers
937             sgWriteChar( fp, (char)SG_TRIANGLE_FANS );   // type
938             sgWriteShort( fp, 1 );                       // nproperties
939             sgWriteShort( fp, end - start );             // nelements
940
941             sgWriteChar( fp, (char)SG_MATERIAL );       // property
942             sgWriteInt( fp, material.length() );        // nbytes
943             sgWriteBytes( fp, material.length(), material.c_str() );
944
945             // write fans
946             for ( i = start; i < end; ++i ) {
947                 // nbytes
948                 sgWriteInt( fp, fans_v[i].size() * 2 * sizeof(short) );
949                 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
950                     sgWriteShort( fp, (short)fans_v[i][j] );
951                     sgWriteShort( fp, (short)fans_tc[i][j] );
952                 }
953             }
954             
955             start = end;
956             end = start + 1;
957         }
958     }
959
960     // close the file
961     gzclose(fp);
962
963     if ( sgWriteError() ) {
964         cout << "We detected an error while writing the file." << endl;
965         return false;
966     }
967
968     return true;
969 }