]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
Fix VS2010 lack of fminf
[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  - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22 //
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30 #include <simgear/debug/logstream.hxx>
31
32 #include <stdio.h>
33 #include <time.h>
34 #include <cstring>
35 #include <cstdlib> // for system()
36 #include <cassert>
37
38 #include <vector>
39 #include <string>
40 #include <iostream>
41 #include <bitset>
42
43 #include <simgear/bucket/newbucket.hxx>
44 #include <simgear/misc/sg_path.hxx>
45 #include <simgear/math/SGGeometry.hxx>
46 #include <simgear/structure/exception.hxx>
47
48 #include "lowlevel.hxx"
49 #include "sg_binobj.hxx"
50
51
52 using std::string;
53 using std::vector;
54 using std::cout;
55 using std::endl;
56
57 enum sgObjectTypes {
58     SG_BOUNDING_SPHERE = 0,
59
60     SG_VERTEX_LIST = 1,
61     SG_NORMAL_LIST = 2,
62     SG_TEXCOORD_LIST = 3,
63     SG_COLOR_LIST = 4,
64     SG_VA_FLOAT_LIST = 5,
65     SG_VA_INTEGER_LIST = 6,
66
67     SG_POINTS = 9,
68
69     SG_TRIANGLE_FACES = 10,
70     SG_TRIANGLE_STRIPS = 11,
71     SG_TRIANGLE_FANS = 12
72 };
73
74 enum sgIndexTypes {
75     SG_IDX_VERTICES =    0x01,
76     SG_IDX_NORMALS =     0x02,
77     SG_IDX_COLORS =      0x04,
78     SG_IDX_TEXCOORDS_0 = 0x08,
79     SG_IDX_TEXCOORDS_1 = 0x10,
80     SG_IDX_TEXCOORDS_2 = 0x20,
81     SG_IDX_TEXCOORDS_3 = 0x40,
82 };
83
84 enum sgVertexAttributeTypes {
85     // vertex attributes
86     SG_VA_INTEGER_0 = 0x00000001,
87     SG_VA_INTEGER_1 = 0x00000002,
88     SG_VA_INTEGER_2 = 0x00000004,
89     SG_VA_INTEGER_3 = 0x00000008,
90
91     SG_VA_FLOAT_0 =   0x00000100,
92     SG_VA_FLOAT_1 =   0x00000200,
93     SG_VA_FLOAT_2 =   0x00000400,
94     SG_VA_FLOAT_3 =   0x00000800,
95 };
96
97 enum sgPropertyTypes {
98     SG_MATERIAL = 0,
99     SG_INDEX_TYPES = 1,
100     SG_VERT_ATTRIBS = 2
101 };
102
103
104 class sgSimpleBuffer {
105
106 private:
107
108     char *ptr;
109     unsigned int size;
110     size_t offset;
111 public:
112
113     sgSimpleBuffer( unsigned int s = 0) :
114         ptr(NULL),
115         size(0),
116         offset(0)
117     {
118         resize(s);
119     }
120
121     ~sgSimpleBuffer()
122     {
123         delete [] ptr;
124     }
125
126     unsigned int get_size() const { return size; }
127     char *get_ptr() const { return ptr; }
128     
129     void reset()
130     {
131         offset = 0;
132     }
133     
134     void resize( unsigned int s )
135     {
136         if ( s > size ) {
137             if ( ptr != NULL ) {
138                 delete [] ptr;
139             }
140           
141             if ( size == 0) {
142                 size = 16;
143             }
144           
145             while ( size < s ) {
146               size = size << 1;
147             }
148             ptr = new char[size];
149         }
150     }
151     
152     float readInt()
153     {
154         unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
155         if ( sgIsBigEndian() ) {            
156             sgEndianSwap((uint32_t *) p);
157         }
158         
159         offset += sizeof(unsigned int);
160         return *p;
161     }
162     
163     SGVec3d readVec3d()
164     {
165         double* p = reinterpret_cast<double*>(ptr + offset);
166         
167         if ( sgIsBigEndian() ) {            
168             sgEndianSwap((uint64_t *) p + 0);
169             sgEndianSwap((uint64_t *) p + 1);
170             sgEndianSwap((uint64_t *) p + 2);
171         }
172         
173         offset += 3 * sizeof(double);
174         return SGVec3d(p);
175     }
176     
177     float readFloat()
178     {
179         float* p = reinterpret_cast<float*>(ptr + offset);
180         if ( sgIsBigEndian() ) {            
181             sgEndianSwap((uint32_t *) p);
182         }
183         
184         offset += sizeof(float);
185         return *p;
186     }
187     
188     SGVec2f readVec2f()
189     {
190         float* p = reinterpret_cast<float*>(ptr + offset);
191         
192         if ( sgIsBigEndian() ) {            
193             sgEndianSwap((uint32_t *) p + 0);
194             sgEndianSwap((uint32_t *) p + 1);
195         }
196         
197         offset += 2 * sizeof(float);
198         return SGVec2f(p);
199     }
200     
201     SGVec3f readVec3f()
202     {
203         float* p = reinterpret_cast<float*>(ptr + offset);
204         
205         if ( sgIsBigEndian() ) {            
206             sgEndianSwap((uint32_t *) p + 0);
207             sgEndianSwap((uint32_t *) p + 1);
208             sgEndianSwap((uint32_t *) p + 2);
209         }
210         
211         offset += 3 * sizeof(float);
212         return SGVec3f(p);
213     }
214     
215     SGVec4f readVec4f()
216     {
217         float* p = reinterpret_cast<float*>(ptr + offset);
218         
219         if ( sgIsBigEndian() ) {            
220             sgEndianSwap((uint32_t *) p + 0);
221             sgEndianSwap((uint32_t *) p + 1);
222             sgEndianSwap((uint32_t *) p + 2);
223             sgEndianSwap((uint32_t *) p + 3);
224         }
225         
226         offset += 4 * sizeof(float);
227         return SGVec4f(p);
228     }
229 };
230
231 template <class T>
232 static void read_indices(char* buffer, 
233                          size_t bytes,
234                          int indexMask,
235                          int vaMask,
236                          int_list& vertices, 
237                          int_list& normals,
238                          int_list& colors,
239                          tci_list& texCoords,
240                          vai_list& vas
241                         )
242 {
243     const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
244     const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
245     const int count = bytes / (indexSize + vaSize);
246     
247     // fix endian-ness of the whole lot, if required
248     if (sgIsBigEndian()) {
249         int indices = bytes / sizeof(T);
250         T* src = reinterpret_cast<T*>(buffer);
251         for (int i=0; i<indices; ++i) {
252             sgEndianSwap(src++);
253         }
254     }
255     
256     T* src = reinterpret_cast<T*>(buffer);
257     for (int i=0; i<count; ++i) {
258         if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
259         if (indexMask & SG_IDX_NORMALS) normals.push_back(*src++);
260         if (indexMask & SG_IDX_COLORS) colors.push_back(*src++);
261         if (indexMask & SG_IDX_TEXCOORDS_0) texCoords[0].push_back(*src++);
262         if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
263         if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
264         if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
265         
266         if ( vaMask ) {
267             if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
268             if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
269             if (vaMask & SG_VA_INTEGER_2) vas[2].push_back(*src++);
270             if (vaMask & SG_VA_INTEGER_3) vas[3].push_back(*src++);
271             if (vaMask & SG_VA_FLOAT_0) vas[4].push_back(*src++);
272             if (vaMask & SG_VA_FLOAT_1) vas[5].push_back(*src++);
273             if (vaMask & SG_VA_FLOAT_2) vas[6].push_back(*src++);
274             if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
275         }
276     } // of elements in the index
277 }
278
279 template <class T>
280 void write_indice(gzFile fp, T value)
281 {
282     sgWriteBytes(fp, sizeof(T), &value);
283 }
284
285 // specialize template to call endian-aware conversion methods
286 template <>
287 void write_indice(gzFile fp, uint16_t value)
288 {
289     sgWriteUShort(fp, value);
290 }
291
292 template <>
293 void write_indice(gzFile fp, uint32_t value)
294 {
295     sgWriteUInt(fp, value);
296 }
297
298
299 template <class T>
300 void write_indices(gzFile fp, 
301     unsigned char indexMask, 
302     unsigned int vaMask,
303     const int_list& vertices, 
304     const int_list& normals, 
305     const int_list& colors,
306     const tci_list& texCoords,
307     const vai_list& vas )
308 {
309     unsigned int count = vertices.size();
310     const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
311     const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
312     sgWriteUInt(fp, (indexSize + vaSize) * count);
313             
314     for (unsigned int i=0; i < count; ++i) {
315         write_indice(fp, static_cast<T>(vertices[i]));
316         
317         if (indexMask & SG_IDX_NORMALS) {
318             write_indice(fp, static_cast<T>(normals[i]));
319         }
320         if (indexMask & SG_IDX_COLORS) {
321             write_indice(fp, static_cast<T>(colors[i]));
322         }
323         if (indexMask & SG_IDX_TEXCOORDS_0) {
324             write_indice(fp, static_cast<T>(texCoords[0][i]));
325         }
326         if (indexMask & SG_IDX_TEXCOORDS_1) {
327             write_indice(fp, static_cast<T>(texCoords[1][i]));
328         }
329         if (indexMask & SG_IDX_TEXCOORDS_2) {
330             write_indice(fp, static_cast<T>(texCoords[2][i]));
331         }
332         if (indexMask & SG_IDX_TEXCOORDS_3) {
333             write_indice(fp, static_cast<T>(texCoords[3][i]));
334         }
335         
336         if (vaMask) {
337             if (vaMask & SG_VA_INTEGER_0) {
338                 write_indice(fp, static_cast<T>(vas[0][i]));
339             }
340             if (vaMask & SG_VA_INTEGER_1) {
341                 write_indice(fp, static_cast<T>(vas[1][i]));
342             }
343             if (vaMask & SG_VA_INTEGER_2) {
344                 write_indice(fp, static_cast<T>(vas[2][i]));
345             }
346             if (vaMask & SG_VA_INTEGER_3) {
347                 write_indice(fp, static_cast<T>(vas[3][i]));
348             }
349
350             if (vaMask & SG_VA_FLOAT_0) {
351                 write_indice(fp, static_cast<T>(vas[4][i]));
352             }
353             if (vaMask & SG_VA_FLOAT_1) {
354                 write_indice(fp, static_cast<T>(vas[5][i]));
355             }
356             if (vaMask & SG_VA_FLOAT_2) {
357                 write_indice(fp, static_cast<T>(vas[6][i]));
358             }
359             if (vaMask & SG_VA_FLOAT_3) {
360                 write_indice(fp, static_cast<T>(vas[7][i]));
361             }
362         }
363     }
364 }
365
366
367 // read object properties
368 void SGBinObject::read_object( gzFile fp,
369                          int obj_type,
370                          int nproperties,
371                          int nelements,
372                          group_list& vertices, 
373                          group_list& normals,
374                          group_list& colors,
375                          group_tci_list& texCoords,
376                          group_vai_list& vertexAttribs,
377                          string_list& materials)
378 {
379     unsigned int  nbytes;
380     unsigned char idx_mask;
381     unsigned int  vertex_attrib_mask;
382     int j;
383     sgSimpleBuffer buf( 32768 );  // 32 Kb
384     char material[256];
385
386     // default values
387     if ( obj_type == SG_POINTS ) {
388         idx_mask = SG_IDX_VERTICES;
389     } else {
390         idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
391     }
392     vertex_attrib_mask = 0;
393     
394     for ( j = 0; j < nproperties; ++j ) {
395         char prop_type;
396         sgReadChar( fp, &prop_type );
397         sgReadUInt( fp, &nbytes );
398         
399         buf.resize(nbytes);
400         char *ptr = buf.get_ptr();
401         
402         switch( prop_type )
403         {
404             case SG_MATERIAL:
405                 sgReadBytes( fp, nbytes, ptr );
406                 if (nbytes > 255) {
407                     nbytes = 255;
408                 }
409                 strncpy( material, ptr, nbytes );
410                 material[nbytes] = '\0';
411                 break;
412                 
413             case SG_INDEX_TYPES:
414                 if (nbytes == 1) {
415                     sgReadChar( fp, (char *)&idx_mask );                    
416                 } else {
417                     sgReadBytes( fp, nbytes, ptr );                    
418                 }
419                 break;
420
421             case SG_VERT_ATTRIBS:
422                 if (nbytes == 4) {
423                     sgReadUInt( fp, &vertex_attrib_mask );
424                 } else {
425                     sgReadBytes( fp, nbytes, ptr );
426                 }
427                 break;
428                 
429             default:
430                 sgReadBytes( fp, nbytes, ptr );
431                 SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
432                 break;
433         }
434     }
435
436     if ( sgReadError() ) {
437         throw sg_exception("Error reading object properties");
438     }
439     
440     size_t indexCount = std::bitset<32>((int)idx_mask).count();
441     if (indexCount == 0) {
442         throw sg_exception("object index mask has no bits set");
443     }
444     
445     for ( j = 0; j < nelements; ++j ) {
446         sgReadUInt( fp, &nbytes );
447         if ( sgReadError() ) {
448             throw sg_exception("Error reading element size");
449         }
450         
451         buf.resize( nbytes );
452         char *ptr = buf.get_ptr();
453         sgReadBytes( fp, nbytes, ptr );
454         
455         if ( sgReadError() ) {
456             throw sg_exception("Error reading element bytes");
457         }
458                 
459         int_list vs;
460         int_list ns;
461         int_list cs;
462         tci_list tcs;
463         vai_list vas;
464
465         if (version >= 10) {
466             read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
467         } else {
468             read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
469         }
470
471         vertices.push_back( vs );
472         normals.push_back( ns );
473         colors.push_back( cs );
474         texCoords.push_back( tcs );
475         vertexAttribs.push_back( vas );
476         materials.push_back( material );
477     } // of element iteration
478 }
479
480
481 // read a binary file and populate the provided structures.
482 bool SGBinObject::read_bin( const string& file ) {
483     SGVec3d p;
484     int i, k;
485     size_t j;
486     unsigned int nbytes;
487     sgSimpleBuffer buf( 32768 );  // 32 Kb
488
489     // zero out structures
490     gbs_center = SGVec3d(0, 0, 0);
491     gbs_radius = 0.0;
492
493     wgs84_nodes.clear();
494     normals.clear();
495     texcoords.clear();
496
497     pts_v.clear();
498     pts_n.clear();
499     pts_c.clear();
500     pts_tcs.clear();
501     pts_vas.clear();
502     pt_materials.clear();
503
504     tris_v.clear();
505     tris_n.clear();
506     tris_c.clear();
507     tris_tcs.clear();
508     tris_vas.clear();
509     tri_materials.clear();
510
511     strips_v.clear();
512     strips_n.clear();
513     strips_c.clear();
514     strips_tcs.clear();
515     strips_vas.clear();
516     strip_materials.clear();
517
518     fans_v.clear();
519     fans_n.clear();
520     fans_c.clear();
521     fans_tcs.clear();
522     fans_vas.clear();
523     fan_materials.clear();
524
525     gzFile fp;
526     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
527         string filegz = file + ".gz";
528         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
529             SG_LOG( SG_EVENT, SG_ALERT,
530                "ERROR: opening " << file << " or " << filegz << " for reading!");
531
532             throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
533         }
534     }
535
536     sgClearReadError();
537
538     // read headers
539     unsigned int header;
540     sgReadUInt( fp, &header );
541     if ( ((header & 0xFF000000) >> 24) == 'S' &&
542          ((header & 0x00FF0000) >> 16) == 'G' ) {
543     
544         // read file version
545         version = (header & 0x0000FFFF);
546     } else {
547         // close the file before we return
548         gzclose(fp);
549         throw sg_io_exception("Bad BTG magic/version", sg_location(file));
550     }
551     
552     // read creation time
553     unsigned int foo_calendar_time;
554     sgReadUInt( fp, &foo_calendar_time );
555
556 #if 0
557     time_t calendar_time = foo_calendar_time;
558     // The following code has a global effect on the host application
559     // and can screws up the time elsewhere.  It should be avoided
560     // unless you need this for debugging in which case you should
561     // disable it again once the debugging task is finished.
562     struct tm *local_tm;
563     local_tm = localtime( &calendar_time );
564     char time_str[256];
565     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
566     SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str);
567 #endif
568
569     // read number of top level objects
570     int nobjects;
571     if ( version >= 10) { // version 10 extends everything to be 32-bit
572         sgReadInt( fp, &nobjects );
573     } else if ( version >= 7 ) {
574         uint16_t v;
575         sgReadUShort( fp, &v );
576         nobjects = v;
577     } else {
578         int16_t v;
579         sgReadShort( fp, &v );
580         nobjects = v;
581     }
582      
583     SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
584
585     if ( sgReadError() ) {
586         throw sg_io_exception("Error reading BTG file header", sg_location(file));
587     }
588     
589     // read in objects
590     for ( i = 0; i < nobjects; ++i ) {
591         // read object header
592         char obj_type;
593         uint32_t nproperties, nelements;
594         sgReadChar( fp, &obj_type );
595         if ( version >= 10 ) {
596             sgReadUInt( fp, &nproperties );
597             sgReadUInt( fp, &nelements );
598         } else if ( version >= 7 ) {
599             uint16_t v;
600             sgReadUShort( fp, &v );
601             nproperties = v;
602             sgReadUShort( fp, &v );
603             nelements = v;
604         } else {
605             int16_t v;
606             sgReadShort( fp, &v );
607             nproperties = v;
608             sgReadShort( fp, &v );
609             nelements = v;
610         }
611
612         SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i << 
613                 " = " << (int)obj_type << " props = " << nproperties << 
614                 " elements = " << nelements);
615          
616         if ( obj_type == SG_BOUNDING_SPHERE ) {
617             // read bounding sphere properties
618             read_properties( fp, nproperties );
619             
620             // read bounding sphere elements
621             for ( j = 0; j < nelements; ++j ) {
622                 sgReadUInt( fp, &nbytes );
623                 buf.resize( nbytes );
624                 buf.reset();
625                 char *ptr = buf.get_ptr();
626                 sgReadBytes( fp, nbytes, ptr );
627                 gbs_center = buf.readVec3d();
628                 gbs_radius = buf.readFloat();
629             }
630         } else if ( obj_type == SG_VERTEX_LIST ) {
631             // read vertex list properties
632             read_properties( fp, nproperties );
633
634             // read vertex list elements
635             for ( j = 0; j < nelements; ++j ) {
636                 sgReadUInt( fp, &nbytes );
637                 buf.resize( nbytes );
638                 buf.reset();
639                 char *ptr = buf.get_ptr();
640                 sgReadBytes( fp, nbytes, ptr );
641                 int count = nbytes / (sizeof(float) * 3);
642                 wgs84_nodes.reserve( count );
643                 for ( k = 0; k < count; ++k ) {
644                     SGVec3f v = buf.readVec3f();
645                     // extend from float to double, hmmm
646                     wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
647                 }
648             }
649         } else if ( obj_type == SG_COLOR_LIST ) {
650             // read color list properties
651             read_properties( fp, nproperties );
652
653             // read color list elements
654             for ( j = 0; j < nelements; ++j ) {
655                 sgReadUInt( fp, &nbytes );
656                 buf.resize( nbytes );
657                 buf.reset();
658                 char *ptr = buf.get_ptr();
659                 sgReadBytes( fp, nbytes, ptr );
660                 int count = nbytes / (sizeof(float) * 4);
661                 colors.reserve(count);
662                 for ( k = 0; k < count; ++k ) {
663                     colors.push_back( buf.readVec4f() );
664                 }
665             }
666         } else if ( obj_type == SG_NORMAL_LIST ) {
667             // read normal list properties
668             read_properties( fp, nproperties );
669
670             // read normal list elements
671             for ( j = 0; j < nelements; ++j ) {
672                 sgReadUInt( fp, &nbytes );
673                 buf.resize( nbytes );
674                 buf.reset();
675                 unsigned char *ptr = (unsigned char *)(buf.get_ptr());
676                 sgReadBytes( fp, nbytes, ptr );
677                 int count = nbytes / 3;
678                 normals.reserve( count );
679  
680                 for ( k = 0; k < count; ++k ) {
681                     SGVec3f normal( (ptr[0]) / 127.5 - 1.0,
682                                     (ptr[1]) / 127.5 - 1.0, 
683                                     (ptr[2]) / 127.5 - 1.0);
684                     normals.push_back(normalize(normal));
685                     ptr += 3;
686                 }
687             }
688         } else if ( obj_type == SG_TEXCOORD_LIST ) {
689             // read texcoord list properties
690             read_properties( fp, nproperties );
691
692             // read texcoord list elements
693             for ( j = 0; j < nelements; ++j ) {
694                 sgReadUInt( fp, &nbytes );
695                 buf.resize( nbytes );
696                 buf.reset();
697                 char *ptr = buf.get_ptr();
698                 sgReadBytes( fp, nbytes, ptr );
699                 int count = nbytes / (sizeof(float) * 2);
700                 texcoords.reserve(count);
701                 for ( k = 0; k < count; ++k ) {
702                     texcoords.push_back( buf.readVec2f() );
703                 }
704             }
705         } else if ( obj_type == SG_VA_FLOAT_LIST ) {
706             // read vertex attribute (float) properties
707             read_properties( fp, nproperties );
708             
709             // read vertex attribute list elements
710             for ( j = 0; j < nelements; ++j ) {
711                 sgReadUInt( fp, &nbytes );
712                 buf.resize( nbytes );
713                 buf.reset();
714                 char *ptr = buf.get_ptr();
715                 sgReadBytes( fp, nbytes, ptr );
716                 int count = nbytes / (sizeof(float));
717                 va_flt.reserve(count);
718                 for ( k = 0; k < count; ++k ) {
719                     va_flt.push_back( buf.readFloat() );
720                 }
721             }
722         } else if ( obj_type == SG_VA_INTEGER_LIST ) {
723             // read vertex attribute (integer) properties
724             read_properties( fp, nproperties );
725             
726             // read vertex attribute list elements
727             for ( j = 0; j < nelements; ++j ) {
728                 sgReadUInt( fp, &nbytes );
729                 buf.resize( nbytes );
730                 buf.reset();
731                 char *ptr = buf.get_ptr();
732                 sgReadBytes( fp, nbytes, ptr );
733                 int count = nbytes / (sizeof(unsigned int));
734                 va_int.reserve(count);
735                 for ( k = 0; k < count; ++k ) {
736                     va_int.push_back( buf.readInt() );
737                 }
738             }
739         } else if ( obj_type == SG_POINTS ) {
740             // read point elements
741             read_object( fp, SG_POINTS, nproperties, nelements,
742                          pts_v, pts_n, pts_c, pts_tcs,
743                          pts_vas, pt_materials );
744         } else if ( obj_type == SG_TRIANGLE_FACES ) {
745             // read triangle face properties
746             read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
747                          tris_v, tris_n, tris_c, tris_tcs,
748                          tris_vas, tri_materials );
749         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
750             // read triangle strip properties
751             read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
752                          strips_v, strips_n, strips_c, strips_tcs,
753                          strips_vas, strip_materials );
754         } else if ( obj_type == SG_TRIANGLE_FANS ) {
755             // read triangle fan properties
756             read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
757                          fans_v, fans_n, fans_c, fans_tcs,
758                          fans_vas, fan_materials );
759         } else {
760             // unknown object type, just skip
761             read_properties( fp, nproperties );
762
763             // read elements
764             for ( j = 0; j < nelements; ++j ) {
765                 sgReadUInt( fp, &nbytes );
766                 // cout << "element size = " << nbytes << endl;
767                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
768                 char *ptr = buf.get_ptr();
769                 sgReadBytes( fp, nbytes, ptr );
770             }
771         }
772         
773         if ( sgReadError() ) {
774             throw sg_io_exception("Error while reading object", sg_location(file, i));
775         }
776     }
777
778     // close the file
779     gzclose(fp);
780
781     return true;
782 }
783
784 void SGBinObject::write_header(gzFile fp, int type, int nProps, int nElements)
785 {
786     sgWriteChar(fp, (unsigned char) type);
787     if (version == 7) {
788         sgWriteUShort(fp, nProps);
789         sgWriteUShort(fp, nElements);
790     } else {
791         sgWriteUInt(fp, nProps);
792         sgWriteUInt(fp, nElements);
793     }
794 }
795
796 unsigned int SGBinObject::count_objects(const string_list& materials)
797 {
798     unsigned int result = 0;
799     unsigned int start = 0, end = 1;
800     unsigned int count = materials.size();
801     string m;
802     
803     while ( start < count ) {
804         m = materials[start];
805         for (end = start+1; (end < count) && (m == materials[end]); ++end) { }     
806         ++result;
807         start = end; 
808     }
809     
810     return result;
811 }
812
813 void SGBinObject::write_objects(gzFile fp, int type, 
814                                 const group_list& verts,
815                                 const group_list& normals, 
816                                 const group_list& colors, 
817                                 const group_tci_list& texCoords,
818                                 const group_vai_list& vertexAttribs, 
819                                 const string_list& materials)
820 {
821     if (verts.empty()) {
822         return;
823     }
824         
825     unsigned int start = 0, end = 1;
826     string m;
827     int_list emptyList;
828     
829     while (start < materials.size()) {
830         m = materials[start];
831         // find range of objects with identical material, write out as a single object
832         for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
833     
834         // calc the number of elements
835         const int count = end - start;
836
837         // calc the number of properties
838         unsigned int va_mask = 0;
839         unsigned int va_count = vertexAttribs.size();
840         for ( unsigned int va=0; va<va_count; va++ ) {
841             if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
842                 va_mask |= ( 1 << va );
843             }
844         }
845
846         if ( va_mask ) {
847             write_header(fp, type, 3, count);
848         } else {
849             write_header(fp, type, 2, count);
850         }
851         
852     // properties
853         // material property
854         sgWriteChar( fp, (char)SG_MATERIAL );        // property
855         sgWriteUInt( fp, m.length() );        // nbytes
856         sgWriteBytes( fp, m.length(), m.c_str() );
857     
858         // index mask property
859         unsigned char idx_mask = 0;
860         if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
861         if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
862         if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
863         if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
864         if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
865         if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
866         if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
867         
868         if (idx_mask == 0) {
869             SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
870                 << m << "has no indices set");
871         }
872
873         sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
874         sgWriteUInt( fp, 1 );                        // nbytes
875         sgWriteChar( fp, idx_mask );
876
877         // vertex attribute property
878         if (va_mask != 0) {
879             sgWriteChar( fp, (char)SG_VERT_ATTRIBS );    // property
880             sgWriteUInt( fp, 4 );                        // nbytes
881             sgWriteChar( fp, va_mask );
882         }
883         
884     // elements
885         for (unsigned int i=start; i < end; ++i) {
886             const int_list& va(verts[i]);
887             const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
888             const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
889
890             // pass the whole texcoord array - we'll figure out which indicies to write 
891             // in write_indices
892             const tci_list& tca( texCoords[i] );
893
894             // pass the whole vertex array - we'll figure out which indicies to write 
895             // in write_indices
896             const vai_list& vaa( vertexAttribs[i] );
897             
898             if (version == 7) {
899                 write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
900             } else {
901                 write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
902             }
903         }
904     
905         start = end;
906     } // of materials iteration
907 }
908
909 // write out the structures to a binary file.  We assume that the
910 // groups come to us sorted by material property.  If not, things
911 // don't break, but the result won't be as optimal.
912 bool SGBinObject::write_bin( const string& base, const string& name,
913                              const SGBucket& b )
914 {
915
916     SGPath file = base + "/" + b.gen_base_path() + "/" + name + ".gz";
917     return write_bin_file(file);
918 }
919
920 static unsigned int max_object_size( const string_list& materials )
921 {
922     unsigned int max_size = 0;
923
924     for (unsigned int start=0; start < materials.size();) {
925         string m = materials[start];
926         unsigned int end = start + 1;
927         // find range of objects with identical material, calc its size
928         for (; (end < materials.size()) && (m == materials[end]); ++end) {}
929
930         unsigned int cur_size = end - start;
931         max_size = std::max(max_size, cur_size);
932         start = end;
933     }
934
935     return max_size;
936 }
937
938 const unsigned int VERSION_7_MATERIAL_LIMIT = 0x7fff;
939
940 bool SGBinObject::write_bin_file(const SGPath& file)
941 {
942     int i;
943     
944     SGPath file2(file);
945     file2.create_dir( 0755 );
946
947     gzFile fp;
948     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
949         cout << "ERROR: opening " << file.str() << " for writing!" << endl;
950         return false;
951     }
952
953     sgClearWriteError();
954
955     SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size() 
956          << "  pt_materials = " << pt_materials.size() );
957     SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size() 
958          << "  tri_materials = " << tri_materials.size() );
959     SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size() 
960          << "  strip_materials = " << strip_materials.size() );
961     SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size() 
962          << "  fan_materials = " << fan_materials.size() );
963
964     SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
965     SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
966     SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
967     SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
968
969     version = 10;
970     bool shortMaterialsRanges = 
971         (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
972         (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
973         (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
974         (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
975                     
976     if ((wgs84_nodes.size() < 0xffff) &&
977         (normals.size() < 0xffff) &&
978         (texcoords.size() < 0xffff) &&
979         shortMaterialsRanges) {
980         version = 7; // use smaller indices if possible
981     }
982
983     // write header magic
984     
985     /** Magic Number for our file format */
986     #define SG_FILE_MAGIC_NUMBER  ( ('S'<<24) + ('G'<<16) + version )
987     
988     sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
989     time_t calendar_time = time(NULL);
990     sgWriteLong( fp, (int32_t)calendar_time );
991
992     // calculate and write number of top level objects
993     int nobjects = 5; // gbs, vertices, colors, normals, texcoords
994     nobjects += count_objects(pt_materials);
995     nobjects += count_objects(tri_materials);
996     nobjects += count_objects(strip_materials);
997     nobjects += count_objects(fan_materials);
998
999     SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
1000
1001     if (version == 7) {
1002         sgWriteUShort( fp, (uint16_t) nobjects );
1003     } else {
1004         sgWriteInt( fp, nobjects );
1005     } 
1006     
1007     // write bounding sphere
1008     write_header( fp, SG_BOUNDING_SPHERE, 0, 1);
1009     sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
1010     sgWritedVec3( fp, gbs_center );
1011     sgWriteFloat( fp, gbs_radius );
1012
1013     // dump vertex list
1014     write_header( fp, SG_VERTEX_LIST, 0, 1);
1015     sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
1016     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1017         sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center));
1018     }
1019
1020     // dump vertex color list
1021     write_header( fp, SG_COLOR_LIST, 0, 1);
1022     sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
1023     for ( i = 0; i < (int)colors.size(); ++i ) {
1024       sgWriteVec4( fp, colors[i]);
1025     }
1026
1027     // dump vertex normal list
1028     write_header( fp, SG_NORMAL_LIST, 0, 1);
1029     sgWriteUInt( fp, normals.size() * 3 );              // nbytes
1030     char normal[3];
1031     for ( i = 0; i < (int)normals.size(); ++i ) {
1032         SGVec3f p = normals[i];
1033         normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
1034         normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
1035         normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
1036         sgWriteBytes( fp, 3, normal );
1037     }
1038
1039     // dump texture coordinates
1040     write_header( fp, SG_TEXCOORD_LIST, 0, 1);
1041     sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
1042     for ( i = 0; i < (int)texcoords.size(); ++i ) {
1043       sgWriteVec2( fp, texcoords[i]);
1044     }
1045
1046     write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
1047     write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
1048     write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
1049     write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
1050     
1051     // close the file
1052     gzclose(fp);
1053
1054     if ( sgWriteError() ) {
1055         cout << "Error while writing file " << file.str() << endl;
1056         return false;
1057     }
1058
1059     return true;
1060 }
1061
1062
1063 // write out the structures to an ASCII file.  We assume that the
1064 // groups come to us sorted by material property.  If not, things
1065 // don't break, but the result won't be as optimal.
1066 bool SGBinObject::write_ascii( const string& base, const string& name,
1067                                const SGBucket& b )
1068 {
1069     int i, j;
1070
1071     SGPath file = base + "/" + b.gen_base_path() + "/" + name;
1072     file.create_dir( 0755 );
1073     cout << "Output file = " << file.str() << endl;
1074
1075     FILE *fp;
1076     if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
1077         cout << "ERROR: opening " << file.str() << " for writing!" << endl;
1078         return false;
1079     }
1080
1081     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
1082          << tri_materials.size() << endl;
1083     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
1084          << strip_materials.size() << endl;
1085     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
1086          << fan_materials.size() << endl;
1087
1088     cout << "points = " << wgs84_nodes.size() << endl;
1089     cout << "tex coords = " << texcoords.size() << endl;
1090     // write headers
1091     fprintf(fp, "# FGFS Scenery\n");
1092     fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
1093
1094     time_t calendar_time = time(NULL);
1095     struct tm *local_tm;
1096     local_tm = localtime( &calendar_time );
1097     char time_str[256];
1098     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
1099     fprintf(fp, "# Created %s\n", time_str );
1100     fprintf(fp, "\n");
1101
1102     // write bounding sphere
1103     fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
1104             gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
1105     fprintf(fp, "\n");
1106
1107     // dump vertex list
1108     fprintf(fp, "# vertex list\n");
1109     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1110         SGVec3d p = wgs84_nodes[i] - gbs_center;
1111         
1112         fprintf(fp,  "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1113     }
1114     fprintf(fp, "\n");
1115
1116     fprintf(fp, "# vertex normal list\n");
1117     for ( i = 0; i < (int)normals.size(); ++i ) {
1118         SGVec3f p = normals[i];
1119         fprintf(fp,  "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1120     }
1121     fprintf(fp, "\n");
1122
1123     // dump texture coordinates
1124     fprintf(fp, "# texture coordinate list\n");
1125     for ( i = 0; i < (int)texcoords.size(); ++i ) {
1126         SGVec2f p = texcoords[i];
1127         fprintf(fp,  "vt %.5f %.5f\n", p.x(), p.y() );
1128     }
1129     fprintf(fp, "\n");
1130
1131     // dump individual triangles if they exist
1132     if ( ! tris_v.empty() ) {
1133         fprintf(fp, "# triangle groups\n");
1134
1135         int start = 0;
1136         int end = 1;
1137         string material;
1138         while ( start < (int)tri_materials.size() ) {
1139             // find next group
1140             material = tri_materials[start];
1141             while ( (end < (int)tri_materials.size()) && 
1142                     (material == tri_materials[end]) )
1143             {
1144                 // cout << "end = " << end << endl;
1145                 end++;
1146             }
1147             // cout << "group = " << start << " to " << end - 1 << endl;
1148
1149       SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1150       for ( i = start; i < end; ++i ) {
1151         for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1152           d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1153         }
1154       }
1155       
1156       SGVec3d bs_center = d.getCenter();
1157       double bs_radius = d.getRadius();
1158             
1159             // write group headers
1160             fprintf(fp, "\n");
1161             fprintf(fp, "# usemtl %s\n", material.c_str());
1162             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1163                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1164
1165             // write groups
1166             for ( i = start; i < end; ++i ) {
1167                 fprintf(fp, "f");
1168                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1169                     fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
1170                 }
1171                 fprintf(fp, "\n");
1172             }
1173
1174             start = end;
1175             end = start + 1;
1176         }
1177     }
1178
1179     // dump triangle groups
1180     if ( ! strips_v.empty() ) {
1181         fprintf(fp, "# triangle strips\n");
1182
1183         int start = 0;
1184         int end = 1;
1185         string material;
1186         while ( start < (int)strip_materials.size() ) {
1187             // find next group
1188             material = strip_materials[start];
1189             while ( (end < (int)strip_materials.size()) && 
1190                     (material == strip_materials[end]) )
1191                 {
1192                     // cout << "end = " << end << endl;
1193                     end++;
1194                 }
1195             // cout << "group = " << start << " to " << end - 1 << endl;
1196
1197
1198       SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1199       for ( i = start; i < end; ++i ) {
1200         for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1201           d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1202         }
1203       }
1204       
1205       SGVec3d bs_center = d.getCenter();
1206       double bs_radius = d.getRadius();
1207
1208             // write group headers
1209             fprintf(fp, "\n");
1210             fprintf(fp, "# usemtl %s\n", material.c_str());
1211             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1212                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1213
1214             // write groups
1215             for ( i = start; i < end; ++i ) {
1216                 fprintf(fp, "ts");
1217                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1218                     fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
1219                 }
1220                 fprintf(fp, "\n");
1221             }
1222             
1223             start = end;
1224             end = start + 1;
1225         }
1226     }
1227
1228     // close the file
1229     fclose(fp);
1230
1231     string command = "gzip --force --best " + file.str();
1232     int err = system(command.c_str());
1233     if (err)
1234     {
1235         cout << "ERROR: gzip " << file.str() << " failed!" << endl;
1236     }
1237
1238     return (err == 0);
1239 }
1240
1241 void SGBinObject::read_properties(gzFile fp, int nproperties)
1242 {
1243     sgSimpleBuffer buf;
1244     uint32_t nbytes;
1245     
1246     // read properties
1247     for ( int j = 0; j < nproperties; ++j ) {
1248         char prop_type;
1249         sgReadChar( fp, &prop_type );
1250         sgReadUInt( fp, &nbytes );
1251         // cout << "property size = " << nbytes << endl;
1252         if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
1253         char *ptr = buf.get_ptr();
1254         sgReadBytes( fp, nbytes, ptr );
1255     }
1256 }
1257
1258 bool SGBinObject::add_point( const SGBinObjectPoint& pt )
1259 {
1260     // add the point info
1261     pt_materials.push_back( pt.material );
1262     
1263     pts_v.push_back( pt.v_list );
1264     pts_n.push_back( pt.n_list );    
1265     pts_c.push_back( pt.c_list );
1266     
1267     return true;
1268 }
1269
1270 bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
1271 {
1272     // add the triangle info and keep lists aligned
1273     tri_materials.push_back( tri.material );
1274     tris_v.push_back( tri.v_list );
1275     tris_n.push_back( tri.n_list );
1276     tris_c.push_back( tri.c_list );
1277     tris_tcs.push_back( tri.tc_list );
1278     tris_vas.push_back( tri.va_list );
1279     
1280     return true;
1281 }