]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
Added support for points objects.
[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 #include <simgear/compiler.h>
26
27 #include <stdio.h>
28 #include <time.h>
29
30 #include <vector>
31 #include STL_STRING
32
33 #include <simgear/bucket/newbucket.hxx>
34
35 #include "lowlevel.hxx"
36 #include "sg_binobj.hxx"
37
38
39 SG_USING_STD( string );
40 SG_USING_STD( vector );
41
42 #if !defined (SG_HAVE_NATIVE_SGI_COMPILERS)
43 SG_USING_STD( cout );
44 SG_USING_STD( endl );
45 #endif
46
47
48 enum {
49     SG_BOUNDING_SPHERE = 0,
50
51     SG_VERTEX_LIST = 1,
52     SG_COLOR_LIST = 4,
53     SG_NORMAL_LIST = 2,
54     SG_TEXCOORD_LIST = 3,
55
56     SG_POINTS = 9,
57
58     SG_TRIANGLE_FACES = 10,
59     SG_TRIANGLE_STRIPS = 11,
60     SG_TRIANGLE_FANS = 12
61 } tgObjectTypes;
62
63 enum {
64     SG_MATERIAL = 0
65 } tgPropertyTypes;
66
67
68 class sgSimpleBuffer {
69
70 private:
71
72     char *ptr;
73     unsigned int size;
74
75 public:
76
77     inline sgSimpleBuffer( unsigned int s )
78     {
79         size = 1;
80         while ( size < s ) {
81             size *= 2;
82         }
83         cout << "Creating a new buffer of size = " << size << endl;
84         ptr = new char[size];
85     }
86
87     inline ~sgSimpleBuffer() {
88         delete [] ptr;
89     }
90
91     inline unsigned int get_size() const { return size; }
92     inline char *get_ptr() const { return ptr; }
93     inline void resize( unsigned int s ) {
94         if ( s > size ) {
95             if ( ptr != NULL ) {
96                 delete [] ptr;
97             }
98             while ( size < s ) {
99                 size *= 2;
100             }
101             cout << "resizing buffer to size = " << size << endl;
102             ptr = new char[size];
103         }
104     }
105 };
106
107
108 // calculate the center of a list of points, by taking the halfway
109 // point between the min and max points.
110 static Point3D calc_center( point_list& wgs84_nodes ) {
111     Point3D p, min, max;
112
113     if ( wgs84_nodes.size() ) {
114         min = max = wgs84_nodes[0];
115     } else {
116         min = max = Point3D( 0 );
117     }
118
119     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
120         p = wgs84_nodes[i];
121
122         if ( p.x() < min.x() ) { min.setx( p.x() ); }
123         if ( p.y() < min.y() ) { min.sety( p.y() ); }
124         if ( p.z() < min.z() ) { min.setz( p.z() ); }
125
126         if ( p.x() > max.x() ) { max.setx( p.x() ); }
127         if ( p.y() > max.y() ) { max.sety( p.y() ); }
128         if ( p.z() > max.z() ) { max.setz( p.z() ); }
129     }
130
131     return ( min + max ) / 2.0;
132 }
133
134 // calculate the bounding sphere.  Center is the center of the
135 // tile and zero elevation
136 double sgCalcBoundingRadius( Point3D center, point_list& wgs84_nodes ) {
137     double dist_squared;
138     double radius_squared = 0;
139     
140     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
141         dist_squared = center.distance3Dsquared( wgs84_nodes[i] );
142         if ( dist_squared > radius_squared ) {
143             radius_squared = dist_squared;
144         }
145     }
146
147     return sqrt(radius_squared);
148 }
149
150
151 // read a binary file and populate the provided structures.
152 bool SGBinObject::read_bin( const string& file ) {
153     Point3D p;
154     int i, j, k;
155     char material[256];
156     unsigned int nbytes;
157     static sgSimpleBuffer buf( 32768 );  // 32 Kb
158
159     // zero out structures
160     gbs_center = Point3D( 0 );
161     gbs_radius = 0.0;
162
163     wgs84_nodes.clear();
164     normals.clear();
165     texcoords.clear();
166
167     pts_v.clear();
168     pt_materials.clear();
169
170     tris_v.clear();
171     tris_tc.clear();
172     tri_materials.clear();
173
174     strips_v.clear();
175     strips_tc.clear();
176     strip_materials.clear();
177
178     fans_v.clear();
179     fans_tc.clear();
180     fan_materials.clear();
181    
182     gzFile fp;
183     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
184         string filegz = file + ".gz";
185         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
186             // cout << "ERROR: opening " << file << " or " << filegz
187             //      << "for reading!" << endl;
188
189             return false;
190         }
191     }
192
193     sgClearReadError();
194
195     // read headers
196     unsigned int header;
197     sgReadUInt( fp, &header );
198     if ( ((header & 0xFF000000) >> 24) == 'S' &&
199          ((header & 0x00FF0000) >> 16) == 'G' ) {
200         // cout << "Good header" << endl;
201         // read file version
202         version = (header & 0x0000FFFF);
203         // cout << "File version = " << version << endl;
204     } else {
205         // close the file before we return
206         gzclose(fp);
207
208         return false;
209     }
210
211     // read creation time
212     time_t calendar_time;
213     sgReadLong( fp, &calendar_time );
214
215 #if 0
216     // The following code has a global effect on the host application
217     // and can screws up the time elsewhere.  It should be avoided
218     // unless you need this for debugging in which case you should
219     // disable it again once the debugging task is finished.
220     struct tm *local_tm;
221     local_tm = localtime( &calendar_time );
222     char time_str[256];
223     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
224     cout << "File created on " << time_str << endl;
225 #endif
226
227     // read number of top level objects
228     short nobjects;
229     sgReadShort( fp, &nobjects );
230     // cout << "Total objects to read = " << nobjects << endl;
231
232     // read in objects
233     for ( i = 0; i < nobjects; ++i ) {
234         // read object header
235         char obj_type;
236         short nproperties, nelements;
237         sgReadChar( fp, &obj_type );
238         sgReadShort( fp, &nproperties );
239         sgReadShort( fp, &nelements );
240
241         // cout << "object " << i << " = " << (int)obj_type << " props = "
242         //      << nproperties << " elements = " << nelements << endl;
243             
244         if ( obj_type == SG_BOUNDING_SPHERE ) {
245             // read bounding sphere properties
246             for ( j = 0; j < nproperties; ++j ) {
247                 char prop_type;
248                 sgReadChar( fp, &prop_type );
249
250                 sgReadUInt( fp, &nbytes );
251                 // cout << "property 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
257             // read bounding sphere elements
258             for ( j = 0; j < nelements; ++j ) {
259                 sgReadUInt( fp, &nbytes );
260                 // cout << "element size = " << nbytes << endl;
261                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
262                 char *ptr = buf.get_ptr();
263                 sgReadBytes( fp, nbytes, ptr );
264
265                 double *dptr = (double *)ptr;
266                 if ( sgIsBigEndian() ) {
267                     sgEndianSwap( (uint64 *)&(dptr[0]) );
268                     sgEndianSwap( (uint64 *)&(dptr[1]) );
269                     sgEndianSwap( (uint64 *)&(dptr[2]) );
270                 }
271                 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
272                 // cout << "Center = " << gbs_center << endl;
273                 ptr += sizeof(double) * 3;
274                 
275                 float *fptr = (float *)ptr;
276                 if ( sgIsBigEndian() ) {
277                     sgEndianSwap( (unsigned int *)fptr );
278                 }
279                 gbs_radius = fptr[0];
280                 // cout << "Bounding radius = " << gbs_radius << endl;
281             }
282         } else if ( obj_type == SG_VERTEX_LIST ) {
283             // read vertex list properties
284             for ( j = 0; j < nproperties; ++j ) {
285                 char prop_type;
286                 sgReadChar( fp, &prop_type );
287
288                 sgReadUInt( fp, &nbytes );
289                 // cout << "property size = " << nbytes << endl;
290                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
291                 char *ptr = buf.get_ptr();
292                 sgReadBytes( fp, nbytes, ptr );
293             }
294
295             // read vertex list elements
296             for ( j = 0; j < nelements; ++j ) {
297                 sgReadUInt( fp, &nbytes );
298                 // cout << "element size = " << nbytes << endl;
299                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
300                 char *ptr = buf.get_ptr();
301                 sgReadBytes( fp, nbytes, ptr );
302                 int count = nbytes / (sizeof(float) * 3);
303                 float *fptr = (float *)ptr;
304                 for ( k = 0; k < count; ++k ) {
305                     if ( sgIsBigEndian() ) {
306                         sgEndianSwap( (unsigned int *)&(fptr[0]) );
307                         sgEndianSwap( (unsigned int *)&(fptr[1]) );
308                         sgEndianSwap( (unsigned int *)&(fptr[2]) );
309                     }
310                     p = Point3D( fptr[0], fptr[1], fptr[2] );
311                     // cout << "node = " << p << endl;
312                     wgs84_nodes.push_back( p );
313                     fptr += 3;
314                 }
315             }
316         } else if ( obj_type == SG_COLOR_LIST ) {
317             // read color list properties
318             for ( j = 0; j < nproperties; ++j ) {
319                 char prop_type;
320                 sgReadChar( fp, &prop_type );
321
322                 sgReadUInt( fp, &nbytes );
323                 // cout << "property size = " << nbytes << endl;
324                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
325                 char *ptr = buf.get_ptr();
326                 sgReadBytes( fp, nbytes, ptr );
327             }
328
329             // read color list elements
330             for ( j = 0; j < nelements; ++j ) {
331                 sgReadUInt( fp, &nbytes );
332                 // cout << "element size = " << nbytes << endl;
333                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
334                 char *ptr = buf.get_ptr();
335                 sgReadBytes( fp, nbytes, ptr );
336                 int count = nbytes / (sizeof(float) * 4);
337                 float *fptr = (float *)ptr;
338                 for ( k = 0; k < count; ++k ) {
339                     if ( sgIsBigEndian() ) {
340                         sgEndianSwap( (unsigned int *)&(fptr[0]) );
341                         sgEndianSwap( (unsigned int *)&(fptr[1]) );
342                         sgEndianSwap( (unsigned int *)&(fptr[2]) );
343                         sgEndianSwap( (unsigned int *)&(fptr[3]) );
344                     }
345                     p = Point3D( fptr[0], fptr[1], fptr[2] );
346                     // cout << "node = " << p << endl;
347                     colors.push_back( p );
348                     fptr += 4;
349                 }
350             }
351         } else if ( obj_type == SG_NORMAL_LIST ) {
352             // read normal list properties
353             for ( j = 0; j < nproperties; ++j ) {
354                 char prop_type;
355                 sgReadChar( fp, &prop_type );
356
357                 sgReadUInt( fp, &nbytes );
358                 // cout << "property size = " << nbytes << endl;
359                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
360                 char *ptr = buf.get_ptr();
361                 sgReadBytes( fp, nbytes, ptr );
362             }
363
364             // read normal list elements
365             for ( j = 0; j < nelements; ++j ) {
366                 sgReadUInt( fp, &nbytes );
367                 // cout << "element size = " << nbytes << endl;
368                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
369                 unsigned char *ptr = (unsigned char *)(buf.get_ptr());
370                 sgReadBytes( fp, nbytes, ptr );
371                 int count = nbytes / 3;
372                 for ( k = 0; k < count; ++k ) {
373                     sgdVec3 normal;
374                     sgdSetVec3( normal,
375                                (ptr[0]) / 127.5 - 1.0,
376                                (ptr[1]) / 127.5 - 1.0,
377                                (ptr[2]) / 127.5 - 1.0 );
378                     sgdNormalizeVec3( normal );
379
380                     p = Point3D( normal[0], normal[1], normal[2] );
381                     // cout << "normal = " << p << endl;
382                     normals.push_back( p );
383                     ptr += 3;
384                 }
385             }
386         } else if ( obj_type == SG_TEXCOORD_LIST ) {
387             // read texcoord list properties
388             for ( j = 0; j < nproperties; ++j ) {
389                 char prop_type;
390                 sgReadChar( fp, &prop_type );
391
392                 sgReadUInt( fp, &nbytes );
393                 // cout << "property size = " << nbytes << endl;
394                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
395                 char *ptr = buf.get_ptr();
396                 sgReadBytes( fp, nbytes, ptr );
397             }
398
399             // read texcoord list elements
400             for ( j = 0; j < nelements; ++j ) {
401                 sgReadUInt( fp, &nbytes );
402                 // cout << "element size = " << nbytes << endl;
403                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
404                 char *ptr = buf.get_ptr();
405                 sgReadBytes( fp, nbytes, ptr );
406                 int count = nbytes / (sizeof(float) * 2);
407                 float *fptr = (float *)ptr;
408                 for ( k = 0; k < count; ++k ) {
409                     if ( sgIsBigEndian() ) {
410                         sgEndianSwap( (unsigned int *)&(fptr[0]) );
411                         sgEndianSwap( (unsigned int *)&(fptr[1]) );
412                     }
413                     p = Point3D( fptr[0], fptr[1], 0 );
414                     // cout << "texcoord = " << p << endl;
415                     texcoords.push_back( p );
416                     fptr += 2;
417                 }
418             }
419         } else if ( obj_type == SG_POINTS ) {
420             // read points 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 point 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);
445                 short *sptr = (short *)ptr;
446                 int_list vs;
447                 vs.clear();
448                 for ( k = 0; k < count; ++k ) {
449                     if ( sgIsBigEndian() ) {
450                         sgEndianSwap( (unsigned short *)&(sptr[0]) );
451                     }
452                     vs.push_back( sptr[0] );
453                     // cout << sptr[0] << " ";
454                     ++sptr;
455                 }
456                 // cout << endl;
457                 pts_v.push_back( vs );
458                 pt_materials.push_back( material );
459             }
460         } else if ( obj_type == SG_TRIANGLE_FACES ) {
461             // read triangle face properties
462             for ( j = 0; j < nproperties; ++j ) {
463                 char prop_type;
464                 sgReadChar( fp, &prop_type );
465
466                 sgReadUInt( fp, &nbytes );
467                 // cout << "property size = " << nbytes << endl;
468                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
469                 char *ptr = buf.get_ptr();
470                 sgReadBytes( fp, nbytes, ptr );
471                 if ( prop_type == SG_MATERIAL ) {
472                     strncpy( material, ptr, nbytes );
473                     material[nbytes] = '\0';
474                     // cout << "material type = " << material << endl;
475                 }
476             }
477
478             // read triangle face elements
479             for ( j = 0; j < nelements; ++j ) {
480                 sgReadUInt( fp, &nbytes );
481                 // cout << "element size = " << nbytes << endl;
482                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
483                 char *ptr = buf.get_ptr();
484                 sgReadBytes( fp, nbytes, ptr );
485                 int count = nbytes / (sizeof(short) * 2);
486                 short *sptr = (short *)ptr;
487                 int_list vs, tcs;
488                 vs.clear(); tcs.clear();
489                 for ( k = 0; k < count; ++k ) {
490                     if ( sgIsBigEndian() ) {
491                         sgEndianSwap( (unsigned short *)&(sptr[0]) );
492                         sgEndianSwap( (unsigned short *)&(sptr[1]) );
493                     }
494                     vs.push_back( sptr[0] );
495                     tcs.push_back( sptr[1] );
496                     // cout << sptr[0] << "/" << sptr[1] << " ";
497                     sptr += 2;
498                 }
499                 // cout << endl;
500                 tris_v.push_back( vs );
501                 tris_tc.push_back( tcs );
502                 tri_materials.push_back( material );
503             }
504         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
505             // read triangle strip properties
506             for ( j = 0; j < nproperties; ++j ) {
507                 char prop_type;
508                 sgReadChar( fp, &prop_type );
509
510                 sgReadUInt( fp, &nbytes );
511                 // cout << "property size = " << nbytes << endl;
512                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
513                 char *ptr = buf.get_ptr();
514                 sgReadBytes( fp, nbytes, ptr );
515                 if ( prop_type == SG_MATERIAL ) {
516                     strncpy( material, ptr, nbytes );
517                     material[nbytes] = '\0';
518                     // cout << "material type = " << material << endl;
519                 }
520             }
521
522             // read triangle strip 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                 int count = nbytes / (sizeof(short) * 2);
530                 short *sptr = (short *)ptr;
531                 int_list vs, tcs;
532                 vs.clear(); tcs.clear();
533                 for ( k = 0; k < count; ++k ) {
534                     if ( sgIsBigEndian() ) {
535                         sgEndianSwap( (unsigned short *)&(sptr[0]) );
536                         sgEndianSwap( (unsigned short *)&(sptr[1]) );
537                     }
538                     vs.push_back( sptr[0] );
539                     tcs.push_back( sptr[1] );
540                     // cout << sptr[0] << "/" << sptr[1] << " ";
541                     sptr += 2;
542                 }
543                 // cout << endl;
544                 strips_v.push_back( vs );
545                 strips_tc.push_back( tcs );
546                 strip_materials.push_back( material );
547             }
548         } else if ( obj_type == SG_TRIANGLE_FANS ) {
549             // read triangle fan properties
550             for ( j = 0; j < nproperties; ++j ) {
551                 char prop_type;
552                 sgReadChar( fp, &prop_type );
553
554                 sgReadUInt( fp, &nbytes );
555                 // cout << "property size = " << nbytes << endl;
556                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
557                 char *ptr = buf.get_ptr();
558                 sgReadBytes( fp, nbytes, ptr );
559                 if ( prop_type == SG_MATERIAL ) {
560                     strncpy( material, ptr, nbytes );
561                     material[nbytes] = '\0';
562                     // cout << "material type = " << material << endl;
563                 }
564             }
565
566             // read triangle fan elements
567             for ( j = 0; j < nelements; ++j ) {
568                 sgReadUInt( fp, &nbytes );
569                 // cout << "element size = " << nbytes << endl;
570                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
571                 char *ptr = buf.get_ptr();
572                 sgReadBytes( fp, nbytes, ptr );
573                 int count = nbytes / (sizeof(short) * 2);
574                 short *sptr = (short *)ptr;
575                 int_list vs, tcs;
576                 vs.clear(); tcs.clear();
577                 for ( k = 0; k < count; ++k ) {
578                     if ( sgIsBigEndian() ) {
579                         sgEndianSwap( (unsigned short *)&(sptr[0]) );
580                         sgEndianSwap( (unsigned short *)&(sptr[1]) );
581                     }
582                     vs.push_back( sptr[0] );
583                     tcs.push_back( sptr[1] );
584                     // cout << sptr[0] << "/" << sptr[1] << " ";
585                     sptr += 2;
586                 }
587                 // cout << endl;
588                 fans_v.push_back( vs );
589                 fans_tc.push_back( tcs );
590                 fan_materials.push_back( material );
591             }
592         } else {
593             // unknown object type, just skip
594
595             // read properties
596             for ( j = 0; j < nproperties; ++j ) {
597                 char prop_type;
598                 sgReadChar( fp, &prop_type );
599
600                 sgReadUInt( fp, &nbytes );
601                 // cout << "property size = " << nbytes << endl;
602                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
603                 char *ptr = buf.get_ptr();
604                 sgReadBytes( fp, nbytes, ptr );
605             }
606
607             // read elements
608             for ( j = 0; j < nelements; ++j ) {
609                 sgReadUInt( fp, &nbytes );
610                 // cout << "element size = " << nbytes << endl;
611                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
612                 char *ptr = buf.get_ptr();
613                 sgReadBytes( fp, nbytes, ptr );
614             }
615         }
616     }
617
618     // close the file
619     gzclose(fp);
620
621     if ( sgReadError() ) {
622         cout << "We detected an error while reading the file." << endl;
623         return false;
624     }
625
626     return true;
627 }
628
629
630 // write out the structures to a binary file.  We assume that the
631 // groups come to us sorted by material property.  If not, things
632 // don't break, but the result won't be as optimal.
633 bool SGBinObject::write_bin( const string& base, const string& name,
634                              const SGBucket& b )
635 {
636     Point3D p;
637     sgVec2 t;
638     sgVec3 pt;
639     sgVec4 color;
640     int i, j;
641
642     string dir = base + "/" + b.gen_base_path();
643     string command = "mkdir -p " + dir;
644 #if defined(_MSC_VER) || defined(__MINGW32__)
645     system( (string("mkdir ") + dir).c_str() );
646 #else
647     system(command.c_str());
648 #endif
649
650     string file = dir + "/" + name + ".gz";
651     cout << "Output file = " << file << endl;
652
653     gzFile fp;
654     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
655         cout << "ERROR: opening " << file << " for writing!" << endl;
656         return false;
657     }
658
659     sgClearWriteError();
660
661     cout << "points size = " << pts_v.size() << "  pt_materials = " 
662          << pt_materials.size() << endl;
663     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
664          << tri_materials.size() << endl;
665     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
666          << strip_materials.size() << endl;
667     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
668          << fan_materials.size() << endl;
669
670     cout << "nodes = " << wgs84_nodes.size() << endl;
671     cout << "colors = " << colors.size() << endl;
672     cout << "normals = " << normals.size() << endl;
673     cout << "tex coords = " << texcoords.size() << endl;
674
675     // write header magic
676     sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
677     time_t calendar_time = time(NULL);
678     sgWriteLong( fp, (long int)calendar_time );
679
680     // calculate and write number of top level objects
681     string material;
682     int start;
683     int end;
684     short nobjects = 0;
685     nobjects++;                 // for gbs
686     nobjects++;                 // for vertices
687     nobjects++;                 // for colors
688     nobjects++;                 // for normals
689     nobjects++;                 // for texcoords
690
691     // points
692     short npts = 0;
693     start = 0; end = 1;
694     while ( start < (int)pt_materials.size() ) {
695         material = pt_materials[start];
696         while ( (end < (int)pt_materials.size()) &&
697                 (material == pt_materials[end]) ) {
698             end++;
699         }
700         npts++;
701         start = end; end = start + 1;
702     }
703     nobjects += npts;
704
705     // tris
706     short ntris = 0;
707     start = 0; end = 1;
708     while ( start < (int)tri_materials.size() ) {
709         material = tri_materials[start];
710         while ( (end < (int)tri_materials.size()) &&
711                 (material == tri_materials[end]) ) {
712             end++;
713         }
714         ntris++;
715         start = end; end = start + 1;
716     }
717     nobjects += ntris;
718
719     // strips
720     short nstrips = 0;
721     start = 0; end = 1;
722     while ( start < (int)strip_materials.size() ) {
723         material = strip_materials[start];
724         while ( (end < (int)strip_materials.size()) &&
725                 (material == strip_materials[end]) ) {
726             end++;
727         }
728         nstrips++;
729         start = end; end = start + 1;
730     }
731     nobjects += nstrips;
732
733     // fans
734     short nfans = 0;
735     start = 0; end = 1;
736     while ( start < (int)fan_materials.size() ) {
737         material = fan_materials[start];
738         while ( (end < (int)fan_materials.size()) &&
739                 (material == fan_materials[end]) ) {
740             end++;
741         }
742         nfans++;
743         start = end; end = start + 1;
744     }
745     nobjects += nfans;
746
747     cout << "total top level objects = " << nobjects << endl;
748     sgWriteShort( fp, nobjects );
749
750     // write bounding sphere
751     sgWriteChar( fp, (char)SG_BOUNDING_SPHERE );        // type
752     sgWriteShort( fp, 0 );                              // nproperties
753     sgWriteShort( fp, 1 );                              // nelements
754
755     sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
756     sgdVec3 center;
757     sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
758     sgWritedVec3( fp, center );
759     sgWriteFloat( fp, gbs_radius );
760
761     // dump vertex list
762     sgWriteChar( fp, (char)SG_VERTEX_LIST );             // type
763     sgWriteShort( fp, 0 );                               // nproperties
764     sgWriteShort( fp, 1 );                               // nelements
765     sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
766     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
767         p = wgs84_nodes[i] - gbs_center;
768         sgSetVec3( pt, p.x(), p.y(), p.z() );
769         sgWriteVec3( fp, pt );
770     }
771
772     // dump vertex color list
773     sgWriteChar( fp, (char)SG_COLOR_LIST );             // type
774     sgWriteShort( fp, 0 );                               // nproperties
775     sgWriteShort( fp, 1 );                               // nelements
776     sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
777     for ( i = 0; i < (int)colors.size(); ++i ) {
778         p = colors[i];
779         // Right now we have a place holder for color alpha but we
780         // need to update the interface so the calling program can
781         // provide the info.
782         sgSetVec4( color, p.x(), p.y(), p.z(), 1.0 );
783         sgWriteVec4( fp, color );
784     }
785
786     // dump vertex normal list
787     sgWriteChar( fp, (char)SG_NORMAL_LIST );            // type
788     sgWriteShort( fp, 0 );                              // nproperties
789     sgWriteShort( fp, 1 );                              // nelements
790     sgWriteUInt( fp, normals.size() * 3 );              // nbytes
791     char normal[3];
792     for ( i = 0; i < (int)normals.size(); ++i ) {
793         p = normals[i];
794         normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
795         normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
796         normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
797         sgWriteBytes( fp, 3, normal );
798     }
799
800     // dump texture coordinates
801     sgWriteChar( fp, (char)SG_TEXCOORD_LIST );          // type
802     sgWriteShort( fp, 0 );                              // nproperties
803     sgWriteShort( fp, 1 );                              // nelements
804     sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
805     for ( i = 0; i < (int)texcoords.size(); ++i ) {
806         p = texcoords[i];
807         sgSetVec2( t, p.x(), p.y() );
808         sgWriteVec2( fp, t );
809     }
810
811     // dump point groups if they exist
812     if ( pts_v.size() > 0 ) {
813         int start = 0;
814         int end = 1;
815         string material;
816         while ( start < (int)pt_materials.size() ) {
817             // find next group
818             material = pt_materials[start];
819             while ( (end < (int)pt_materials.size()) && 
820                     (material == pt_materials[end]) )
821                 {
822                     // cout << "end = " << end << endl;
823                     end++;
824                 }
825             // cout << "group = " << start << " to " << end - 1 << endl;
826
827             // write group headers
828             sgWriteChar( fp, (char)SG_POINTS );          // type
829             sgWriteShort( fp, 1 );                       // nproperties
830             sgWriteShort( fp, end - start );             // nelements
831
832             sgWriteChar( fp, (char)SG_MATERIAL );        // property
833             sgWriteUInt( fp, material.length() );        // nbytes
834             sgWriteBytes( fp, material.length(), material.c_str() );
835
836             // write strips
837             for ( i = start; i < end; ++i ) {
838                 // nbytes
839                 sgWriteUInt( fp, pts_v[i].size() * sizeof(short) );
840                 for ( j = 0; j < (int)pts_v[i].size(); ++j ) {
841                     sgWriteShort( fp, (short)pts_v[i][j] );
842                 }
843             }
844             
845             start = end;
846             end = start + 1;
847         }
848     }
849
850     // dump individual triangles if they exist
851     if ( tris_v.size() > 0 ) {
852         int start = 0;
853         int end = 1;
854         string material;
855         while ( start < (int)tri_materials.size() ) {
856             // find next group
857             material = tri_materials[start];
858             while ( (end < (int)tri_materials.size()) && 
859                     (material == tri_materials[end]) )
860             {
861                 // cout << "end = " << end << endl;
862                 end++;
863             }
864             // cout << "group = " << start << " to " << end - 1 << endl;
865
866             // write group headers
867             sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
868             sgWriteShort( fp, 1 );                      // nproperties
869             sgWriteShort( fp, 1 );                      // nelements
870
871             sgWriteChar( fp, (char)SG_MATERIAL );       // property
872             sgWriteUInt( fp, material.length() );        // nbytes
873             sgWriteBytes( fp, material.length(), material.c_str() );
874
875             sgWriteUInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
876
877             // write group
878             for ( i = start; i < end; ++i ) {
879                 for ( j = 0; j < 3; ++j ) {
880                     sgWriteShort( fp, (short)tris_v[i][j] );
881                     sgWriteShort( fp, (short)tris_tc[i][j] );
882                 }
883             }
884
885             start = end;
886             end = start + 1;
887         }
888     }
889
890     // dump triangle strips
891     if ( strips_v.size() > 0 ) {
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             // write group headers
907             sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
908             sgWriteShort( fp, 1 );                       // nproperties
909             sgWriteShort( fp, end - start );             // nelements
910
911             sgWriteChar( fp, (char)SG_MATERIAL );        // property
912             sgWriteUInt( fp, material.length() );        // nbytes
913             sgWriteBytes( fp, material.length(), material.c_str() );
914
915             // write strips
916             for ( i = start; i < end; ++i ) {
917                 // nbytes
918                 sgWriteUInt( fp, strips_v[i].size() * 2 * sizeof(short) );
919                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
920                     sgWriteShort( fp, (short)strips_v[i][j] );
921                     sgWriteShort( fp, (short)strips_tc[i][j] );
922                 }
923             }
924             
925             start = end;
926             end = start + 1;
927         }
928     }
929
930     // dump triangle fans
931     if ( fans_v.size() > 0 ) {
932         int start = 0;
933         int end = 1;
934         string material;
935         while ( start < (int)fan_materials.size() ) {
936             // find next group
937             material = fan_materials[start];
938             while ( (end < (int)fan_materials.size()) && 
939                     (material == fan_materials[end]) )
940                 {
941                     // cout << "end = " << end << endl;
942                     end++;
943                 }
944             // cout << "group = " << start << " to " << end - 1 << endl;
945
946             // write group headers
947             sgWriteChar( fp, (char)SG_TRIANGLE_FANS );   // type
948             sgWriteShort( fp, 1 );                       // nproperties
949             sgWriteShort( fp, end - start );             // nelements
950
951             sgWriteChar( fp, (char)SG_MATERIAL );       // property
952             sgWriteUInt( fp, material.length() );        // nbytes
953             sgWriteBytes( fp, material.length(), material.c_str() );
954
955             // write fans
956             for ( i = start; i < end; ++i ) {
957                 // nbytes
958                 sgWriteUInt( fp, fans_v[i].size() * 2 * sizeof(short) );
959                 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
960                     sgWriteShort( fp, (short)fans_v[i][j] );
961                     sgWriteShort( fp, (short)fans_tc[i][j] );
962                 }
963             }
964             
965             start = end;
966             end = start + 1;
967         }
968     }
969
970     // close the file
971     gzclose(fp);
972
973     if ( sgWriteError() ) {
974         cout << "We detected an error while writing the file." << endl;
975         return false;
976     }
977
978     return true;
979 }
980
981
982 // write out the structures to an ASCII file.  We assume that the
983 // groups come to us sorted by material property.  If not, things
984 // don't break, but the result won't be as optimal.
985 bool SGBinObject::write_ascii( const string& base, const string& name,
986                                const SGBucket& b )
987 {
988     Point3D p;
989     int i, j;
990
991     string dir = base + "/" + b.gen_base_path();
992     string command = "mkdir -p " + dir;
993 #if defined(_MSC_VER) || defined(__MINGW32__)
994     system( (string("mkdir ") + dir).c_str() );
995 #else
996     system(command.c_str());
997 #endif
998
999     // string file = dir + "/" + b.gen_index_str();
1000     string file = dir + "/" + name;
1001     cout << "Output file = " << file << endl;
1002
1003     FILE *fp;
1004     if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
1005         cout << "ERROR: opening " << file << " for writing!" << endl;
1006         return false;
1007     }
1008
1009     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
1010          << tri_materials.size() << endl;
1011     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
1012          << strip_materials.size() << endl;
1013     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
1014          << fan_materials.size() << endl;
1015
1016     cout << "points = " << wgs84_nodes.size() << endl;
1017     cout << "tex coords = " << texcoords.size() << endl;
1018     // write headers
1019     fprintf(fp, "# FGFS Scenery\n");
1020     fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
1021
1022     time_t calendar_time = time(NULL);
1023     struct tm *local_tm;
1024     local_tm = localtime( &calendar_time );
1025     char time_str[256];
1026     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
1027     fprintf(fp, "# Created %s\n", time_str );
1028     fprintf(fp, "\n");
1029
1030     // write bounding sphere
1031     fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
1032             gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
1033     fprintf(fp, "\n");
1034
1035     // dump vertex list
1036     fprintf(fp, "# vertex list\n");
1037     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1038         p = wgs84_nodes[i] - gbs_center;
1039         
1040         fprintf(fp,  "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1041     }
1042     fprintf(fp, "\n");
1043
1044     fprintf(fp, "# vertex normal list\n");
1045     for ( i = 0; i < (int)normals.size(); ++i ) {
1046         p = normals[i];
1047         fprintf(fp,  "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1048     }
1049     fprintf(fp, "\n");
1050
1051     // dump texture coordinates
1052     fprintf(fp, "# texture coordinate list\n");
1053     for ( i = 0; i < (int)texcoords.size(); ++i ) {
1054         p = texcoords[i];
1055         fprintf(fp,  "vt %.5f %.5f\n", p.x(), p.y() );
1056     }
1057     fprintf(fp, "\n");
1058
1059     // dump individual triangles if they exist
1060     if ( tris_v.size() > 0 ) {
1061         fprintf(fp, "# triangle groups\n");
1062
1063         int start = 0;
1064         int end = 1;
1065         string material;
1066         while ( start < (int)tri_materials.size() ) {
1067             // find next group
1068             material = tri_materials[start];
1069             while ( (end < (int)tri_materials.size()) && 
1070                     (material == tri_materials[end]) )
1071             {
1072                 // cout << "end = " << end << endl;
1073                 end++;
1074             }
1075             // cout << "group = " << start << " to " << end - 1 << endl;
1076
1077             // make a list of points for the group
1078             point_list group_nodes;
1079             group_nodes.clear();
1080             Point3D bs_center;
1081             double bs_radius = 0;
1082             for ( i = start; i < end; ++i ) {
1083                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1084                     group_nodes.push_back( wgs84_nodes[ tris_v[i][j] ] );
1085                     bs_center = calc_center( group_nodes );
1086                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
1087                 }
1088             }
1089
1090             // write group headers
1091             fprintf(fp, "\n");
1092             fprintf(fp, "# usemtl %s\n", material.c_str());
1093             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1094                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1095
1096             // write groups
1097             for ( i = start; i < end; ++i ) {
1098                 fprintf(fp, "f");
1099                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1100                     fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
1101                 }
1102                 fprintf(fp, "\n");
1103             }
1104
1105             start = end;
1106             end = start + 1;
1107         }
1108     }
1109
1110     // dump triangle groups
1111     if ( strips_v.size() > 0 ) {
1112         fprintf(fp, "# triangle strips\n");
1113
1114         int start = 0;
1115         int end = 1;
1116         string material;
1117         while ( start < (int)strip_materials.size() ) {
1118             // find next group
1119             material = strip_materials[start];
1120             while ( (end < (int)strip_materials.size()) && 
1121                     (material == strip_materials[end]) )
1122                 {
1123                     // cout << "end = " << end << endl;
1124                     end++;
1125                 }
1126             // cout << "group = " << start << " to " << end - 1 << endl;
1127
1128             // make a list of points for the group
1129             point_list group_nodes;
1130             group_nodes.clear();
1131             Point3D bs_center;
1132             double bs_radius = 0;
1133             for ( i = start; i < end; ++i ) {
1134                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1135                     group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] );
1136                     bs_center = calc_center( group_nodes );
1137                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
1138                 }
1139             }
1140
1141             // write group headers
1142             fprintf(fp, "\n");
1143             fprintf(fp, "# usemtl %s\n", material.c_str());
1144             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1145                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1146
1147             // write groups
1148             for ( i = start; i < end; ++i ) {
1149                 fprintf(fp, "ts");
1150                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1151                     fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
1152                 }
1153                 fprintf(fp, "\n");
1154             }
1155             
1156             start = end;
1157             end = start + 1;
1158         }
1159     }
1160
1161     // close the file
1162     fclose(fp);
1163
1164     command = "gzip --force --best " + file;
1165     system(command.c_str());
1166
1167     return true;
1168 }