vrshoot
view libs/assimp/BaseImporter.cpp @ 1:e7ca128b8713
looks nice :)
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Feb 2014 00:35:22 +0200 |
parents | |
children |
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file BaseImporter.cpp
43 * @brief Implementation of BaseImporter
44 */
46 #include "AssimpPCH.h"
47 #include "BaseImporter.h"
48 #include "FileSystemFilter.h"
50 #include "Importer.h"
52 using namespace Assimp;
54 // ------------------------------------------------------------------------------------------------
55 // Constructor to be privately used by Importer
56 BaseImporter::BaseImporter()
57 : progress()
58 {
59 // nothing to do here
60 }
62 // ------------------------------------------------------------------------------------------------
63 // Destructor, private as well
64 BaseImporter::~BaseImporter()
65 {
66 // nothing to do here
67 }
69 // ------------------------------------------------------------------------------------------------
70 // Imports the given file and returns the imported data.
71 aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler)
72 {
73 progress = pImp->GetProgressHandler();
74 ai_assert(progress);
76 // Gather configuration properties for this run
77 SetupProperties( pImp );
79 // Construct a file system filter to improve our success ratio at reading external files
80 FileSystemFilter filter(pFile,pIOHandler);
82 // create a scene object to hold the data
83 ScopeGuard<aiScene> sc(new aiScene());
85 // dispatch importing
86 try
87 {
88 InternReadFile( pFile, sc, &filter);
90 } catch( const std::exception& err ) {
91 // extract error description
92 mErrorText = err.what();
93 DefaultLogger::get()->error(mErrorText);
94 return NULL;
95 }
97 // return what we gathered from the import.
98 sc.dismiss();
99 return sc;
100 }
102 // ------------------------------------------------------------------------------------------------
103 void BaseImporter::SetupProperties(const Importer* /*pImp*/)
104 {
105 // the default implementation does nothing
106 }
108 // ------------------------------------------------------------------------------------------------
109 void BaseImporter::GetExtensionList(std::set<std::string>& extensions)
110 {
111 const aiImporterDesc* desc = GetInfo();
112 ai_assert(desc != NULL);
114 const char* ext = desc->mFileExtensions;
115 ai_assert(ext != NULL);
117 const char* last = ext;
118 do {
119 if (!*ext || *ext == ' ') {
120 extensions.insert(std::string(last,ext-last));
121 ai_assert(ext-last > 0);
122 last = ext;
123 while(*last == ' ') {
124 ++last;
125 }
126 }
127 }
128 while(*ext++);
129 }
131 // ------------------------------------------------------------------------------------------------
132 /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
133 const std::string& pFile,
134 const char** tokens,
135 unsigned int numTokens,
136 unsigned int searchBytes /* = 200 */,
137 bool tokensSol /* false */)
138 {
139 ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes);
140 if (!pIOHandler)
141 return false;
143 boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
144 if (pStream.get() ) {
145 // read 200 characters from the file
146 boost::scoped_array<char> _buffer (new char[searchBytes+1 /* for the '\0' */]);
147 char* buffer = _buffer.get();
149 const unsigned int read = pStream->Read(buffer,1,searchBytes);
150 if (!read)
151 return false;
153 for (unsigned int i = 0; i < read;++i)
154 buffer[i] = ::tolower(buffer[i]);
156 // It is not a proper handling of unicode files here ...
157 // ehm ... but it works in most cases.
158 char* cur = buffer,*cur2 = buffer,*end = &buffer[read];
159 while (cur != end) {
160 if (*cur)
161 *cur2++ = *cur;
162 ++cur;
163 }
164 *cur2 = '\0';
166 for (unsigned int i = 0; i < numTokens;++i) {
167 ai_assert(NULL != tokens[i]);
170 const char* r = strstr(buffer,tokens[i]);
171 if (!r)
172 continue;
173 // We got a match, either we don't care where it is, or it happens to
174 // be in the beginning of the file / line
175 if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
176 DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]);
177 return true;
178 }
179 }
180 }
181 return false;
182 }
184 // ------------------------------------------------------------------------------------------------
185 // Simple check for file extension
186 /*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
187 const char* ext0,
188 const char* ext1,
189 const char* ext2)
190 {
191 std::string::size_type pos = pFile.find_last_of('.');
193 // no file extension - can't read
194 if( pos == std::string::npos)
195 return false;
197 const char* ext_real = & pFile[ pos+1 ];
198 if( !ASSIMP_stricmp(ext_real,ext0) )
199 return true;
201 // check for other, optional, file extensions
202 if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
203 return true;
205 if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
206 return true;
208 return false;
209 }
211 // ------------------------------------------------------------------------------------------------
212 // Get file extension from path
213 /*static*/ std::string BaseImporter::GetExtension (const std::string& pFile)
214 {
215 std::string::size_type pos = pFile.find_last_of('.');
217 // no file extension at all
218 if( pos == std::string::npos)
219 return "";
221 std::string ret = pFile.substr(pos+1);
222 std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint
223 return ret;
224 }
226 // ------------------------------------------------------------------------------------------------
227 // Check for magic bytes at the beginning of the file.
228 /* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
229 const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
230 {
231 ai_assert(size <= 16 && _magic);
233 if (!pIOHandler) {
234 return false;
235 }
236 union {
237 const char* magic;
238 const uint16_t* magic_u16;
239 const uint32_t* magic_u32;
240 };
241 magic = reinterpret_cast<const char*>(_magic);
242 boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
243 if (pStream.get() ) {
245 // skip to offset
246 pStream->Seek(offset,aiOrigin_SET);
248 // read 'size' characters from the file
249 union {
250 char data[16];
251 uint16_t data_u16[8];
252 uint32_t data_u32[4];
253 };
254 if(size != pStream->Read(data,1,size)) {
255 return false;
256 }
258 for (unsigned int i = 0; i < num; ++i) {
259 // also check against big endian versions of tokens with size 2,4
260 // that's just for convinience, the chance that we cause conflicts
261 // is quite low and it can save some lines and prevent nasty bugs
262 if (2 == size) {
263 uint16_t rev = *magic_u16;
264 ByteSwap::Swap(&rev);
265 if (data_u16[0] == *magic_u16 || data_u16[0] == rev) {
266 return true;
267 }
268 }
269 else if (4 == size) {
270 uint32_t rev = *magic_u32;
271 ByteSwap::Swap(&rev);
272 if (data_u32[0] == *magic_u32 || data_u32[0] == rev) {
273 return true;
274 }
275 }
276 else {
277 // any length ... just compare
278 if(!memcmp(magic,data,size)) {
279 return true;
280 }
281 }
282 magic += size;
283 }
284 }
285 return false;
286 }
288 #include "ConvertUTF/ConvertUTF.h"
290 // ------------------------------------------------------------------------------------------------
291 void ReportResult(ConversionResult res)
292 {
293 if(res == sourceExhausted) {
294 DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails");
295 }
296 else if(res == sourceIllegal) {
297 DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails");
298 }
299 }
301 // ------------------------------------------------------------------------------------------------
302 // Convert to UTF8 data
303 void BaseImporter::ConvertToUTF8(std::vector<char>& data)
304 {
305 ConversionResult result;
306 if(data.size() < 8) {
307 throw DeadlyImportError("File is too small");
308 }
310 // UTF 8 with BOM
311 if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
312 DefaultLogger::get()->debug("Found UTF-8 BOM ...");
314 std::copy(data.begin()+3,data.end(),data.begin());
315 data.resize(data.size()-3);
316 return;
317 }
319 // UTF 32 BE with BOM
320 if(*((uint32_t*)&data.front()) == 0xFFFE0000) {
322 // swap the endianess ..
323 for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) {
324 AI_SWAP4P(p);
325 }
326 }
328 // UTF 32 LE with BOM
329 if(*((uint32_t*)&data.front()) == 0x0000FFFE) {
330 DefaultLogger::get()->debug("Found UTF-32 BOM ...");
332 const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1;
333 char* dstart,*dend;
334 std::vector<char> output;
335 do {
336 output.resize(output.size()?output.size()*3/2:data.size()/2);
337 dstart = &output.front(),dend = &output.back()+1;
339 result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
340 } while(result == targetExhausted);
342 ReportResult(result);
344 // copy to output buffer.
345 const size_t outlen = (size_t)(dstart-&output.front());
346 data.assign(output.begin(),output.begin()+outlen);
347 return;
348 }
350 // UTF 16 BE with BOM
351 if(*((uint16_t*)&data.front()) == 0xFFFE) {
353 // swap the endianess ..
354 for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) {
355 ByteSwap::Swap2(p);
356 }
357 }
359 // UTF 16 LE with BOM
360 if(*((uint16_t*)&data.front()) == 0xFEFF) {
361 DefaultLogger::get()->debug("Found UTF-16 BOM ...");
363 const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)(&data.back()+1);
364 char* dstart,*dend;
365 std::vector<char> output;
366 do {
367 output.resize(output.size()?output.size()*3/2:data.size()*3/4);
368 dstart = &output.front(),dend = &output.back()+1;
370 result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
371 } while(result == targetExhausted);
373 ReportResult(result);
375 // copy to output buffer.
376 const size_t outlen = (size_t)(dstart-&output.front());
377 data.assign(output.begin(),output.begin()+outlen);
378 return;
379 }
380 }
382 // ------------------------------------------------------------------------------------------------
383 void BaseImporter::TextFileToBuffer(IOStream* stream,
384 std::vector<char>& data)
385 {
386 ai_assert(NULL != stream);
388 const size_t fileSize = stream->FileSize();
389 if(!fileSize) {
390 throw DeadlyImportError("File is empty");
391 }
393 data.reserve(fileSize+1);
394 data.resize(fileSize);
395 if(fileSize != stream->Read( &data[0], 1, fileSize)) {
396 throw DeadlyImportError("File read error");
397 }
399 ConvertToUTF8(data);
401 // append a binary zero to simplify string parsing
402 data.push_back(0);
403 }
405 // ------------------------------------------------------------------------------------------------
406 namespace Assimp
407 {
408 // Represents an import request
409 struct LoadRequest
410 {
411 LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
412 : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id)
413 {
414 if (_map)
415 map = *_map;
416 }
418 const std::string file;
419 unsigned int flags;
420 unsigned int refCnt;
421 aiScene* scene;
422 bool loaded;
423 BatchLoader::PropertyMap map;
424 unsigned int id;
426 bool operator== (const std::string& f) {
427 return file == f;
428 }
429 };
430 }
432 // ------------------------------------------------------------------------------------------------
433 // BatchLoader::pimpl data structure
434 struct Assimp::BatchData
435 {
436 BatchData()
437 : next_id(0xffff)
438 {}
440 // IO system to be used for all imports
441 IOSystem* pIOSystem;
443 // Importer used to load all meshes
444 Importer* pImporter;
446 // List of all imports
447 std::list<LoadRequest> requests;
449 // Base path
450 std::string pathBase;
452 // Id for next item
453 unsigned int next_id;
454 };
456 // ------------------------------------------------------------------------------------------------
457 BatchLoader::BatchLoader(IOSystem* pIO)
458 {
459 ai_assert(NULL != pIO);
461 data = new BatchData();
462 data->pIOSystem = pIO;
464 data->pImporter = new Importer();
465 data->pImporter->SetIOHandler(data->pIOSystem);
466 }
468 // ------------------------------------------------------------------------------------------------
469 BatchLoader::~BatchLoader()
470 {
471 // delete all scenes wthat have not been polled by the user
472 for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
474 delete (*it).scene;
475 }
476 data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */
477 delete data->pImporter;
478 delete data;
479 }
482 // ------------------------------------------------------------------------------------------------
483 unsigned int BatchLoader::AddLoadRequest (const std::string& file,
484 unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
485 {
486 ai_assert(!file.empty());
488 // check whether we have this loading request already
489 std::list<LoadRequest>::iterator it;
490 for (it = data->requests.begin();it != data->requests.end(); ++it) {
492 // Call IOSystem's path comparison function here
493 if (data->pIOSystem->ComparePaths((*it).file,file)) {
495 if (map) {
496 if (!((*it).map == *map))
497 continue;
498 }
499 else if (!(*it).map.empty())
500 continue;
502 (*it).refCnt++;
503 return (*it).id;
504 }
505 }
507 // no, we don't have it. So add it to the queue ...
508 data->requests.push_back(LoadRequest(file,steps,map,data->next_id));
509 return data->next_id++;
510 }
512 // ------------------------------------------------------------------------------------------------
513 aiScene* BatchLoader::GetImport (unsigned int which)
514 {
515 for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
517 if ((*it).id == which && (*it).loaded) {
519 aiScene* sc = (*it).scene;
520 if (!(--(*it).refCnt)) {
521 data->requests.erase(it);
522 }
523 return sc;
524 }
525 }
526 return NULL;
527 }
529 // ------------------------------------------------------------------------------------------------
530 void BatchLoader::LoadAll()
531 {
532 // no threaded implementation for the moment
533 for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
534 // force validation in debug builds
535 unsigned int pp = (*it).flags;
536 #ifdef _DEBUG
537 pp |= aiProcess_ValidateDataStructure;
538 #endif
539 // setup config properties if necessary
540 ImporterPimpl* pimpl = data->pImporter->Pimpl();
541 pimpl->mFloatProperties = (*it).map.floats;
542 pimpl->mIntProperties = (*it).map.ints;
543 pimpl->mStringProperties = (*it).map.strings;
545 if (!DefaultLogger::isNullLogger())
546 {
547 DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%");
548 DefaultLogger::get()->info("File: " + (*it).file);
549 }
550 data->pImporter->ReadFile((*it).file,pp);
551 (*it).scene = data->pImporter->GetOrphanedScene();
552 (*it).loaded = true;
554 DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%");
555 }
556 }