]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/sg_binobj.cxx
Introduce SGBinaryFile
[simgear.git] / simgear / io / sg_binobj.cxx
index 7a58321f14d827a72ffff0def645c84ee78861ca..1c604a28b39fb7e2555324050b3197c026071cae 100644 (file)
@@ -43,6 +43,7 @@
 #include <simgear/bucket/newbucket.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/math/SGGeometry.hxx>
+#include <simgear/structure/exception.hxx>
 
 #include "lowlevel.hxx"
 #include "sg_binobj.hxx"
@@ -57,9 +58,11 @@ enum sgObjectTypes {
     SG_BOUNDING_SPHERE = 0,
 
     SG_VERTEX_LIST = 1,
-    SG_COLOR_LIST = 4,
     SG_NORMAL_LIST = 2,
     SG_TEXCOORD_LIST = 3,
+    SG_COLOR_LIST = 4,
+    SG_VA_FLOAT_LIST = 5,
+    SG_VA_INTEGER_LIST = 6,
 
     SG_POINTS = 9,
 
@@ -69,15 +72,32 @@ enum sgObjectTypes {
 };
 
 enum sgIndexTypes {
-    SG_IDX_VERTICES =  0x01,
-    SG_IDX_NORMALS =   0x02,
-    SG_IDX_COLORS =    0x04,
-    SG_IDX_TEXCOORDS = 0x08
+    SG_IDX_VERTICES =    0x01,
+    SG_IDX_NORMALS =     0x02,
+    SG_IDX_COLORS =      0x04,
+    SG_IDX_TEXCOORDS_0 = 0x08,
+    SG_IDX_TEXCOORDS_1 = 0x10,
+    SG_IDX_TEXCOORDS_2 = 0x20,
+    SG_IDX_TEXCOORDS_3 = 0x40,
+};
+
+enum sgVertexAttributeTypes {
+    // vertex attributes
+    SG_VA_INTEGER_0 = 0x00000001,
+    SG_VA_INTEGER_1 = 0x00000002,
+    SG_VA_INTEGER_2 = 0x00000004,
+    SG_VA_INTEGER_3 = 0x00000008,
+
+    SG_VA_FLOAT_0 =   0x00000100,
+    SG_VA_FLOAT_1 =   0x00000200,
+    SG_VA_FLOAT_2 =   0x00000400,
+    SG_VA_FLOAT_3 =   0x00000800,
 };
 
 enum sgPropertyTypes {
     SG_MATERIAL = 0,
-    SG_INDEX_TYPES = 1
+    SG_INDEX_TYPES = 1,
+    SG_VERT_ATTRIBS = 2
 };
 
 
@@ -129,6 +149,17 @@ public:
         }
     }
     
