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