Prechádzať zdrojové kódy

Merge pull request #633 from heandreas/master

Multiple meshes in single STL ascii file and binary bly exporter fix
Kim Kulling 10 rokov pred
rodič
commit
2a432f9adc
2 zmenil súbory, kde vykonal 174 pridanie a 133 odobranie
  1. 18 5
      code/PlyExporter.cpp
  2. 156 128
      code/STLLoader.cpp

+ 18 - 5
code/PlyExporter.cpp

@@ -189,7 +189,12 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina
     }
 
     mOutput << "element face " << faces << endl;
-    mOutput << "property list uint uint vertex_index" << endl;
+
+    // uchar seems to be the most common type for the number of indices per polygon and int seems to be most common for the vertex indices.
+    // For instance, MeshLab fails to load meshes in which both types are uint. Houdini seems to have problems as well.
+    // Obviously, using uchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case?
+    mOutput << "property list uchar int vertex_index" << endl;
+
     mOutput << "end_header" << endl;
 
     for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
@@ -342,16 +347,24 @@ void PlyExporter::WriteMeshIndices(const aiMesh* m, unsigned int offset)
     }
 }
 
-void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset)
+// Generic method in case we want to use different data types for the indices or make this configurable.
+template<typename NumIndicesType, typename IndexType>
+void WriteMeshIndicesBinary_Generic(const aiMesh* m, unsigned int offset, std::ostringstream& output)
 {
     for (unsigned int i = 0; i < m->mNumFaces; ++i) {
         const aiFace& f = m->mFaces[i];
-        mOutput.write(reinterpret_cast<const char*>(&f.mNumIndices), 4);
+        NumIndicesType numIndices = static_cast<NumIndicesType>(f.mNumIndices);
+        output.write(reinterpret_cast<const char*>(&numIndices), sizeof(NumIndicesType));
         for (unsigned int c = 0; c < f.mNumIndices; ++c) {
-            unsigned int index = f.mIndices[c] + offset;
-            mOutput.write(reinterpret_cast<const char*>(&index), 4);
+            IndexType index = f.mIndices[c] + offset;
+            output.write(reinterpret_cast<const char*>(&index), sizeof(IndexType));
         }
     }
 }
 
+void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset)
+{
+    WriteMeshIndicesBinary_Generic<unsigned char, int>(m, offset, mOutput);
+}
+
 #endif

+ 156 - 128
code/STLLoader.cpp

@@ -154,6 +154,19 @@ const aiImporterDesc* STLImporter::GetInfo () const {
     return &desc;
 }
 
