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