nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Implementation of the STL importer class */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_STL_IMPORTER nuclear@0: nuclear@0: // internal headers nuclear@0: #include "STLLoader.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "Stereolithography (STL) Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "stl" nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: STLImporter::STLImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: STLImporter::~STLImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: const std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: if (extension == "stl") nuclear@0: return true; nuclear@0: else if (!extension.length() || checkSig) { nuclear@0: if (!pIOHandler) nuclear@0: return true; nuclear@0: const char* tokens[] = {"STL","solid"}; nuclear@0: return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const aiImporterDesc* STLImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void STLImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); nuclear@0: nuclear@0: // Check whether we can read from the file nuclear@0: if( file.get() == NULL) { nuclear@0: throw DeadlyImportError( "Failed to open STL file " + pFile + "."); nuclear@0: } nuclear@0: nuclear@0: fileSize = (unsigned int)file->FileSize(); nuclear@0: nuclear@0: // allocate storage and copy the contents of the file to a memory buffer nuclear@0: // (terminate it with zero) nuclear@0: std::vector mBuffer2; nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: nuclear@0: this->pScene = pScene; nuclear@0: this->mBuffer = &mBuffer2[0]; nuclear@0: nuclear@0: // the default vertex color is white nuclear@0: clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f; nuclear@0: nuclear@0: // allocate one mesh nuclear@0: pScene->mNumMeshes = 1; nuclear@0: pScene->mMeshes = new aiMesh*[1]; nuclear@0: aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh(); nuclear@0: pMesh->mMaterialIndex = 0; nuclear@0: nuclear@0: // allocate a single node nuclear@0: pScene->mRootNode = new aiNode(); nuclear@0: pScene->mRootNode->mNumMeshes = 1; nuclear@0: pScene->mRootNode->mMeshes = new unsigned int[1]; nuclear@0: pScene->mRootNode->mMeshes[0] = 0; nuclear@0: nuclear@0: bool bMatClr = false; nuclear@0: nuclear@0: // check whether the file starts with 'solid' - nuclear@0: // in this case we can simply assume it IS a text file. finished. nuclear@0: if (!::strncmp(mBuffer,"solid",5)) { nuclear@0: LoadASCIIFile(); nuclear@0: } nuclear@0: else bMatClr = LoadBinaryFile(); nuclear@0: nuclear@0: // now copy faces nuclear@0: pMesh->mFaces = new aiFace[pMesh->mNumFaces]; nuclear@0: for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) { nuclear@0: nuclear@0: aiFace& face = pMesh->mFaces[i]; nuclear@0: face.mIndices = new unsigned int[face.mNumIndices = 3]; nuclear@0: for (unsigned int o = 0; o < 3;++o,++p) { nuclear@0: face.mIndices[o] = p; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // create a single default material - everything white, as we have vertex colors nuclear@0: aiMaterial* pcMat = new aiMaterial(); nuclear@0: aiString s; nuclear@0: s.Set(AI_DEFAULT_MATERIAL_NAME); nuclear@0: pcMat->AddProperty(&s, AI_MATKEY_NAME); nuclear@0: nuclear@0: aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f); nuclear@0: if (bMatClr) { nuclear@0: clrDiffuse = clrColorDefault; nuclear@0: } nuclear@0: pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE); nuclear@0: pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR); nuclear@0: clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f); nuclear@0: pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT); nuclear@0: nuclear@0: pScene->mNumMaterials = 1; nuclear@0: pScene->mMaterials = new aiMaterial*[1]; nuclear@0: pScene->mMaterials[0] = pcMat; nuclear@0: } nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read an ASCII STL file nuclear@0: void STLImporter::LoadASCIIFile() nuclear@0: { nuclear@0: aiMesh* pMesh = pScene->mMeshes[0]; nuclear@0: nuclear@0: const char* sz = mBuffer + 5; // skip the "solid" nuclear@0: SkipSpaces(&sz); nuclear@0: const char* szMe = sz; nuclear@0: while (!::IsSpaceOrNewLine(*sz)) { nuclear@0: sz++; nuclear@0: } nuclear@0: nuclear@0: size_t temp; nuclear@0: // setup the name of the node nuclear@0: if ((temp = (size_t)(sz-szMe))) { nuclear@0: nuclear@0: pScene->mRootNode->mName.length = temp; nuclear@0: memcpy(pScene->mRootNode->mName.data,szMe,temp); nuclear@0: pScene->mRootNode->mName.data[temp] = '\0'; nuclear@0: } nuclear@0: else pScene->mRootNode->mName.Set(""); nuclear@0: nuclear@0: // try to guess how many vertices we could have nuclear@0: // assume we'll need 160 bytes for each face nuclear@0: pMesh->mNumVertices = ( pMesh->mNumFaces = std::max(1u,fileSize / 160u )) * 3; nuclear@0: pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; nuclear@0: pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; nuclear@0: nuclear@0: unsigned int curFace = 0, curVertex = 3; nuclear@0: for ( ;; ) nuclear@0: { nuclear@0: // go to the next token nuclear@0: if(!SkipSpacesAndLineEnd(&sz)) nuclear@0: { nuclear@0: // seems we're finished although there was no end marker nuclear@0: DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected"); nuclear@0: break; nuclear@0: } nuclear@0: // facet normal -0.13 -0.13 -0.98 nuclear@0: if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) { nuclear@0: nuclear@0: if (3 != curVertex) { nuclear@0: DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete"); nuclear@0: } nuclear@0: if (pMesh->mNumFaces == curFace) { nuclear@0: ai_assert(pMesh->mNumFaces != 0); nuclear@0: nuclear@0: // need to resize the arrays, our size estimate was wrong nuclear@0: unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces; nuclear@0: if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping nuclear@0: unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize; nuclear@0: add += add >> 3; // add 12.5% as buffer nuclear@0: iNeededSize = (pMesh->mNumFaces + add)*3; nuclear@0: aiVector3D* pv = new aiVector3D[iNeededSize]; nuclear@0: memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D)); nuclear@0: delete[] pMesh->mVertices; nuclear@0: pMesh->mVertices = pv; nuclear@0: pv = new aiVector3D[iNeededSize]; nuclear@0: memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D)); nuclear@0: delete[] pMesh->mNormals; nuclear@0: pMesh->mNormals = pv; nuclear@0: nuclear@0: pMesh->mNumVertices = iNeededSize; nuclear@0: pMesh->mNumFaces += add; nuclear@0: } nuclear@0: aiVector3D* vn = &pMesh->mNormals[curFace++*3]; nuclear@0: nuclear@0: sz += 6; nuclear@0: curVertex = 0; nuclear@0: SkipSpaces(&sz); nuclear@0: if (strncmp(sz,"normal",6)) { nuclear@0: DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found"); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: sz += 7; nuclear@0: SkipSpaces(&sz); nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->x ); nuclear@0: SkipSpaces(&sz); nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->y ); nuclear@0: SkipSpaces(&sz); nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->z ); nuclear@0: *(vn+1) = *vn; nuclear@0: *(vn+2) = *vn; nuclear@0: } nuclear@0: } nuclear@0: // vertex 1.50000 1.50000 0.00000 nuclear@0: else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6))) nuclear@0: { nuclear@0: if (3 == curVertex) { nuclear@0: DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found"); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: sz += 7; nuclear@0: SkipSpaces(&sz); nuclear@0: aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++]; nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->x ); nuclear@0: SkipSpaces(&sz); nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->y ); nuclear@0: SkipSpaces(&sz); nuclear@0: sz = fast_atoreal_move(sz, (float&)vn->z ); nuclear@0: } nuclear@0: } nuclear@0: else if (!::strncmp(sz,"endsolid",8)) { nuclear@0: // finished! nuclear@0: break; nuclear@0: } nuclear@0: // else skip the whole identifier nuclear@0: else while (!::IsSpaceOrNewLine(*sz)) { nuclear@0: ++sz; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!curFace) { nuclear@0: pMesh->mNumFaces = 0; nuclear@0: throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded"); nuclear@0: } nuclear@0: pMesh->mNumFaces = curFace; nuclear@0: pMesh->mNumVertices = curFace*3; nuclear@0: // we are finished! nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Read a binary STL file nuclear@0: bool STLImporter::LoadBinaryFile() nuclear@0: { nuclear@0: // skip the first 80 bytes nuclear@0: if (fileSize < 84) { nuclear@0: throw DeadlyImportError("STL: file is too small for the header"); nuclear@0: } nuclear@0: bool bIsMaterialise = false; nuclear@0: nuclear@0: // search for an occurence of "COLOR=" in the header nuclear@0: const char* sz2 = (const char*)mBuffer; nuclear@0: const char* const szEnd = sz2+80; nuclear@0: while (sz2 < szEnd) { nuclear@0: nuclear@0: if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ && nuclear@0: 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) { nuclear@0: nuclear@0: // read the default vertex color for facets nuclear@0: bIsMaterialise = true; nuclear@0: DefaultLogger::get()->info("STL: Taking code path for Materialise files"); nuclear@0: clrColorDefault.r = (*sz2++) / 255.0f; nuclear@0: clrColorDefault.g = (*sz2++) / 255.0f; nuclear@0: clrColorDefault.b = (*sz2++) / 255.0f; nuclear@0: clrColorDefault.a = (*sz2++) / 255.0f; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: const unsigned char* sz = (const unsigned char*)mBuffer + 80; nuclear@0: nuclear@0: // now read the number of facets nuclear@0: aiMesh* pMesh = pScene->mMeshes[0]; nuclear@0: pScene->mRootNode->mName.Set(""); nuclear@0: nuclear@0: pMesh->mNumFaces = *((uint32_t*)sz); nuclear@0: sz += 4; nuclear@0: nuclear@0: if (fileSize < 84 + pMesh->mNumFaces*50) { nuclear@0: throw DeadlyImportError("STL: file is too small to hold all facets"); nuclear@0: } nuclear@0: nuclear@0: if (!pMesh->mNumFaces) { nuclear@0: throw DeadlyImportError("STL: file is empty. There are no facets defined"); nuclear@0: } nuclear@0: nuclear@0: pMesh->mNumVertices = pMesh->mNumFaces*3; nuclear@0: nuclear@0: aiVector3D* vp,*vn; nuclear@0: vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; nuclear@0: vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < pMesh->mNumFaces;++i) { nuclear@0: nuclear@0: // NOTE: Blender sometimes writes empty normals ... this is not nuclear@0: // our fault ... the RemoveInvalidData helper step should fix that nuclear@0: *vn = *((aiVector3D*)sz); nuclear@0: sz += sizeof(aiVector3D); nuclear@0: *(vn+1) = *vn; nuclear@0: *(vn+2) = *vn; nuclear@0: vn += 3; nuclear@0: nuclear@0: *vp++ = *((aiVector3D*)sz); nuclear@0: sz += sizeof(aiVector3D); nuclear@0: nuclear@0: *vp++ = *((aiVector3D*)sz); nuclear@0: sz += sizeof(aiVector3D); nuclear@0: nuclear@0: *vp++ = *((aiVector3D*)sz); nuclear@0: sz += sizeof(aiVector3D); nuclear@0: nuclear@0: uint16_t color = *((uint16_t*)sz); nuclear@0: sz += 2; nuclear@0: nuclear@0: if (color & (1 << 15)) nuclear@0: { nuclear@0: // seems we need to take the color nuclear@0: if (!pMesh->mColors[0]) nuclear@0: { nuclear@0: pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; nuclear@0: for (unsigned int i = 0; i mNumVertices;++i) nuclear@0: *pMesh->mColors[0]++ = this->clrColorDefault; nuclear@0: pMesh->mColors[0] -= pMesh->mNumVertices; nuclear@0: nuclear@0: DefaultLogger::get()->info("STL: Mesh has vertex colors"); nuclear@0: } nuclear@0: aiColor4D* clr = &pMesh->mColors[0][i*3]; nuclear@0: clr->a = 1.0f; nuclear@0: if (bIsMaterialise) // this is reversed nuclear@0: { nuclear@0: clr->r = (color & 0x31u) / 31.0f; nuclear@0: clr->g = ((color & (0x31u<<5))>>5u) / 31.0f; nuclear@0: clr->b = ((color & (0x31u<<10))>>10u) / 31.0f; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: clr->b = (color & 0x31u) / 31.0f; nuclear@0: clr->g = ((color & (0x31u<<5))>>5u) / 31.0f; nuclear@0: clr->r = ((color & (0x31u<<10))>>10u) / 31.0f; nuclear@0: } nuclear@0: // assign the color to all vertices of the face nuclear@0: *(clr+1) = *clr; nuclear@0: *(clr+2) = *clr; nuclear@0: } nuclear@0: } nuclear@0: if (bIsMaterialise && !pMesh->mColors[0]) nuclear@0: { nuclear@0: // use the color as diffuse material color nuclear@0: return true; nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_STL_IMPORTER