+void addFacesToMesh(aiMesh* pMesh)
+{
+    pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+    for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i)    {
+
+        aiFace& face = pMesh->mFaces[i];
+        face.mIndices = new unsigned int[face.mNumIndices = 3];
+        for (unsigned int o = 0; o < 3;++o,++p) {
+            face.mIndices[o] = p;
+        }
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void STLImporter::InternReadFile( const std::string& pFile,
@@ -179,17 +192,8 @@ void STLImporter::InternReadFile( const std::string& pFile,
     // the default vertex color is light gray.
     clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 0.6f;
 
-    // allocate one mesh
-    pScene->mNumMeshes = 1;
-    pScene->mMeshes = new aiMesh*[1];
-    aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
-    pMesh->mMaterialIndex = 0;
-
     // allocate a single node
     pScene->mRootNode = new aiNode();
-    pScene->mRootNode->mNumMeshes = 1;
-    pScene->mRootNode->mMeshes = new unsigned int[1];
-    pScene->mRootNode->mMeshes[0] = 0;
 
     bool bMatClr = false;
 
@@ -201,16 +205,12 @@ void STLImporter::InternReadFile( const std::string& pFile,
         throw DeadlyImportError( "Failed to determine STL storage representation for " + pFile + ".");
     }
 
-    // now copy faces
-    pMesh->mFaces = new aiFace[pMesh->mNumFaces];
-    for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i)   {
-
-        aiFace& face = pMesh->mFaces[i];
-        face.mIndices = new unsigned int[face.mNumIndices = 3];
-        for (unsigned int o = 0; o < 3;++o,++p) {
-            face.mIndices[o] = p;
-        }
-    }
+    // add all created meshes to the single node
+    pScene->mRootNode = new aiNode();
+    pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+    pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+    for (unsigned int i = 0; i < pScene->mNumMeshes; i++)
+        pScene->mRootNode->mMeshes[i] = i;
 
     // create a single default material, using a light gray diffuse color for consistency with
     // other geometric types (e.g., PLY).
@@ -236,140 +236,165 @@ void STLImporter::InternReadFile( const std::string& pFile,
 // Read an ASCII STL file
 void STLImporter::LoadASCIIFile()
 {
-    aiMesh* pMesh = pScene->mMeshes[0];
-
+    std::vector<aiMesh*> meshes;
     const char* sz = mBuffer;
-    SkipSpaces(&sz);
-    ai_assert(!IsLineEnd(sz));
-
-    sz += 5; // skip the "solid"
-    SkipSpaces(&sz);
-    const char* szMe = sz;
-    while (!::IsSpaceOrNewLine(*sz)) {
-        sz++;
-    }
-
-    size_t temp;
-    // setup the name of the node
-    if ((temp = (size_t)(sz-szMe))) {
-        if (temp >= MAXLEN) {
-            throw DeadlyImportError( "STL: Node name too long" );
-        }
-
-        pScene->mRootNode->mName.length = temp;
-        memcpy(pScene->mRootNode->mName.data,szMe,temp);
-        pScene->mRootNode->mName.data[temp] = '\0';
-    }
-    else pScene->mRootNode->mName.Set("<STL_ASCII>");
+    const char* bufferEnd = mBuffer + fileSize;
+    std::vector<aiVector3D> positionBuffer;
+    std::vector<aiVector3D> normalBuffer;
 
     // try to guess how many vertices we could have
     // assume we'll need 160 bytes for each face
-    pMesh->mNumVertices = ( pMesh->mNumFaces = std::max(1u,fileSize / 160u )) * 3;
-    pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
-    pMesh->mNormals  = new aiVector3D[pMesh->mNumVertices];
+    size_t sizeEstimate = std::max(1u, fileSize / 160u ) * 3;
+    positionBuffer.reserve(sizeEstimate);
+    normalBuffer.reserve(sizeEstimate);
 
-    unsigned int curFace = 0, curVertex = 3;
-    for ( ;; )
+    while (IsAsciiSTL(sz, bufferEnd - sz))
     {
-        // go to the next token
-        if(!SkipSpacesAndLineEnd(&sz))
-        {
-            // seems we're finished although there was no end marker
-            DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
-            break;
+        aiMesh* pMesh = new aiMesh();
+        pMesh->mMaterialIndex = 0;
+        meshes.push_back(pMesh);
+
+        SkipSpaces(&sz);
+        ai_assert(!IsLineEnd(sz));
+
+        sz += 5; // skip the "solid"
+        SkipSpaces(&sz);
+        const char* szMe = sz;
+        while (!::IsSpaceOrNewLine(*sz)) {
+            sz++;
         }
-        // facet normal -0.13 -0.13 -0.98
-        if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5)) && *(sz + 5) != '\0')    {
 
-            if (3 != curVertex) {
-                DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
+        size_t temp;
+        // setup the name of the node
+        if ((temp = (size_t)(sz-szMe)))    {
+            if (temp >= MAXLEN) {
+                throw DeadlyImportError( "STL: Node name too long" );
             }
-            if (pMesh->mNumFaces == curFace)    {
-                ai_assert(pMesh->mNumFaces != 0);
-
-                // need to resize the arrays, our size estimate was wrong
-                unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces;
-                if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping
-                unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize;
-                add += add >> 3; // add 12.5% as buffer
-                iNeededSize = (pMesh->mNumFaces + add)*3;
-                aiVector3D* pv = new aiVector3D[iNeededSize];
-                memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D));
-                delete[] pMesh->mVertices;
-                pMesh->mVertices = pv;
-                pv = new aiVector3D[iNeededSize];
-                memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D));
-                delete[] pMesh->mNormals;
-                pMesh->mNormals = pv;
-
-                pMesh->mNumVertices = iNeededSize;
-                pMesh->mNumFaces += add;
+
+            pScene->mRootNode->mName.length = temp;
+            memcpy(pScene->mRootNode->mName.data,szMe,temp);
+            pScene->mRootNode->mName.data[temp] = '\0';
+        }
+        else pScene->mRootNode->mName.Set("<STL_ASCII>");
+
+        unsigned int faceVertexCounter = 0;
+        for ( ;; )
+        {
+            // go to the next token
+            if(!SkipSpacesAndLineEnd(&sz))
+            {
+                // seems we're finished although there was no end marker
+                DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
+                break;
             }
-            aiVector3D* vn = &pMesh->mNormals[curFace++*3];
+            // facet normal -0.13 -0.13 -0.98
+            if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5)) && *(sz + 5) != '\0')    {
 
-            sz += 6;
-            curVertex = 0;
-            SkipSpaces(&sz);
-            if (strncmp(sz,"normal",6)) {
-                DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
+                if (faceVertexCounter != 3) {
+                    DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
+                }
+                faceVertexCounter = 0;
+                normalBuffer.push_back(aiVector3D());
+                aiVector3D* vn = &normalBuffer.back();
+
+                sz += 6;
+                SkipSpaces(&sz);
+                if (strncmp(sz,"normal",6))    {
+                    DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
+                }
+                else
+                {
+                    sz += 7;
+                    SkipSpaces(&sz);
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->x );
+                    SkipSpaces(&sz);
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->y );
+                    SkipSpaces(&sz);
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->z );
+                    normalBuffer.push_back(*vn);
+                    normalBuffer.push_back(*vn);
+                }
             }