+    float readInt()
+    {
+        unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
+        if ( sgIsBigEndian() ) {            
+            sgEndianSwap((uint32_t *) p);
+        }
+        
+        offset += sizeof(unsigned int);
+        return *p;
+    }
+    
     SGVec3d readVec3d()
     {
         double* p = reinterpret_cast<double*>(ptr + offset);
@@ -201,15 +232,19 @@ template <class T>
 static void read_indices(char* buffer, 
                          size_t bytes,
                          int indexMask,
+                         int vaMask,
                          int_list& vertices, 
                          int_list& normals,
                          int_list& colors,
-                         int_list& texCoords)
+                         tci_list& texCoords,
+                         vai_list& vas
+                        )
 {
-    const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
-    const int count = bytes / indexSize;
+    const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
+    const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
+    const int count = bytes / (indexSize + vaSize);
     
-// fix endian-ness of the whole lot, if required
+    // fix endian-ness of the whole lot, if required
     if (sgIsBigEndian()) {
         int indices = bytes / sizeof(T);
         T* src = reinterpret_cast<T*>(buffer);
@@ -223,8 +258,31 @@ static void read_indices(char* buffer,
         if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
         if (indexMask & SG_IDX_NORMALS) normals.push_back(*src++);
         if (indexMask & SG_IDX_COLORS) colors.push_back(*src++);
-        if (indexMask & SG_IDX_TEXCOORDS) texCoords.push_back(*src++);
+        if (indexMask & SG_IDX_TEXCOORDS_0) texCoords[0].push_back(*src++);
+        if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
+        if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
+        if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
+        
+        if ( vaMask ) {
+            if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
+            if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
+            if (vaMask & SG_VA_INTEGER_2) vas[2].push_back(*src++);
+            if (vaMask & SG_VA_INTEGER_3) vas[3].push_back(*src++);
+            if (vaMask & SG_VA_FLOAT_0) vas[4].push_back(*src++);
+            if (vaMask & SG_VA_FLOAT_1) vas[5].push_back(*src++);
+            if (vaMask & SG_VA_FLOAT_2) vas[6].push_back(*src++);
+            if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
+        }
     } // of elements in the index
+    
+    // WS2.0 fix : toss zero area triangles
+    if ( ( count == 3 ) && (indexMask & SG_IDX_VERTICES) ) {
+        if ( (vertices[0] == vertices[1]) || 
+             (vertices[1] == vertices[2]) || 
+             (vertices[2] == vertices[0]) ) {
+            vertices.clear();
+        }
+    }
 }
 
 template <class T>
@@ -248,27 +306,68 @@ void write_indice(gzFile fp, uint32_t value)
 
 
 template <class T>
-void write_indices(gzFile fp, unsigned char indexMask, 
+void write_indices(gzFile fp, 
+    unsigned char indexMask, 
+    unsigned int vaMask,
     const int_list& vertices, 
     const int_list& normals, 
     const int_list& colors,
-    const int_list& texCoords)
+    const tci_list& texCoords,
+    const vai_list& vas )
 {
     unsigned int count = vertices.size();
-    const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
-    sgWriteUInt(fp, indexSize * count);
+    const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
+    const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
+    sgWriteUInt(fp, (indexSize + vaSize) * count);
             
     for (unsigned int i=0; i < count; ++i) {
         write_indice(fp, static_cast<T>(vertices[i]));
         
-        if (!normals.empty()) {
+        if (indexMask & SG_IDX_NORMALS) {
             write_indice(fp, static_cast<T>(normals[i]));
         }
-        if (!colors.empty()) {
+        if (indexMask & SG_IDX_COLORS) {
             write_indice(fp, static_cast<T>(colors[i]));
         }
-        if (!texCoords.empty()) {
-            write_indice(fp, static_cast<T>(texCoords[i]));
+        if (indexMask & SG_IDX_TEXCOORDS_0) {
+            write_indice(fp, static_cast<T>(texCoords[0][i]));
+        }
+        if (indexMask & SG_IDX_TEXCOORDS_1) {
+            write_indice(fp, static_cast<T>(texCoords[1][i]));
+        }
+        if (indexMask & SG_IDX_TEXCOORDS_2) {
+            write_indice(fp, static_cast<T>(texCoords[2][i]));
+        }
+        if (indexMask & SG_IDX_TEXCOORDS_3) {
+            write_indice(fp, static_cast<T>(texCoords[3][i]));
+        }
+        
+        if (vaMask) {
+            if (vaMask & SG_VA_INTEGER_0) {
+                write_indice(fp, static_cast<T>(vas[0][i]));
+            }
+            if (vaMask & SG_VA_INTEGER_1) {
+                write_indice(fp, static_cast<T>(vas[1][i]));
+            }
+            if (vaMask & SG_VA_INTEGER_2) {
+                write_indice(fp, static_cast<T>(vas[2][i]));
+            }
+            if (vaMask & SG_VA_INTEGER_3) {
+                write_indice(fp, static_cast<T>(vas[3][i]));
+            }
+
+            if (vaMask & SG_VA_FLOAT_0) {
+                write_indice(fp, static_cast<T>(vas[4][i]));
+            }
+            if (vaMask & SG_VA_FLOAT_1) {
+                write_indice(fp, static_cast<T>(vas[5][i]));
+            }
+            if (vaMask & SG_VA_FLOAT_2) {
+                write_indice(fp, static_cast<T>(vas[6][i]));
+            }
+            if (vaMask & SG_VA_FLOAT_3) {
+                write_indice(fp, static_cast<T>(vas[7][i]));
+            }
         }
     }
 }
@@ -280,13 +379,15 @@ void SGBinObject::read_object( gzFile fp,
                          int nproperties,
                          int nelements,
                          group_list& vertices, 
-                          group_list& normals,
-                          group_list& colors,
-                          group_list& texCoords,
-                          string_list& materials)
+                         group_list& normals,
+                         group_list& colors,
+                         group_tci_list& texCoords,
+                         group_vai_list& vertexAttribs,
+                         string_list& materials)
 {
-    unsigned int nbytes;
+    unsigned int  nbytes;
     unsigned char idx_mask;
+    unsigned int  vertex_attrib_mask;
     int j;
     sgSimpleBuffer buf( 32768 );  // 32 Kb
     char material[256];
@@ -295,39 +396,65 @@ void SGBinObject::read_object( gzFile fp,
     if ( obj_type == SG_POINTS ) {
         idx_mask = SG_IDX_VERTICES;
     } else {
-        idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS);
+        idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
     }
-
+    vertex_attrib_mask = 0;
+    
     for ( j = 0; j < nproperties; ++j ) {
         char prop_type;
         sgReadChar( fp, &prop_type );
         sgReadUInt( fp, &nbytes );
+        
         buf.resize(nbytes);
         char *ptr = buf.get_ptr();
-        sgReadBytes( fp, nbytes, ptr );
-        if ( prop_type == SG_MATERIAL ) {
-            if (nbytes > 255) {
-                nbytes = 255;
-            }
-            strncpy( material, ptr, nbytes );
-            material[nbytes] = '\0';
-            // cout << "material type = " << material << endl;
-        } else if ( prop_type == SG_INDEX_TYPES ) {
-            idx_mask = ptr[0];
-            //cout << std::hex << "index mask:" << idx_mask << std::dec << endl;
+        
+        switch( prop_type )
+        {
+            case SG_MATERIAL:
+                sgReadBytes( fp, nbytes, ptr );
+                if (nbytes > 255) {
+                    nbytes = 255;
+                }
+                strncpy( material, ptr, nbytes );
+                material[nbytes] = '\0';
+                break;
+                
+            case SG_INDEX_TYPES:
+                if (nbytes == 1) {
+                    sgReadChar( fp, (char *)&idx_mask );                    
+                } else {
+                    sgReadBytes( fp, nbytes, ptr );                    
+                }
+                break;
+
+            case SG_VERT_ATTRIBS:
+                if (nbytes == 4) {
+                    sgReadUInt( fp, &vertex_attrib_mask );
+                } else {
+                    sgReadBytes( fp, nbytes, ptr );
+                }
+                break;
+                
+            default:
+                sgReadBytes( fp, nbytes, ptr );
+                SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
+                break;
         }
     }
 
     if ( sgReadError() ) {
-        cout << "We detected an error reading object properties"  << endl;
-        return;
+        throw sg_exception("Error reading object properties");
+    }
+    
+    size_t indexCount = std::bitset<32>((int)idx_mask).count();
+    if (indexCount == 0) {
+        throw sg_exception("object index mask has no bits set");
     }
     
     for ( j = 0; j < nelements; ++j ) {
         sgReadUInt( fp, &nbytes );
         if ( sgReadError() ) {
-            cout << "We detected an error reading element size for :" << j << endl;
-            return;
+            throw sg_exception("Error reading element size");
         }
         
         buf.resize( nbytes );
@@ -335,25 +462,30 @@ void SGBinObject::read_object( gzFile fp,
         sgReadBytes( fp, nbytes, ptr );
         
         if ( sgReadError() ) {
-            cout << "We detected an error reading object element:" << j << "bytes="<< nbytes  << endl;
-            return;
+            throw sg_exception("Error reading element bytes");
         }
                 
         int_list vs;
         int_list ns;
         int_list cs;
-        int_list tcs;
+        tci_list tcs;
+        vai_list vas;
+
         if (version >= 10) {
-            read_indices<uint32_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
+            read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
         } else {
-            read_indices<uint16_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
+            read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
         }
 
-        vertices.push_back( vs );
-        normals.push_back( ns );
-        colors.push_back( cs );
-        texCoords.push_back( tcs );
-        materials.push_back( material );
+        // Fix for WS2.0 - ignore zero area triangles
+        if ( !vs.empty() ) {
+            vertices.push_back( vs );
+            normals.push_back( ns );
+            colors.push_back( cs );
+            texCoords.push_back( tcs );
+            vertexAttribs.push_back( vas );
+            materials.push_back( material );
+        }
     } // of element iteration
 }
 
@@ -377,25 +509,29 @@ bool SGBinObject::read_bin( const string& file ) {
     pts_v.clear();
     pts_n.clear();
     pts_c.clear();
-    pts_tc.clear();
+    pts_tcs.clear();
+    pts_vas.clear();
     pt_materials.clear();
 
     tris_v.clear();
     tris_n.clear();
     tris_c.clear();
-    tris_tc.clear();
+    tris_tcs.clear();
+    tris_vas.clear();
     tri_materials.clear();
 
     strips_v.clear();
     strips_n.clear();
     strips_c.clear();
-    strips_tc.clear();
+    strips_tcs.clear();
+    strips_vas.clear();
     strip_materials.clear();
 
     fans_v.clear();
     fans_n.clear();
     fans_c.clear();
-    fans_tc.clear();
+    fans_tcs.clear();
+    fans_vas.clear();
     fan_materials.clear();
 
     gzFile fp;
@@ -405,7 +541,7 @@ bool SGBinObject::read_bin( const string& file ) {
             SG_LOG( SG_EVENT, SG_ALERT,
                "ERROR: opening " << file << " or " << filegz << " for reading!");
 
-            return false;
+            throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
         }
     }
 
@@ -416,17 +552,15 @@ bool SGBinObject::read_bin( const string& file ) {
     sgReadUInt( fp, &header );
     if ( ((header & 0xFF000000) >> 24) == 'S' &&
          ((header & 0x00FF0000) >> 16) == 'G' ) {
-        // cout << "Good header" << endl;
+    
         // read file version
         version = (header & 0x0000FFFF);
-        // cout << "File version = " << version << endl;
     } else {
         // close the file before we return
         gzclose(fp);
-
-        return false;
+        throw sg_io_exception("Bad BTG magic/version", sg_location(file));
     }
-
+    
     // read creation time
     unsigned int foo_calendar_time;
     sgReadUInt( fp, &foo_calendar_time );
@@ -458,11 +592,10 @@ bool SGBinObject::read_bin( const string& file ) {
         nobjects = v;
     }
      
-     //cout << "Total objects to read = " << nobjects << endl;
+    SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
 
     if ( sgReadError() ) {
-        cout << "We detected an error while reading the file header" << endl;
-        return false;
+        throw sg_io_exception("Error reading BTG file header", sg_location(file));
     }
     
     // read in objects
@@ -488,9 +621,10 @@ bool SGBinObject::read_bin( const string& file ) {
             nelements = v;
         }
 
-         //cout << "object " << i << " = " << (int)obj_type << " props = "
-         //     << nproperties << " elements = " << nelements << endl;
-            
+        SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i << 
+                " = " << (int)obj_type << " props = " << nproperties << 
+                " elements = " << nelements);
+         
         if ( obj_type == SG_BOUNDING_SPHERE ) {
             // read bounding sphere properties
             read_properties( fp, nproperties );
@@ -520,7 +654,7 @@ bool SGBinObject::read_bin( const string& file ) {
                 wgs84_nodes.reserve( count );
                 for ( k = 0; k < count; ++k ) {
                     SGVec3f v = buf.readVec3f();
-                // extend from float to double, hmmm
+                    // extend from float to double, hmmm
                     wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
                 }
             }
@@ -580,23 +714,60 @@ bool SGBinObject::read_bin( const string& file ) {
                     texcoords.push_back( buf.readVec2f() );
                 }
             }
+        } else if ( obj_type == SG_VA_FLOAT_LIST ) {
+            // read vertex attribute (float) properties
+            read_properties( fp, nproperties );
+            
+            // read vertex attribute list elements
+            for ( j = 0; j < nelements; ++j ) {
+                sgReadUInt( fp, &nbytes );
+                buf.resize( nbytes );
+                buf.reset();
+                char *ptr = buf.get_ptr();
+                sgReadBytes( fp, nbytes, ptr );
+                int count = nbytes / (sizeof(float));
+                va_flt.reserve(count);
+                for ( k = 0; k < count; ++k ) {
+                    va_flt.push_back( buf.readFloat() );
+                }
+            }
+        } else if ( obj_type == SG_VA_INTEGER_LIST ) {
+            // read vertex attribute (integer) properties
+            read_properties( fp, nproperties );
+            
+            // read vertex attribute list elements
+            for ( j = 0; j < nelements; ++j ) {
+                sgReadUInt( fp, &nbytes );
+                buf.resize( nbytes );
+                buf.reset();
+                char *ptr = buf.get_ptr();
+                sgReadBytes( fp, nbytes, ptr );
+                int count = nbytes / (sizeof(unsigned int));
+                va_int.reserve(count);
+                for ( k = 0; k < count; ++k ) {
+                    va_int.push_back( buf.readInt() );
+                }
+            }
         } else if ( obj_type == SG_POINTS ) {
             // read point elements
             read_object( fp, SG_POINTS, nproperties, nelements,
-                         pts_v, pts_n, pts_c, pts_tc, pt_materials );
+                         pts_v, pts_n, pts_c, pts_tcs,
+                         pts_vas, pt_materials );
         } else if ( obj_type == SG_TRIANGLE_FACES ) {
             // read triangle face properties
             read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
-                         tris_v, tris_n, tris_c, tris_tc, tri_materials );
+                         tris_v, tris_n, tris_c, tris_tcs,
+                         tris_vas, tri_materials );
         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
             // read triangle strip properties
             read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
-                         strips_v, strips_n, strips_c, strips_tc,
-                         strip_materials );
+                         strips_v, strips_n, strips_c, strips_tcs,
+                         strips_vas, strip_materials );
         } else if ( obj_type == SG_TRIANGLE_FANS ) {
             // read triangle fan properties
             read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
-                         fans_v, fans_n, fans_c, fans_tc, fan_materials );
+                         fans_v, fans_n, fans_c, fans_tcs,
+                         fans_vas, fan_materials );
         } else {
             // unknown object type, just skip
             read_properties( fp, nproperties );
@@ -612,19 +783,13 @@ bool SGBinObject::read_bin( const string& file ) {
         }
         
         if ( sgReadError() ) {
-            cout << "We detected an error while reading object:" << i << endl;
-            return false;
+            throw sg_io_exception("Error while reading object", sg_location(file, i));
         }
     }
 
     // close the file
     gzclose(fp);
 
