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