From 59b08198666da731c80458322dc7b84577aab166 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 19 Mar 2015 17:27:06 +0100 Subject: [PATCH 1/5] STL loader can now handle more than one mesh in a single ascii file. --- code/STLLoader.cpp | 308 ++++++++++++++++++++++++--------------------- 1 file changed, 168 insertions(+), 140 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 987de89d0..8f2a4e18e 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -131,6 +131,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, @@ -156,17 +169,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; + // allocate a single node + pScene->mRootNode = new aiNode(); bool bMatClr = false; @@ -178,16 +182,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 (uint 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). @@ -213,140 +213,165 @@ void STLImporter::InternReadFile( const std::string& pFile, // Read an ASCII STL file void STLImporter::LoadASCIIFile() { - aiMesh* pMesh = pScene->mMeshes[0]; + std::vector meshes; + const char* sz = mBuffer; + const char* bufferEnd = mBuffer + fileSize; + std::vector positionBuffer; + std::vector normalBuffer; - const char* sz = mBuffer; - SkipSpaces(&sz); - ai_assert(!IsLineEnd(sz)); + // try to guess how many vertices we could have + // assume we'll need 160 bytes for each face + size_t sizeEstimate = std::max(1u, fileSize / 160u ) * 3; + positionBuffer.reserve(sizeEstimate); + normalBuffer.reserve(sizeEstimate); - sz += 5; // skip the "solid" - SkipSpaces(&sz); - const char* szMe = sz; - while (!::IsSpaceOrNewLine(*sz)) { - sz++; - } + while (IsAsciiSTL(sz, bufferEnd - sz)) + { + aiMesh* pMesh = new aiMesh(); + pMesh->mMaterialIndex = 0; + meshes.push_back(pMesh); - 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" ); - } + SkipSpaces(&sz); + ai_assert(!IsLineEnd(sz)); - pScene->mRootNode->mName.length = temp; - memcpy(pScene->mRootNode->mName.data,szMe,temp); - pScene->mRootNode->mName.data[temp] = '\0'; - } - else pScene->mRootNode->mName.Set(""); + sz += 5; // skip the "solid" + SkipSpaces(&sz); + const char* szMe = sz; + while (!::IsSpaceOrNewLine(*sz)) { + sz++; + } - // 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]; - - unsigned int curFace = 0, curVertex = 3; - 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; - } - // facet normal -0.13 -0.13 -0.98 - if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { + 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 (3 != curVertex) { - DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete"); - } - if (pMesh->mNumFaces == curFace) { - ai_assert(pMesh->mNumFaces != 0); + pScene->mRootNode->mName.length = temp; + memcpy(pScene->mRootNode->mName.data,szMe,temp); + pScene->mRootNode->mName.data[temp] = '\0'; + } + else pScene->mRootNode->mName.Set(""); - // 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; + uint 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; + } + // facet normal -0.13 -0.13 -0.98 + if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { - pMesh->mNumVertices = iNeededSize; - pMesh->mNumFaces += add; - } - aiVector3D* vn = &pMesh->mNormals[curFace++*3]; + 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; - curVertex = 0; - 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(sz, (float&)vn->x ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->y ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->z ); - *(vn+1) = *vn; - *(vn+2) = *vn; - } - } - // 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 - { - sz += 7; - SkipSpaces(&sz); - aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++]; - sz = fast_atoreal_move(sz, (float&)vn->x ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->y ); - SkipSpaces(&sz); - sz = fast_atoreal_move(sz, (float&)vn->z ); - } - } - else if (!::strncmp(sz,"endsolid",8)) { - // finished! - break; - } - // else skip the whole identifier - else { - do { - ++sz; - } while (!::IsSpaceOrNewLine(*sz)); - } - } + 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(sz, (float&)vn->x ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->y ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->z ); + normalBuffer.push_back(*vn); + normalBuffer.push_back(*vn); + } + } + // vertex 1.50000 1.50000 0.00000 + else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) + { + 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(sz, (float&)vn->x ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->y ); + SkipSpaces(&sz); + sz = fast_atoreal_move(sz, (float&)vn->z ); + faceVertexCounter++; + } + } + else if (!::strncmp(sz,"endsolid",8)) { + do { + ++sz; + } while (!::IsLineEnd(*sz)); + SkipSpacesAndLineEnd(&sz); + // finished! + break; + } + // else skip the whole identifier + else { + do { + ++sz; + } while (!::IsSpaceOrNewLine(*sz)); + } + } - if (!curFace) { - pMesh->mNumFaces = 0; - throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded"); - } - pMesh->mNumFaces = curFace; - pMesh->mNumVertices = curFace*3; - // we are finished! + 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"); + } + 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); + } + // 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]; + } } // ------------------------------------------------------------------------------------------------ // 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"); @@ -374,7 +399,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(""); pMesh->mNumFaces = *((uint32_t*)sz); @@ -447,6 +471,10 @@ bool STLImporter::LoadBinaryFile() *(clr+2) = *clr; } } + + // now copy faces + addFacesToMesh(pMesh); + if (bIsMaterialise && !pMesh->mColors[0]) { // use the color as diffuse material color From 816ceeda6915e025cb11af959e0652b015a1c930 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Fri, 20 Mar 2015 14:18:26 +0100 Subject: [PATCH 2/5] Eliminated error in MSVC. --- code/STLLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 8f2a4e18e..6b9df9d91 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -186,7 +186,7 @@ void STLImporter::InternReadFile( const std::string& pFile, pScene->mRootNode = new aiNode(); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - for (uint i = 0; i < pScene->mNumMeshes; i++) + 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 @@ -254,7 +254,7 @@ void STLImporter::LoadASCIIFile() } else pScene->mRootNode->mName.Set(""); - uint faceVertexCounter = 0; + unsigned int faceVertexCounter = 0; for ( ;; ) { // go to the next token From fb9e4d126673adf722dc00924dd1dd51487fadc3 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 6 Aug 2015 13:30:49 +0200 Subject: [PATCH 3/5] Ply export now uses uchar as type for the number of vertices per polygon and int as vertex index type. --- code/PlyExporter.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 9268794f0..f8ff34fc2 100644 --- a/code/PlyExporter.cpp +++ b/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 unchar 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 +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(&f.mNumIndices), 4); + NumIndicesType numIndices = static_cast(f.mNumIndices); + output.write(reinterpret_cast(&numIndices), sizeof(NumIndicesType)); for (unsigned int c = 0; c < f.mNumIndices; ++c) { - unsigned int index = f.mIndices[c] + offset; - mOutput.write(reinterpret_cast(&index), 4); + IndexType index = f.mIndices[c] + offset; + output.write(reinterpret_cast(&index), sizeof(IndexType)); } } } +void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset) +{ + WriteMeshIndicesBinary_Generic(m, offset, mOutput); +} + #endif From 5030fe8c7e7479f83b58e92f83b5604986b00baf Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 6 Aug 2015 13:37:16 +0200 Subject: [PATCH 4/5] Formatting changes. --- code/STLLoader.cpp | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index 79ed37c66..df15bdbfa 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2015, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -25,16 +25,16 @@ conditions are met: derived from this software without specific prior written permission of the assimp team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/assimp/scene.h" #include "../include/assimp/DefaultLogger.hpp" + using namespace Assimp; namespace { @@ -111,19 +112,19 @@ STLImporter::STLImporter() {} // ------------------------------------------------------------------------------------------------ -// Destructor, private as well +// Destructor, private as well STLImporter::~STLImporter() {} // ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. +// Returns whether the class can handle the format of the given file. bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); if (extension == "stl") return true; - else if (!extension.length() || checkSig) { + else if (!extension.length() || checkSig) { if (!pIOHandler) return true; const char* tokens[] = {"STL","solid"}; @@ -152,14 +153,14 @@ void addFacesToMesh(aiMesh* pMesh) } // ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void STLImporter::InternReadFile( const std::string& pFile, +// Imports the given file into the given scene structure. +void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file - if( file.get() == NULL) { + if( file.get() == NULL) { throw DeadlyImportError( "Failed to open STL file " + pFile + "."); } @@ -388,7 +389,7 @@ bool STLImporter::LoadBinaryFile() // search for an occurence of "COLOR=" in the header const unsigned char* sz2 = (const unsigned char*)mBuffer; const unsigned char* const szEnd = sz2+80; - while (sz2 < szEnd) { + while (sz2 < szEnd) { if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ && 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) { @@ -425,7 +426,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 From bcf3f985fb06ca1fff76e3df4c7c1ac0fdf6cb43 Mon Sep 17 00:00:00 2001 From: Andreas Henne Date: Thu, 13 Aug 2015 13:22:21 +0200 Subject: [PATCH 5/5] Fixed spelling error. --- code/PlyExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index f8ff34fc2..0a23e4b75 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -192,7 +192,7 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina // 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 unchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case? + // 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;