-    if ( sgReadError() ) {
-        cout << "We detected an error while reading the file." << endl;
-        return false;
-    }
-
     return true;
 }
 
@@ -657,45 +822,95 @@ unsigned int SGBinObject::count_objects(const string_list& materials)
     return result;
 }
 
-void SGBinObject::write_objects(gzFile fp, int type, const group_list& verts,
-    const group_list& normals, const group_list& colors, 
-    const group_list& texCoords, const string_list& materials)
+void SGBinObject::write_objects(gzFile fp, int type, 
+                                const group_list& verts,
+                                const group_list& normals, 
+                                const group_list& colors, 
+                                const group_tci_list& texCoords,
+                                const group_vai_list& vertexAttribs, 
+                                const string_list& materials)
 {
     if (verts.empty()) {
         return;
     }
-    
+        
     unsigned int start = 0, end = 1;
     string m;
+    int_list emptyList;
+    
     while (start < materials.size()) {
         m = materials[start];
-    // find range of objects with identical material, write out as a single object
+        // find range of objects with identical material, write out as a single object
         for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
     
+        // calc the number of elements
         const int count = end - start;
-        write_header(fp, type, 2, count);
+
+        // calc the number of properties
+        unsigned int va_mask = 0;
+        unsigned int va_count = vertexAttribs.size();
+        for ( unsigned int va=0; va<va_count; va++ ) {
+            if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
+                va_mask |= ( 1 << va );
+            }
+        }
+
+        if ( va_mask ) {
+            write_header(fp, type, 3, count);
+        } else {
+            write_header(fp, type, 2, count);
+        }
         
     // properties
+        // material property
         sgWriteChar( fp, (char)SG_MATERIAL );        // property
         sgWriteUInt( fp, m.length() );        // nbytes
         sgWriteBytes( fp, m.length(), m.c_str() );
     
+        // index mask property
         unsigned char idx_mask = 0;
-        if ( !verts.empty() && !verts.front().empty()) idx_mask |= SG_IDX_VERTICES;
-        if ( !normals.empty() && !normals.front().empty()) idx_mask |= SG_IDX_NORMALS;
-        if ( !colors.empty() && !colors.front().empty()) idx_mask |= SG_IDX_COLORS;
-        if ( !texCoords.empty() && !texCoords.front().empty()) idx_mask |= SG_IDX_TEXCOORDS;
+        if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
+        if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
+        if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
+        if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
+        if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
+        if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
+        if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
+        
+        if (idx_mask == 0) {
+            SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
+                << m << "has no indices set");
+        }
+
         sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
         sgWriteUInt( fp, 1 );                        // nbytes
         sgWriteChar( fp, idx_mask );