-            else
+            // vertex 1.50000 1.50000 0.00000
+            else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
             {
-                sz += 7;
-                SkipSpaces(&sz);
-                sz = fast_atoreal_move<float>(sz, (float&)vn->x );
-                SkipSpaces(&sz);
-                sz = fast_atoreal_move<float>(sz, (float&)vn->y );
-                SkipSpaces(&sz);
-                sz = fast_atoreal_move<float>(sz, (float&)vn->z );
-                *(vn+1) = *vn;
-                *(vn+2) = *vn;
+                if (faceVertexCounter >= 3) {
+                    DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
+                    ++sz;
+                }
+                else
+                {
+                    sz += 7;
+                    SkipSpaces(&sz);
+                    positionBuffer.push_back(aiVector3D());
+                    aiVector3D* vn = &positionBuffer.back();
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->x );
+                    SkipSpaces(&sz);
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->y );
+                    SkipSpaces(&sz);
+                    sz = fast_atoreal_move<float>(sz, (float&)vn->z );
+                    faceVertexCounter++;
+                }
             }
-        }
-        // vertex 1.50000 1.50000 0.00000
-        else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
-        {
-            if (3 == curVertex) {
-                DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
-                ++sz;
+            else if (!::strncmp(sz,"endsolid",8))    {
+                do {
+                    ++sz;
+                } while (!::IsLineEnd(*sz));
+                SkipSpacesAndLineEnd(&sz);
+                // finished!
+                break;
             }
-            else
-            {
-                sz += 7;
-                SkipSpaces(&sz);
-                aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
-                sz = fast_atoreal_move<float>(sz, (float&)vn->x );
-                SkipSpaces(&sz);
-                sz = fast_atoreal_move<float>(sz, (float&)vn->y );
-                SkipSpaces(&sz);
-                sz = fast_atoreal_move<float>(sz, (float&)vn->z );
+            // else skip the whole identifier
+            else {
+                do {
+                    ++sz;
+                } while (!::IsSpaceOrNewLine(*sz));
             }
         }
-        else if (!::strncmp(sz,"endsolid",8))   {
-            // finished!
-            break;
+
+        if (positionBuffer.empty())    {
+            pMesh->mNumFaces = 0;
+            throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
+        }
+        if (positionBuffer.size() % 3 != 0)    {
+            pMesh->mNumFaces = 0;
+            throw DeadlyImportError("STL: Invalid number of vertices");
         }
-        // else skip the whole identifier
-        else {
-            do {
-                ++sz;
-            } while (!::IsSpaceOrNewLine(*sz));
+        if (normalBuffer.size() != positionBuffer.size())    {
+            pMesh->mNumFaces = 0;
+            throw DeadlyImportError("Normal buffer size does not match position buffer size");
         }
+        pMesh->mNumFaces = positionBuffer.size() / 3;
+        pMesh->mNumVertices = positionBuffer.size();
+        pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+        memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+        positionBuffer.clear();
+        pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+        memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+        normalBuffer.clear();
+
+        // now copy faces
+        addFacesToMesh(pMesh);
     }
-
-    if (!curFace)   {
-        pMesh->mNumFaces = 0;
-        throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
+    // now add the loaded meshes
+    pScene->mNumMeshes = (unsigned int)meshes.size();
+    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+    for (size_t i = 0; i < meshes.size(); i++)
+    {
+        pScene->mMeshes[i] = meshes[i];
     }
-    pMesh->mNumFaces = curFace;
-    pMesh->mNumVertices = curFace*3;
-    // we are finished!
 }
 
 // ------------------------------------------------------------------------------------------------
 // Read a binary STL file
 bool STLImporter::LoadBinaryFile()
 {
+    // allocate one mesh
+    pScene->mNumMeshes = 1;
+    pScene->mMeshes = new aiMesh*[1];
+    aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
+    pMesh->mMaterialIndex = 0;
+
     // skip the first 80 bytes
     if (fileSize < 84) {
         throw DeadlyImportError("STL: file is too small for the header");
@@ -397,7 +422,6 @@ bool STLImporter::LoadBinaryFile()
     const unsigned char* sz = (const unsigned char*)mBuffer + 80;
 
     // now read the number of facets
-    aiMesh* pMesh = pScene->mMeshes[0];
     pScene->mRootNode->mName.Set("<STL_BINARY>");
 
     pMesh->mNumFaces = *((uint32_t*)sz);
@@ -417,7 +441,7 @@ bool STLImporter::LoadBinaryFile()
     vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
     vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
 
-    for (unsigned int i = 0; i < pMesh->mNumFaces;++i)  {
+    for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
 
         // NOTE: Blender sometimes writes empty normals ... this is not
         // our fault ... the RemoveInvalidData helper step should fix that
@@ -470,6 +494,10 @@ bool STLImporter::LoadBinaryFile()
             *(clr+2) = *clr;
         }
     }
+
+    // now copy faces
+    addFacesToMesh(pMesh);
+
     if (bIsMaterialise && !pMesh->mColors[0])
     {
         // use the color as diffuse material color