123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- ---------------------------------------------------------------------------
- Open Asset Import Library (ASSIMP)
- ---------------------------------------------------------------------------
- Copyright (c) 2006-2008, ASSIMP Development 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
- conditions are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
- * Neither the name of the ASSIMP team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the ASSIMP Development Team.
- 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
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- 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
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ---------------------------------------------------------------------------
- */
- /** @file Implementation of the STL importer class */
- #include "AssimpPCH.h"
- #ifndef ASSIMP_BUILD_NO_STL_IMPORTER
- // internal headers
- #include "STLLoader.h"
- #include "ParsingUtils.h"
- #include "fast_atof.h"
- using namespace Assimp;
- // ------------------------------------------------------------------------------------------------
- // Constructor to be privately used by Importer
- STLImporter::STLImporter()
- {}
- // ------------------------------------------------------------------------------------------------
- // Destructor, private as well
- STLImporter::~STLImporter()
- {}
- // ------------------------------------------------------------------------------------------------
- // 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) {
- if (!pIOHandler)
- return true;
- const char* tokens[] = {"STL","solid"};
- return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- void STLImporter::GetExtensionList(std::string& append)
- {
- append.append("*.stl");
- }
- // ------------------------------------------------------------------------------------------------
- // Imports the given file into the given scene structure.
- void STLImporter::InternReadFile(
- const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
- {
- boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
- // Check whether we can read from the file
- if( file.get() == NULL)
- {
- throw new ImportErrorException( "Failed to open STL file " + pFile + ".");
- }
- this->fileSize = (unsigned int)file->FileSize();
- // allocate storage and copy the contents of the file to a memory buffer
- // (terminate it with zero)
- std::vector<char> mBuffer2(fileSize+1);
-
- file->Read(&mBuffer2[0], 1, fileSize);
- mBuffer2[fileSize] = '\0';
- this->pScene = pScene;
- this->mBuffer = &mBuffer2[0];
- // the default vertex color is white
- clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f;
- // 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;
- // check whether the file starts with 'solid' -
- // in this case we can simply assume it IS a text file. finished.
- if (!::strncmp(mBuffer,"solid",5))
- this->LoadASCIIFile();
- else bMatClr = this->LoadBinaryFile();
- // 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;
- }
- // create a single default material - everything white, as we have vertex colors
- MaterialHelper* pcMat = new MaterialHelper();
- aiString s;
- s.Set(AI_DEFAULT_MATERIAL_NAME);
- pcMat->AddProperty(&s, AI_MATKEY_NAME);
- aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f);
- if (bMatClr)clrDiffuse = this->clrColorDefault;
- pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
- pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
- clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
- pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
- pScene->mNumMaterials = 1;
- pScene->mMaterials = new aiMaterial*[1];
- pScene->mMaterials[0] = pcMat;
- }
- // ------------------------------------------------------------------------------------------------
- // Read an ASCII STL file
- void STLImporter::LoadASCIIFile()
- {
- aiMesh* pMesh = pScene->mMeshes[0];
- const char* sz = mBuffer + 5; // skip the "solid"
- SkipSpaces(&sz);
- const char* szMe = sz;
- while (!::IsSpaceOrNewLine(*sz))sz++;
- unsigned int temp;
- // setup the name of the node
- if ((temp = (unsigned int)(sz-szMe)))
- {
- 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>");
- // try to guess how many vertices we could have
- // assume we'll need 160 bytes for each face
- pMesh->mNumVertices = ( pMesh->mNumFaces = fileSize / 160 ) * 3;
- pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
- pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
-
- unsigned int curFace = 0, curVertex = 3;
- while (true)
- {
- // 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)))
- {
- if (3 != curVertex)DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
- if (pMesh->mNumFaces == curFace)
- {
- // 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
- {
- sz += 7;
- SkipSpaces(&sz);
- sz = fast_atof_move(sz, (float&)vn->x );
- SkipSpaces(&sz);
- sz = fast_atof_move(sz, (float&)vn->y );
- SkipSpaces(&sz);
- sz = fast_atof_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");
- }
- else
- {
- sz += 7;
- SkipSpaces(&sz);
- aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
- sz = fast_atof_move(sz, (float&)vn->x );
- SkipSpaces(&sz);
- sz = fast_atof_move(sz, (float&)vn->y );
- SkipSpaces(&sz);
- sz = fast_atof_move(sz, (float&)vn->z );
- }
- }
- else if (!::strncmp(sz,"endsolid",8))
- {
- // finished!
- break;
- }
- // else skip the whole identifier
- else while (!::IsSpaceOrNewLine(*sz))++sz;
- }
- if (!curFace)
- {
- pMesh->mNumFaces = 0;
- throw new ImportErrorException("STL: ASCII file is empty or invalid; no data loaded");
- }
- pMesh->mNumFaces = curFace;
- pMesh->mNumVertices = curFace*3;
- // we are finished!
- }
- // ------------------------------------------------------------------------------------------------
- // Read a binary STL file
- bool STLImporter::LoadBinaryFile()
- {
- // skip the first 80 bytes
- if (fileSize < 84)
- throw new ImportErrorException("STL: file is too small for the header");
- bool bIsMaterialise = false;
- // search for an occurence of "COLOR=" in the header
- const char* sz2 = (const char*)mBuffer;
- const char* const szEnd = sz2+80;
- while (sz2 < szEnd)
- {
- if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
- 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++)
- {
- // read the default vertex color for facets
- bIsMaterialise = true;
- DefaultLogger::get()->info("STL: Taking code path for Materialise files");
- this->clrColorDefault.r = (*sz2++) / 255.0f;
- this->clrColorDefault.g = (*sz2++) / 255.0f;
- this->clrColorDefault.b = (*sz2++) / 255.0f;
- this->clrColorDefault.a = (*sz2++) / 255.0f;
- break;
- }
- }
- 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);
- sz += 4;
- if (fileSize < 84 + pMesh->mNumFaces*50)
- throw new ImportErrorException("STL: file is too small to keep all facets");
- if (!pMesh->mNumFaces)
- throw new ImportErrorException("STL: file is empty. There are no facets defined");
- pMesh->mNumVertices = pMesh->mNumFaces*3;
- aiVector3D* vp,*vn;
- vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
- vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
- 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
- *vn = *((aiVector3D*)sz);
- sz += sizeof(aiVector3D);
- *(vn+1) = *vn;
- *(vn+2) = *vn;
- vn += 3;
- *vp++ = *((aiVector3D*)sz);
- sz += sizeof(aiVector3D);
- *vp++ = *((aiVector3D*)sz);
- sz += sizeof(aiVector3D);
- *vp++ = *((aiVector3D*)sz);
- sz += sizeof(aiVector3D);
- uint16_t color = *((uint16_t*)sz);
- sz += 2;
- if (color & (1 << 15))
- {
- // seems we need to take the color
- if (!pMesh->mColors[0])
- {
- pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
- for (unsigned int i = 0; i <pMesh->mNumVertices;++i)
- *pMesh->mColors[0]++ = this->clrColorDefault;
- pMesh->mColors[0] -= pMesh->mNumVertices;
- DefaultLogger::get()->info("STL: Mesh has vertex colors");
- }
- aiColor4D* clr = &pMesh->mColors[0][pMesh->mNumFaces*3];
- clr->a = 1.0f;
- if (bIsMaterialise) // fuck, this is reversed
- {
- clr->r = (color & 0x31u) / 31.0f;
- clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
- clr->b = ((color & (0x31u<<10))>>10u) / 31.0f;
- }
- else
- {
- clr->b = (color & 0x31u) / 31.0f;
- clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
- clr->r = ((color & (0x31u<<10))>>10u) / 31.0f;
- }
- // assign the color to all vertices of the face
- *(clr+1) = *clr;
- *(clr+2) = *clr;
- }
- }
- if (bIsMaterialise && !pMesh->mColors[0])
- {
- // use the color as diffuse material color
- return true;
- }
- return false;
- }
- #endif // !! ASSIMP_BUILD_NO_STL_IMPORTER
|