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