-    
-//        cout << "material:" << m << ", count =" << count << endl;
+
+        // vertex attribute property
+        if (va_mask != 0) {
+            sgWriteChar( fp, (char)SG_VERT_ATTRIBS );    // property
+            sgWriteUInt( fp, 4 );                        // nbytes
+            sgWriteChar( fp, va_mask );
+        }
+        
     // elements
         for (unsigned int i=start; i < end; ++i) {
+            const int_list& va(verts[i]);
+            const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
+            const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
+
+            // pass the whole texcoord array - we'll figure out which indicies to write 
+            // in write_indices
+            const tci_list& tca( texCoords[i] );
+
+            // pass the whole vertex array - we'll figure out which indicies to write 
+            // in write_indices
+            const vai_list& vaa( vertexAttribs[i] );
+            
             if (version == 7) {
-                write_indices<uint16_t>(fp, idx_mask, verts[i], normals[i], colors[i], texCoords[i]);
+                write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
             } else {
-                write_indices<uint32_t>(fp, idx_mask, verts[i], normals[i], colors[i], texCoords[i]);
+                write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
             }
         }
     
@@ -714,13 +929,32 @@ bool SGBinObject::write_bin( const string& base, const string& name,
     return write_bin_file(file);
 }
 
+static unsigned int max_object_size( const string_list& materials )
+{
+    unsigned int max_size = 0;
+
+    for (unsigned int start=0; start < materials.size();) {
+        string m = materials[start];
+        unsigned int end = start + 1;
+        // find range of objects with identical material, calc its size
+        for (; (end < materials.size()) && (m == materials[end]); ++end) {}
+
+        unsigned int cur_size = end - start;
+        max_size = std::max(max_size, cur_size);
+        start = end;
+    }
+
+    return max_size;
+}
+
+const unsigned int VERSION_7_MATERIAL_LIMIT = 0x7fff;
+
 bool SGBinObject::write_bin_file(const SGPath& file)
 {
     int i;
     
     SGPath file2(file);
     file2.create_dir( 0755 );
-    cout << "Output file = " << file.str() << endl;
 
     gzFile fp;
     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
@@ -730,22 +964,31 @@ bool SGBinObject::write_bin_file(const SGPath& file)
 
     sgClearWriteError();
 
-    cout << "points size = " << pts_v.size() << "  pt_materials = " 
-         << pt_materials.size() << endl;
-    cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
-         << tri_materials.size() << endl;
-    cout << "strips size = " << strips_v.size() << "  strip_materials = " 
-         << strip_materials.size() << endl;
-    cout << "fans size = " << fans_v.size() << "  fan_materials = " 
-         << fan_materials.size() << endl;
+    SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size() 
+         << "  pt_materials = " << pt_materials.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size() 
+         << "  tri_materials = " << tri_materials.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size() 
+         << "  strip_materials = " << strip_materials.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size() 
+         << "  fan_materials = " << fan_materials.size() );
 
