vrshoot
diff libs/assimp/BaseImporter.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/BaseImporter.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,560 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file BaseImporter.cpp 1.46 + * @brief Implementation of BaseImporter 1.47 + */ 1.48 + 1.49 +#include "AssimpPCH.h" 1.50 +#include "BaseImporter.h" 1.51 +#include "FileSystemFilter.h" 1.52 + 1.53 +#include "Importer.h" 1.54 + 1.55 +using namespace Assimp; 1.56 + 1.57 +// ------------------------------------------------------------------------------------------------ 1.58 +// Constructor to be privately used by Importer 1.59 +BaseImporter::BaseImporter() 1.60 +: progress() 1.61 +{ 1.62 + // nothing to do here 1.63 +} 1.64 + 1.65 +// ------------------------------------------------------------------------------------------------ 1.66 +// Destructor, private as well 1.67 +BaseImporter::~BaseImporter() 1.68 +{ 1.69 + // nothing to do here 1.70 +} 1.71 + 1.72 +// ------------------------------------------------------------------------------------------------ 1.73 +// Imports the given file and returns the imported data. 1.74 +aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) 1.75 +{ 1.76 + progress = pImp->GetProgressHandler(); 1.77 + ai_assert(progress); 1.78 + 1.79 + // Gather configuration properties for this run 1.80 + SetupProperties( pImp ); 1.81 + 1.82 + // Construct a file system filter to improve our success ratio at reading external files 1.83 + FileSystemFilter filter(pFile,pIOHandler); 1.84 + 1.85 + // create a scene object to hold the data 1.86 + ScopeGuard<aiScene> sc(new aiScene()); 1.87 + 1.88 + // dispatch importing 1.89 + try 1.90 + { 1.91 + InternReadFile( pFile, sc, &filter); 1.92 + 1.93 + } catch( const std::exception& err ) { 1.94 + // extract error description 1.95 + mErrorText = err.what(); 1.96 + DefaultLogger::get()->error(mErrorText); 1.97 + return NULL; 1.98 + } 1.99 + 1.100 + // return what we gathered from the import. 1.101 + sc.dismiss(); 1.102 + return sc; 1.103 +} 1.104 + 1.105 +// ------------------------------------------------------------------------------------------------ 1.106 +void BaseImporter::SetupProperties(const Importer* /*pImp*/) 1.107 +{ 1.108 + // the default implementation does nothing 1.109 +} 1.110 + 1.111 +// ------------------------------------------------------------------------------------------------ 1.112 +void BaseImporter::GetExtensionList(std::set<std::string>& extensions) 1.113 +{ 1.114 + const aiImporterDesc* desc = GetInfo(); 1.115 + ai_assert(desc != NULL); 1.116 + 1.117 + const char* ext = desc->mFileExtensions; 1.118 + ai_assert(ext != NULL); 1.119 + 1.120 + const char* last = ext; 1.121 + do { 1.122 + if (!*ext || *ext == ' ') { 1.123 + extensions.insert(std::string(last,ext-last)); 1.124 + ai_assert(ext-last > 0); 1.125 + last = ext; 1.126 + while(*last == ' ') { 1.127 + ++last; 1.128 + } 1.129 + } 1.130 + } 1.131 + while(*ext++); 1.132 +} 1.133 + 1.134 +// ------------------------------------------------------------------------------------------------ 1.135 +/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler, 1.136 + const std::string& pFile, 1.137 + const char** tokens, 1.138 + unsigned int numTokens, 1.139 + unsigned int searchBytes /* = 200 */, 1.140 + bool tokensSol /* false */) 1.141 +{ 1.142 + ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes); 1.143 + if (!pIOHandler) 1.144 + return false; 1.145 + 1.146 + boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile)); 1.147 + if (pStream.get() ) { 1.148 + // read 200 characters from the file 1.149 + boost::scoped_array<char> _buffer (new char[searchBytes+1 /* for the '\0' */]); 1.150 + char* buffer = _buffer.get(); 1.151 + 1.152 + const unsigned int read = pStream->Read(buffer,1,searchBytes); 1.153 + if (!read) 1.154 + return false; 1.155 + 1.156 + for (unsigned int i = 0; i < read;++i) 1.157 + buffer[i] = ::tolower(buffer[i]); 1.158 + 1.159 + // It is not a proper handling of unicode files here ... 1.160 + // ehm ... but it works in most cases. 1.161 + char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; 1.162 + while (cur != end) { 1.163 + if (*cur) 1.164 + *cur2++ = *cur; 1.165 + ++cur; 1.166 + } 1.167 + *cur2 = '\0'; 1.168 + 1.169 + for (unsigned int i = 0; i < numTokens;++i) { 1.170 + ai_assert(NULL != tokens[i]); 1.171 + 1.172 + 1.173 + const char* r = strstr(buffer,tokens[i]); 1.174 + if (!r) 1.175 + continue; 1.176 + // We got a match, either we don't care where it is, or it happens to 1.177 + // be in the beginning of the file / line 1.178 + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { 1.179 + DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]); 1.180 + return true; 1.181 + } 1.182 + } 1.183 + } 1.184 + return false; 1.185 +} 1.186 + 1.187 +// ------------------------------------------------------------------------------------------------ 1.188 +// Simple check for file extension 1.189 +/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, 1.190 + const char* ext0, 1.191 + const char* ext1, 1.192 + const char* ext2) 1.193 +{ 1.194 + std::string::size_type pos = pFile.find_last_of('.'); 1.195 + 1.196 + // no file extension - can't read 1.197 + if( pos == std::string::npos) 1.198 + return false; 1.199 + 1.200 + const char* ext_real = & pFile[ pos+1 ]; 1.201 + if( !ASSIMP_stricmp(ext_real,ext0) ) 1.202 + return true; 1.203 + 1.204 + // check for other, optional, file extensions 1.205 + if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) 1.206 + return true; 1.207 + 1.208 + if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) 1.209 + return true; 1.210 + 1.211 + return false; 1.212 +} 1.213 + 1.214 +// ------------------------------------------------------------------------------------------------ 1.215 +// Get file extension from path 1.216 +/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile) 1.217 +{ 1.218 + std::string::size_type pos = pFile.find_last_of('.'); 1.219 + 1.220 + // no file extension at all 1.221 + if( pos == std::string::npos) 1.222 + return ""; 1.223 + 1.224 + std::string ret = pFile.substr(pos+1); 1.225 + std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint 1.226 + return ret; 1.227 +} 1.228 + 1.229 +// ------------------------------------------------------------------------------------------------ 1.230 +// Check for magic bytes at the beginning of the file. 1.231 +/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, 1.232 + const void* _magic, unsigned int num, unsigned int offset, unsigned int size) 1.233 +{ 1.234 + ai_assert(size <= 16 && _magic); 1.235 + 1.236 + if (!pIOHandler) { 1.237 + return false; 1.238 + } 1.239 + union { 1.240 + const char* magic; 1.241 + const uint16_t* magic_u16; 1.242 + const uint32_t* magic_u32; 1.243 + }; 1.244 + magic = reinterpret_cast<const char*>(_magic); 1.245 + boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile)); 1.246 + if (pStream.get() ) { 1.247 + 1.248 + // skip to offset 1.249 + pStream->Seek(offset,aiOrigin_SET); 1.250 + 1.251 + // read 'size' characters from the file 1.252 + union { 1.253 + char data[16]; 1.254 + uint16_t data_u16[8]; 1.255 + uint32_t data_u32[4]; 1.256 + }; 1.257 + if(size != pStream->Read(data,1,size)) { 1.258 + return false; 1.259 + } 1.260 + 1.261 + for (unsigned int i = 0; i < num; ++i) { 1.262 + // also check against big endian versions of tokens with size 2,4 1.263 + // that's just for convinience, the chance that we cause conflicts 1.264 + // is quite low and it can save some lines and prevent nasty bugs 1.265 + if (2 == size) { 1.266 + uint16_t rev = *magic_u16; 1.267 + ByteSwap::Swap(&rev); 1.268 + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { 1.269 + return true; 1.270 + } 1.271 + } 1.272 + else if (4 == size) { 1.273 + uint32_t rev = *magic_u32; 1.274 + ByteSwap::Swap(&rev); 1.275 + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { 1.276 + return true; 1.277 + } 1.278 + } 1.279 + else { 1.280 + // any length ... just compare 1.281 + if(!memcmp(magic,data,size)) { 1.282 + return true; 1.283 + } 1.284 + } 1.285 + magic += size; 1.286 + } 1.287 + } 1.288 + return false; 1.289 +} 1.290 + 1.291 +#include "ConvertUTF/ConvertUTF.h" 1.292 + 1.293 +// ------------------------------------------------------------------------------------------------ 1.294 +void ReportResult(ConversionResult res) 1.295 +{ 1.296 + if(res == sourceExhausted) { 1.297 + DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails"); 1.298 + } 1.299 + else if(res == sourceIllegal) { 1.300 + DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails"); 1.301 + } 1.302 +} 1.303 + 1.304 +// ------------------------------------------------------------------------------------------------ 1.305 +// Convert to UTF8 data 1.306 +void BaseImporter::ConvertToUTF8(std::vector<char>& data) 1.307 +{ 1.308 + ConversionResult result; 1.309 + if(data.size() < 8) { 1.310 + throw DeadlyImportError("File is too small"); 1.311 + } 1.312 + 1.313 + // UTF 8 with BOM 1.314 + if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { 1.315 + DefaultLogger::get()->debug("Found UTF-8 BOM ..."); 1.316 + 1.317 + std::copy(data.begin()+3,data.end(),data.begin()); 1.318 + data.resize(data.size()-3); 1.319 + return; 1.320 + } 1.321 + 1.322 + // UTF 32 BE with BOM 1.323 + if(*((uint32_t*)&data.front()) == 0xFFFE0000) { 1.324 + 1.325 + // swap the endianess .. 1.326 + for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { 1.327 + AI_SWAP4P(p); 1.328 + } 1.329 + } 1.330 + 1.331 + // UTF 32 LE with BOM 1.332 + if(*((uint32_t*)&data.front()) == 0x0000FFFE) { 1.333 + DefaultLogger::get()->debug("Found UTF-32 BOM ..."); 1.334 + 1.335 + const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1; 1.336 + char* dstart,*dend; 1.337 + std::vector<char> output; 1.338 + do { 1.339 + output.resize(output.size()?output.size()*3/2:data.size()/2); 1.340 + dstart = &output.front(),dend = &output.back()+1; 1.341 + 1.342 + result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); 1.343 + } while(result == targetExhausted); 1.344 + 1.345 + ReportResult(result); 1.346 + 1.347 + // copy to output buffer. 1.348 + const size_t outlen = (size_t)(dstart-&output.front()); 1.349 + data.assign(output.begin(),output.begin()+outlen); 1.350 + return; 1.351 + } 1.352 + 1.353 + // UTF 16 BE with BOM 1.354 + if(*((uint16_t*)&data.front()) == 0xFFFE) { 1.355 + 1.356 + // swap the endianess .. 1.357 + for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { 1.358 + ByteSwap::Swap2(p); 1.359 + } 1.360 + } 1.361 + 1.362 + // UTF 16 LE with BOM 1.363 + if(*((uint16_t*)&data.front()) == 0xFEFF) { 1.364 + DefaultLogger::get()->debug("Found UTF-16 BOM ..."); 1.365 + 1.366 + const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)(&data.back()+1); 1.367 + char* dstart,*dend; 1.368 + std::vector<char> output; 1.369 + do { 1.370 + output.resize(output.size()?output.size()*3/2:data.size()*3/4); 1.371 + dstart = &output.front(),dend = &output.back()+1; 1.372 + 1.373 + result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); 1.374 + } while(result == targetExhausted); 1.375 + 1.376 + ReportResult(result); 1.377 + 1.378 + // copy to output buffer. 1.379 + const size_t outlen = (size_t)(dstart-&output.front()); 1.380 + data.assign(output.begin(),output.begin()+outlen); 1.381 + return; 1.382 + } 1.383 +} 1.384 + 1.385 +// ------------------------------------------------------------------------------------------------ 1.386 +void BaseImporter::TextFileToBuffer(IOStream* stream, 1.387 + std::vector<char>& data) 1.388 +{ 1.389 + ai_assert(NULL != stream); 1.390 + 1.391 + const size_t fileSize = stream->FileSize(); 1.392 + if(!fileSize) { 1.393 + throw DeadlyImportError("File is empty"); 1.394 + } 1.395 + 1.396 + data.reserve(fileSize+1); 1.397 + data.resize(fileSize); 1.398 + if(fileSize != stream->Read( &data[0], 1, fileSize)) { 1.399 + throw DeadlyImportError("File read error"); 1.400 + } 1.401 + 1.402 + ConvertToUTF8(data); 1.403 + 1.404 + // append a binary zero to simplify string parsing 1.405 + data.push_back(0); 1.406 +} 1.407 + 1.408 +// ------------------------------------------------------------------------------------------------ 1.409 +namespace Assimp 1.410 +{ 1.411 + // Represents an import request 1.412 + struct LoadRequest 1.413 + { 1.414 + LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) 1.415 + : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id) 1.416 + { 1.417 + if (_map) 1.418 + map = *_map; 1.419 + } 1.420 + 1.421 + const std::string file; 1.422 + unsigned int flags; 1.423 + unsigned int refCnt; 1.424 + aiScene* scene; 1.425 + bool loaded; 1.426 + BatchLoader::PropertyMap map; 1.427 + unsigned int id; 1.428 + 1.429 + bool operator== (const std::string& f) { 1.430 + return file == f; 1.431 + } 1.432 + }; 1.433 +} 1.434 + 1.435 +// ------------------------------------------------------------------------------------------------ 1.436 +// BatchLoader::pimpl data structure 1.437 +struct Assimp::BatchData 1.438 +{ 1.439 + BatchData() 1.440 + : next_id(0xffff) 1.441 + {} 1.442 + 1.443 + // IO system to be used for all imports 1.444 + IOSystem* pIOSystem; 1.445 + 1.446 + // Importer used to load all meshes 1.447 + Importer* pImporter; 1.448 + 1.449 + // List of all imports 1.450 + std::list<LoadRequest> requests; 1.451 + 1.452 + // Base path 1.453 + std::string pathBase; 1.454 + 1.455 + // Id for next item 1.456 + unsigned int next_id; 1.457 +}; 1.458 + 1.459 +// ------------------------------------------------------------------------------------------------ 1.460 +BatchLoader::BatchLoader(IOSystem* pIO) 1.461 +{ 1.462 + ai_assert(NULL != pIO); 1.463 + 1.464 + data = new BatchData(); 1.465 + data->pIOSystem = pIO; 1.466 + 1.467 + data->pImporter = new Importer(); 1.468 + data->pImporter->SetIOHandler(data->pIOSystem); 1.469 +} 1.470 + 1.471 +// ------------------------------------------------------------------------------------------------ 1.472 +BatchLoader::~BatchLoader() 1.473 +{ 1.474 + // delete all scenes wthat have not been polled by the user 1.475 + for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) { 1.476 + 1.477 + delete (*it).scene; 1.478 + } 1.479 + data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ 1.480 + delete data->pImporter; 1.481 + delete data; 1.482 +} 1.483 + 1.484 + 1.485 +// ------------------------------------------------------------------------------------------------ 1.486 +unsigned int BatchLoader::AddLoadRequest (const std::string& file, 1.487 + unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) 1.488 +{ 1.489 + ai_assert(!file.empty()); 1.490 + 1.491 + // check whether we have this loading request already 1.492 + std::list<LoadRequest>::iterator it; 1.493 + for (it = data->requests.begin();it != data->requests.end(); ++it) { 1.494 + 1.495 + // Call IOSystem's path comparison function here 1.496 + if (data->pIOSystem->ComparePaths((*it).file,file)) { 1.497 + 1.498 + if (map) { 1.499 + if (!((*it).map == *map)) 1.500 + continue; 1.501 + } 1.502 + else if (!(*it).map.empty()) 1.503 + continue; 1.504 + 1.505 + (*it).refCnt++; 1.506 + return (*it).id; 1.507 + } 1.508 + } 1.509 + 1.510 + // no, we don't have it. So add it to the queue ... 1.511 + data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); 1.512 + return data->next_id++; 1.513 +} 1.514 + 1.515 +// ------------------------------------------------------------------------------------------------ 1.516 +aiScene* BatchLoader::GetImport (unsigned int which) 1.517 +{ 1.518 + for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) { 1.519 + 1.520 + if ((*it).id == which && (*it).loaded) { 1.521 + 1.522 + aiScene* sc = (*it).scene; 1.523 + if (!(--(*it).refCnt)) { 1.524 + data->requests.erase(it); 1.525 + } 1.526 + return sc; 1.527 + } 1.528 + } 1.529 + return NULL; 1.530 +} 1.531 + 1.532 +// ------------------------------------------------------------------------------------------------ 1.533 +void BatchLoader::LoadAll() 1.534 +{ 1.535 + // no threaded implementation for the moment 1.536 + for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) { 1.537 + // force validation in debug builds 1.538 + unsigned int pp = (*it).flags; 1.539 +#ifdef _DEBUG 1.540 + pp |= aiProcess_ValidateDataStructure; 1.541 +#endif 1.542 + // setup config properties if necessary 1.543 + ImporterPimpl* pimpl = data->pImporter->Pimpl(); 1.544 + pimpl->mFloatProperties = (*it).map.floats; 1.545 + pimpl->mIntProperties = (*it).map.ints; 1.546 + pimpl->mStringProperties = (*it).map.strings; 1.547 + 1.548 + if (!DefaultLogger::isNullLogger()) 1.549 + { 1.550 + DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%"); 1.551 + DefaultLogger::get()->info("File: " + (*it).file); 1.552 + } 1.553 + data->pImporter->ReadFile((*it).file,pp); 1.554 + (*it).scene = data->pImporter->GetOrphanedScene(); 1.555 + (*it).loaded = true; 1.556 + 1.557 + DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%"); 1.558 + } 1.559 +} 1.560 + 1.561 + 1.562 + 1.563 +