vrshoot

annotate libs/assimp/STLLoader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file Implementation of the STL importer class */
nuclear@0 43
nuclear@0 44 #include "AssimpPCH.h"
nuclear@0 45 #ifndef ASSIMP_BUILD_NO_STL_IMPORTER
nuclear@0 46
nuclear@0 47 // internal headers
nuclear@0 48 #include "STLLoader.h"
nuclear@0 49 #include "ParsingUtils.h"
nuclear@0 50 #include "fast_atof.h"
nuclear@0 51
nuclear@0 52 using namespace Assimp;
nuclear@0 53
nuclear@0 54 static const aiImporterDesc desc = {
nuclear@0 55 "Stereolithography (STL) Importer",
nuclear@0 56 "",
nuclear@0 57 "",
nuclear@0 58 "",
nuclear@0 59 aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
nuclear@0 60 0,
nuclear@0 61 0,
nuclear@0 62 0,
nuclear@0 63 0,
nuclear@0 64 "stl"
nuclear@0 65 };
nuclear@0 66
nuclear@0 67 // ------------------------------------------------------------------------------------------------
nuclear@0 68 // Constructor to be privately used by Importer
nuclear@0 69 STLImporter::STLImporter()
nuclear@0 70 {}
nuclear@0 71
nuclear@0 72 // ------------------------------------------------------------------------------------------------
nuclear@0 73 // Destructor, private as well
nuclear@0 74 STLImporter::~STLImporter()
nuclear@0 75 {}
nuclear@0 76
nuclear@0 77 // ------------------------------------------------------------------------------------------------
nuclear@0 78 // Returns whether the class can handle the format of the given file.
nuclear@0 79 bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
nuclear@0 80 {
nuclear@0 81 const std::string extension = GetExtension(pFile);
nuclear@0 82
nuclear@0 83 if (extension == "stl")
nuclear@0 84 return true;
nuclear@0 85 else if (!extension.length() || checkSig) {
nuclear@0 86 if (!pIOHandler)
nuclear@0 87 return true;
nuclear@0 88 const char* tokens[] = {"STL","solid"};
nuclear@0 89 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
nuclear@0 90 }
nuclear@0 91 return false;
nuclear@0 92 }
nuclear@0 93
nuclear@0 94 // ------------------------------------------------------------------------------------------------
nuclear@0 95 const aiImporterDesc* STLImporter::GetInfo () const
nuclear@0 96 {
nuclear@0 97 return &desc;
nuclear@0 98 }
nuclear@0 99
nuclear@0 100 // ------------------------------------------------------------------------------------------------
nuclear@0 101 // Imports the given file into the given scene structure.
nuclear@0 102 void STLImporter::InternReadFile( const std::string& pFile,
nuclear@0 103 aiScene* pScene, IOSystem* pIOHandler)
nuclear@0 104 {
nuclear@0 105 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
nuclear@0 106
nuclear@0 107 // Check whether we can read from the file
nuclear@0 108 if( file.get() == NULL) {
nuclear@0 109 throw DeadlyImportError( "Failed to open STL file " + pFile + ".");
nuclear@0 110 }
nuclear@0 111
nuclear@0 112 fileSize = (unsigned int)file->FileSize();
nuclear@0 113
nuclear@0 114 // allocate storage and copy the contents of the file to a memory buffer
nuclear@0 115 // (terminate it with zero)
nuclear@0 116 std::vector<char> mBuffer2;
nuclear@0 117 TextFileToBuffer(file.get(),mBuffer2);
nuclear@0 118
nuclear@0 119 this->pScene = pScene;
nuclear@0 120 this->mBuffer = &mBuffer2[0];
nuclear@0 121
nuclear@0 122 // the default vertex color is white
nuclear@0 123 clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f;
nuclear@0 124
nuclear@0 125 // allocate one mesh
nuclear@0 126 pScene->mNumMeshes = 1;
nuclear@0 127 pScene->mMeshes = new aiMesh*[1];
nuclear@0 128 aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
nuclear@0 129 pMesh->mMaterialIndex = 0;
nuclear@0 130
nuclear@0 131 // allocate a single node
nuclear@0 132 pScene->mRootNode = new aiNode();
nuclear@0 133 pScene->mRootNode->mNumMeshes = 1;
nuclear@0 134 pScene->mRootNode->mMeshes = new unsigned int[1];
nuclear@0 135 pScene->mRootNode->mMeshes[0] = 0;
nuclear@0 136
nuclear@0 137 bool bMatClr = false;
nuclear@0 138
nuclear@0 139 // check whether the file starts with 'solid' -
nuclear@0 140 // in this case we can simply assume it IS a text file. finished.
nuclear@0 141 if (!::strncmp(mBuffer,"solid",5)) {
nuclear@0 142 LoadASCIIFile();
nuclear@0 143 }
nuclear@0 144 else bMatClr = LoadBinaryFile();
nuclear@0 145
nuclear@0 146 // now copy faces
nuclear@0 147 pMesh->mFaces = new aiFace[pMesh->mNumFaces];
nuclear@0 148 for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) {
nuclear@0 149
nuclear@0 150 aiFace& face = pMesh->mFaces[i];
nuclear@0 151 face.mIndices = new unsigned int[face.mNumIndices = 3];
nuclear@0 152 for (unsigned int o = 0; o < 3;++o,++p) {
nuclear@0 153 face.mIndices[o] = p;
nuclear@0 154 }
nuclear@0 155 }
nuclear@0 156
nuclear@0 157 // create a single default material - everything white, as we have vertex colors
nuclear@0 158 aiMaterial* pcMat = new aiMaterial();
nuclear@0 159 aiString s;
nuclear@0 160 s.Set(AI_DEFAULT_MATERIAL_NAME);
nuclear@0 161 pcMat->AddProperty(&s, AI_MATKEY_NAME);
nuclear@0 162
nuclear@0 163 aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f);
nuclear@0 164 if (bMatClr) {
nuclear@0 165 clrDiffuse = clrColorDefault;
nuclear@0 166 }
nuclear@0 167 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
nuclear@0 168 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
nuclear@0 169 clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
nuclear@0 170 pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
nuclear@0 171
nuclear@0 172 pScene->mNumMaterials = 1;
nuclear@0 173 pScene->mMaterials = new aiMaterial*[1];
nuclear@0 174 pScene->mMaterials[0] = pcMat;
nuclear@0 175 }
nuclear@0 176 // ------------------------------------------------------------------------------------------------
nuclear@0 177 // Read an ASCII STL file
nuclear@0 178 void STLImporter::LoadASCIIFile()
nuclear@0 179 {
nuclear@0 180 aiMesh* pMesh = pScene->mMeshes[0];
nuclear@0 181
nuclear@0 182 const char* sz = mBuffer + 5; // skip the "solid"
nuclear@0 183 SkipSpaces(&sz);
nuclear@0 184 const char* szMe = sz;
nuclear@0 185 while (!::IsSpaceOrNewLine(*sz)) {
nuclear@0 186 sz++;
nuclear@0 187 }
nuclear@0 188
nuclear@0 189 size_t temp;
nuclear@0 190 // setup the name of the node
nuclear@0 191 if ((temp = (size_t)(sz-szMe))) {
nuclear@0 192
nuclear@0 193 pScene->mRootNode->mName.length = temp;
nuclear@0 194 memcpy(pScene->mRootNode->mName.data,szMe,temp);
nuclear@0 195 pScene->mRootNode->mName.data[temp] = '\0';
nuclear@0 196 }
nuclear@0 197 else pScene->mRootNode->mName.Set("<STL_ASCII>");
nuclear@0 198
nuclear@0 199 // try to guess how many vertices we could have
nuclear@0 200 // assume we'll need 160 bytes for each face
nuclear@0 201 pMesh->mNumVertices = ( pMesh->mNumFaces = std::max(1u,fileSize / 160u )) * 3;
nuclear@0 202 pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
nuclear@0 203 pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
nuclear@0 204
nuclear@0 205 unsigned int curFace = 0, curVertex = 3;
nuclear@0 206 for ( ;; )
nuclear@0 207 {
nuclear@0 208 // go to the next token
nuclear@0 209 if(!SkipSpacesAndLineEnd(&sz))
nuclear@0 210 {
nuclear@0 211 // seems we're finished although there was no end marker
nuclear@0 212 DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
nuclear@0 213 break;
nuclear@0 214 }
nuclear@0 215 // facet normal -0.13 -0.13 -0.98
nuclear@0 216 if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) {
nuclear@0 217
nuclear@0 218 if (3 != curVertex) {
nuclear@0 219 DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
nuclear@0 220 }
nuclear@0 221 if (pMesh->mNumFaces == curFace) {
nuclear@0 222 ai_assert(pMesh->mNumFaces != 0);
nuclear@0 223
nuclear@0 224 // need to resize the arrays, our size estimate was wrong
nuclear@0 225 unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces;
nuclear@0 226 if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping
nuclear@0 227 unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize;
nuclear@0 228 add += add >> 3; // add 12.5% as buffer
nuclear@0 229 iNeededSize = (pMesh->mNumFaces + add)*3;
nuclear@0 230 aiVector3D* pv = new aiVector3D[iNeededSize];
nuclear@0 231 memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D));
nuclear@0 232 delete[] pMesh->mVertices;
nuclear@0 233 pMesh->mVertices = pv;
nuclear@0 234 pv = new aiVector3D[iNeededSize];
nuclear@0 235 memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D));
nuclear@0 236 delete[] pMesh->mNormals;
nuclear@0 237 pMesh->mNormals = pv;
nuclear@0 238
nuclear@0 239 pMesh->mNumVertices = iNeededSize;
nuclear@0 240 pMesh->mNumFaces += add;
nuclear@0 241 }
nuclear@0 242 aiVector3D* vn = &pMesh->mNormals[curFace++*3];
nuclear@0 243
nuclear@0 244 sz += 6;
nuclear@0 245 curVertex = 0;
nuclear@0 246 SkipSpaces(&sz);
nuclear@0 247 if (strncmp(sz,"normal",6)) {
nuclear@0 248 DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
nuclear@0 249 }
nuclear@0 250 else
nuclear@0 251 {
nuclear@0 252 sz += 7;
nuclear@0 253 SkipSpaces(&sz);
nuclear@0 254 sz = fast_atoreal_move<float>(sz, (float&)vn->x );
nuclear@0 255 SkipSpaces(&sz);
nuclear@0 256 sz = fast_atoreal_move<float>(sz, (float&)vn->y );
nuclear@0 257 SkipSpaces(&sz);
nuclear@0 258 sz = fast_atoreal_move<float>(sz, (float&)vn->z );
nuclear@0 259 *(vn+1) = *vn;
nuclear@0 260 *(vn+2) = *vn;
nuclear@0 261 }
nuclear@0 262 }
nuclear@0 263 // vertex 1.50000 1.50000 0.00000
nuclear@0 264 else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
nuclear@0 265 {
nuclear@0 266 if (3 == curVertex) {
nuclear@0 267 DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
nuclear@0 268 }
nuclear@0 269 else
nuclear@0 270 {
nuclear@0 271 sz += 7;
nuclear@0 272 SkipSpaces(&sz);
nuclear@0 273 aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
nuclear@0 274 sz = fast_atoreal_move<float>(sz, (float&)vn->x );
nuclear@0 275 SkipSpaces(&sz);
nuclear@0 276 sz = fast_atoreal_move<float>(sz, (float&)vn->y );
nuclear@0 277 SkipSpaces(&sz);
nuclear@0 278 sz = fast_atoreal_move<float>(sz, (float&)vn->z );
nuclear@0 279 }
nuclear@0 280 }
nuclear@0 281 else if (!::strncmp(sz,"endsolid",8)) {
nuclear@0 282 // finished!
nuclear@0 283 break;
nuclear@0 284 }
nuclear@0 285 // else skip the whole identifier
nuclear@0 286 else while (!::IsSpaceOrNewLine(*sz)) {
nuclear@0 287 ++sz;
nuclear@0 288 }
nuclear@0 289 }
nuclear@0 290
nuclear@0 291 if (!curFace) {
nuclear@0 292 pMesh->mNumFaces = 0;
nuclear@0 293 throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
nuclear@0 294 }
nuclear@0 295 pMesh->mNumFaces = curFace;
nuclear@0 296 pMesh->mNumVertices = curFace*3;
nuclear@0 297 // we are finished!
nuclear@0 298 }
nuclear@0 299
nuclear@0 300 // ------------------------------------------------------------------------------------------------
nuclear@0 301 // Read a binary STL file
nuclear@0 302 bool STLImporter::LoadBinaryFile()
nuclear@0 303 {
nuclear@0 304 // skip the first 80 bytes
nuclear@0 305 if (fileSize < 84) {
nuclear@0 306 throw DeadlyImportError("STL: file is too small for the header");
nuclear@0 307 }
nuclear@0 308 bool bIsMaterialise = false;
nuclear@0 309
nuclear@0 310 // search for an occurence of "COLOR=" in the header
nuclear@0 311 const char* sz2 = (const char*)mBuffer;
nuclear@0 312 const char* const szEnd = sz2+80;
nuclear@0 313 while (sz2 < szEnd) {
nuclear@0 314
nuclear@0 315 if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
nuclear@0 316 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) {
nuclear@0 317
nuclear@0 318 // read the default vertex color for facets
nuclear@0 319 bIsMaterialise = true;
nuclear@0 320 DefaultLogger::get()->info("STL: Taking code path for Materialise files");
nuclear@0 321 clrColorDefault.r = (*sz2++) / 255.0f;
nuclear@0 322 clrColorDefault.g = (*sz2++) / 255.0f;
nuclear@0 323 clrColorDefault.b = (*sz2++) / 255.0f;
nuclear@0 324 clrColorDefault.a = (*sz2++) / 255.0f;
nuclear@0 325 break;
nuclear@0 326 }
nuclear@0 327 }
nuclear@0 328 const unsigned char* sz = (const unsigned char*)mBuffer + 80;
nuclear@0 329
nuclear@0 330 // now read the number of facets
nuclear@0 331 aiMesh* pMesh = pScene->mMeshes[0];
nuclear@0 332 pScene->mRootNode->mName.Set("<STL_BINARY>");
nuclear@0 333
nuclear@0 334 pMesh->mNumFaces = *((uint32_t*)sz);
nuclear@0 335 sz += 4;
nuclear@0 336
nuclear@0 337 if (fileSize < 84 + pMesh->mNumFaces*50) {
nuclear@0 338 throw DeadlyImportError("STL: file is too small to hold all facets");
nuclear@0 339 }
nuclear@0 340
nuclear@0 341 if (!pMesh->mNumFaces) {
nuclear@0 342 throw DeadlyImportError("STL: file is empty. There are no facets defined");
nuclear@0 343 }
nuclear@0 344
nuclear@0 345 pMesh->mNumVertices = pMesh->mNumFaces*3;
nuclear@0 346
nuclear@0 347 aiVector3D* vp,*vn;
nuclear@0 348 vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
nuclear@0 349 vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
nuclear@0 350
nuclear@0 351 for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
nuclear@0 352
nuclear@0 353 // NOTE: Blender sometimes writes empty normals ... this is not
nuclear@0 354 // our fault ... the RemoveInvalidData helper step should fix that
nuclear@0 355 *vn = *((aiVector3D*)sz);
nuclear@0 356 sz += sizeof(aiVector3D);
nuclear@0 357 *(vn+1) = *vn;
nuclear@0 358 *(vn+2) = *vn;
nuclear@0 359 vn += 3;
nuclear@0 360
nuclear@0 361 *vp++ = *((aiVector3D*)sz);
nuclear@0 362 sz += sizeof(aiVector3D);
nuclear@0 363
nuclear@0 364 *vp++ = *((aiVector3D*)sz);
nuclear@0 365 sz += sizeof(aiVector3D);
nuclear@0 366
nuclear@0 367 *vp++ = *((aiVector3D*)sz);
nuclear@0 368 sz += sizeof(aiVector3D);
nuclear@0 369
nuclear@0 370 uint16_t color = *((uint16_t*)sz);
nuclear@0 371 sz += 2;
nuclear@0 372
nuclear@0 373 if (color & (1 << 15))
nuclear@0 374 {
nuclear@0 375 // seems we need to take the color
nuclear@0 376 if (!pMesh->mColors[0])
nuclear@0 377 {
nuclear@0 378 pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
nuclear@0 379 for (unsigned int i = 0; i <pMesh->mNumVertices;++i)
nuclear@0 380 *pMesh->mColors[0]++ = this->clrColorDefault;
nuclear@0 381 pMesh->mColors[0] -= pMesh->mNumVertices;
nuclear@0 382
nuclear@0 383 DefaultLogger::get()->info("STL: Mesh has vertex colors");
nuclear@0 384 }
nuclear@0 385 aiColor4D* clr = &pMesh->mColors[0][i*3];
nuclear@0 386 clr->a = 1.0f;
nuclear@0 387 if (bIsMaterialise) // this is reversed
nuclear@0 388 {
nuclear@0 389 clr->r = (color & 0x31u) / 31.0f;
nuclear@0 390 clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
nuclear@0 391 clr->b = ((color & (0x31u<<10))>>10u) / 31.0f;
nuclear@0 392 }
nuclear@0 393 else
nuclear@0 394 {
nuclear@0 395 clr->b = (color & 0x31u) / 31.0f;
nuclear@0 396 clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
nuclear@0 397 clr->r = ((color & (0x31u<<10))>>10u) / 31.0f;
nuclear@0 398 }
nuclear@0 399 // assign the color to all vertices of the face
nuclear@0 400 *(clr+1) = *clr;
nuclear@0 401 *(clr+2) = *clr;
nuclear@0 402 }
nuclear@0 403 }
nuclear@0 404 if (bIsMaterialise && !pMesh->mColors[0])
nuclear@0 405 {
nuclear@0 406 // use the color as diffuse material color
nuclear@0 407 return true;
nuclear@0 408 }
nuclear@0 409 return false;
nuclear@0 410 }
nuclear@0 411
nuclear@0 412 #endif // !! ASSIMP_BUILD_NO_STL_IMPORTER