-    cout << "nodes = " << wgs84_nodes.size() << endl;
-    cout << "colors = " << colors.size() << endl;
-    cout << "normals = " << normals.size() << endl;
-    cout << "tex coords = " << texcoords.size() << endl;
+    SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
+    SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
 
     version = 10;
-    if (wgs84_nodes.size() < 0xffff) {
+    bool shortMaterialsRanges = 
+        (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
+        (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
+        (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
+        (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
+                    
+    if ((wgs84_nodes.size() < 0xffff) &&
+        (normals.size() < 0xffff) &&
+        (texcoords.size() < 0xffff) &&
+        shortMaterialsRanges) {
         version = 7; // use smaller indices if possible
     }
 
@@ -765,7 +1008,8 @@ bool SGBinObject::write_bin_file(const SGPath& file)
     nobjects += count_objects(strip_materials);
     nobjects += count_objects(fan_materials);
 
-    cout << "total top level objects = " << nobjects << endl;
+    SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
+
     if (version == 7) {
         sgWriteUShort( fp, (uint16_t) nobjects );
     } else {
@@ -811,16 +1055,16 @@ bool SGBinObject::write_bin_file(const SGPath& file)
       sgWriteVec2( fp, texcoords[i]);
     }
 
-    write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tc, pt_materials);
-    write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tc, tri_materials);
-    write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tc, strip_materials);
-    write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tc, fan_materials);
+    write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
+    write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
+    write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
+    write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
     
     // close the file
     gzclose(fp);
 
     if ( sgWriteError() ) {
-        cout << "We detected an error while writing the file." << endl;
+        cout << "Error while writing file " << file.str() << endl;
         return false;
     }
 
@@ -897,7 +1141,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
     fprintf(fp, "\n");
 
     // dump individual triangles if they exist
-    if ( tris_v.size() > 0 ) {
+    if ( ! tris_v.empty() ) {
         fprintf(fp, "# triangle groups\n");
 
         int start = 0;
@@ -914,7 +1158,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
             }
             // cout << "group = " << start << " to " << end - 1 << endl;
 
-      SGSphered d;
+      SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
       for ( i = start; i < end; ++i ) {
         for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
           d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
@@ -934,7 +1178,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
             for ( i = start; i < end; ++i ) {
                 fprintf(fp, "f");
                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
-                    fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
+                    fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
                 }
                 fprintf(fp, "\n");
             }
@@ -945,7 +1189,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
     }
 
     // dump triangle groups
-    if ( strips_v.size() > 0 ) {
+    if ( ! strips_v.empty() ) {
         fprintf(fp, "# triangle strips\n");
 
         int start = 0;
@@ -963,7 +1207,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
             // cout << "group = " << start << " to " << end - 1 << endl;
 
 
-      SGSphered d;
+      SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
       for ( i = start; i < end; ++i ) {
         for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
           d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
@@ -983,7 +1227,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
             for ( i = start; i < end; ++i ) {
                 fprintf(fp, "ts");
                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
-                    fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
+                    fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
                 }
                 fprintf(fp, "\n");
             }
@@ -997,9 +1241,13 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
     fclose(fp);
 
     string command = "gzip --force --best " + file.str();
-    system(command.c_str());
+    int err = system(command.c_str());
+    if (err)
+    {
+        cout << "ERROR: gzip " << file.str() << " failed!" << endl;
+    }
 
-    return true;
+    return (err == 0);
 }
 
 void SGBinObject::read_properties(gzFile fp, int nproperties)
@@ -1019,3 +1267,27 @@ void SGBinObject::read_properties(gzFile fp, int nproperties)
     }
 }
 
+bool SGBinObject::add_point( const SGBinObjectPoint& pt )
+{
+    // add the point info
+    pt_materials.push_back( pt.material );
+    
+    pts_v.push_back( pt.v_list );
+    pts_n.push_back( pt.n_list );    
+    pts_c.push_back( pt.c_list );
+    
+    return true;
+}
+
+bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
+{
+    // add the triangle info and keep lists aligned
+    tri_materials.push_back( tri.material );
+    tris_v.push_back( tri.v_list );
+    tris_n.push_back( tri.n_list );
+    tris_c.push_back( tri.c_list );
+    tris_tcs.push_back( tri.tc_list );
+    tris_vas.push_back( tri.va_list );
+    
+    return true;
+}
\ No newline at end of file