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