Merge pull request #633 from heandreas/master
Multiple meshes in single STL ascii file and binary bly exporter fixpull/635/head
commit
2a432f9adc
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
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++;
|
||||
}
|
||||
|
||||
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>");
|
||||
|
||||
unsigned int faceVertexCounter = 0;
|
||||
for ( ;; )
|
||||
{
|
||||
// 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)) && *(sz + 5) != '\0') {
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
aiVector3D* vn = &pMesh->mNormals[curFace++*3];
|
||||
|
||||
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
|
||||
// go to the next token
|
||||
if(!SkipSpacesAndLineEnd(&sz))
|
||||
{
|
||||
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;
|
||||
// seems we're finished although there was no end marker
|
||||
DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 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<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 if (!::strncmp(sz,"endsolid",8)) {
|
||||
// finished!
|
||||
break;
|
||||
}
|
||||
// else skip the whole identifier
|
||||
else {
|
||||
do {
|
||||
++sz;
|
||||
} while (!::IsSpaceOrNewLine(*sz));
|
||||
}
|
||||
}
|
||||
// facet normal -0.13 -0.13 -0.98
|
||||
if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5)) && *(sz + 5) != '\0') {
|
||||
|
||||
if (!curFace) {
|
||||
pMesh->mNumFaces = 0;
|
||||
throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 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<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++;
|
||||
}
|
||||
}
|
||||
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 (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];
|
||||
}
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue