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