/src/assimp/code/Common/BaseImporter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
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: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
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. |
22 | | |
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. |
27 | | |
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 | | */ |
41 | | |
42 | | /** @file BaseImporter.cpp |
43 | | * @brief Implementation of BaseImporter |
44 | | */ |
45 | | |
46 | | #include "FileSystemFilter.h" |
47 | | #include "Importer.h" |
48 | | #include <assimp/BaseImporter.h> |
49 | | #include <assimp/ByteSwapper.h> |
50 | | #include <assimp/ParsingUtils.h> |
51 | | #include <assimp/importerdesc.h> |
52 | | #include <assimp/postprocess.h> |
53 | | #include <assimp/scene.h> |
54 | | #include <assimp/Importer.hpp> |
55 | | |
56 | | #include <cctype> |
57 | | #include <ios> |
58 | | #include <list> |
59 | | #include <memory> |
60 | | #include <sstream> |
61 | | |
62 | | namespace { |
63 | | // Checks whether the passed string is a gcs version. |
64 | 0 | bool IsGcsVersion(const std::string &s) { |
65 | 0 | if (s.empty()) return false; |
66 | 0 | return std::all_of(s.cbegin(), s.cend(), [](const char c) { |
67 | | // gcs only permits numeric characters. |
68 | 0 | return std::isdigit(static_cast<int>(c)); |
69 | 0 | }); |
70 | 0 | } |
71 | | |
72 | | // Removes a possible version hash from a filename, as found for example in |
73 | | // gcs uris (e.g. `gs://bucket/model.glb#1234`), see also |
74 | | // https://github.com/GoogleCloudPlatform/gsutil/blob/c80f329bc3c4011236c78ce8910988773b2606cb/gslib/storage_url.py#L39. |
75 | 21.4k | std::string StripVersionHash(const std::string &filename) { |
76 | 21.4k | const std::string::size_type pos = filename.find_last_of('#'); |
77 | | // Only strip if the hash is behind a possible file extension and the part |
78 | | // behind the hash is a version string. |
79 | 21.4k | if (pos != std::string::npos && pos > filename.find_last_of('.') && |
80 | 21.4k | IsGcsVersion(filename.substr(pos + 1))) { |
81 | 0 | return filename.substr(0, pos); |
82 | 0 | } |
83 | 21.4k | return filename; |
84 | 21.4k | } |
85 | | } // namespace |
86 | | |
87 | | using namespace Assimp; |
88 | | |
89 | | // ------------------------------------------------------------------------------------------------ |
90 | | // Constructor to be privately used by Importer |
91 | | BaseImporter::BaseImporter() AI_NO_EXCEPT |
92 | 19.8k | : m_progress() { |
93 | | // empty |
94 | 19.8k | } |
95 | | |
96 | 184 | void BaseImporter::UpdateImporterScale(Importer *pImp) { |
97 | 184 | ai_assert(pImp != nullptr); |
98 | 184 | ai_assert(importerScale != 0.0); |
99 | 184 | ai_assert(fileScale != 0.0); |
100 | | |
101 | 184 | double activeScale = importerScale * fileScale; |
102 | | |
103 | | // Set active scaling |
104 | 184 | pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast<float>(activeScale)); |
105 | | |
106 | 184 | ASSIMP_LOG_DEBUG("UpdateImporterScale scale set: ", activeScale); |
107 | 184 | } |
108 | | |
109 | | // ------------------------------------------------------------------------------------------------ |
110 | | // Imports the given file and returns the imported data. |
111 | 354 | aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSystem *pIOHandler) { |
112 | | |
113 | 354 | m_progress = pImp->GetProgressHandler(); |
114 | 354 | if (nullptr == m_progress) { |
115 | 0 | return nullptr; |
116 | 0 | } |
117 | | |
118 | 354 | ai_assert(m_progress); |
119 | | |
120 | | // Gather configuration properties for this run |
121 | 354 | SetupProperties(pImp); |
122 | | |
123 | | // Construct a file system filter to improve our success ratio at reading external files |
124 | 354 | FileSystemFilter filter(pFile, pIOHandler); |
125 | | |
126 | | // create a scene object to hold the data |
127 | 354 | std::unique_ptr<aiScene> sc(new aiScene()); |
128 | | |
129 | | // dispatch importing |
130 | 354 | try { |
131 | 354 | InternReadFile(pFile, sc.get(), &filter); |
132 | | |
133 | | // Calculate import scale hook - required because pImp not available anywhere else |
134 | | // passes scale into ScaleProcess |
135 | 354 | UpdateImporterScale(pImp); |
136 | | |
137 | 354 | } catch( const std::exception &err ) { |
138 | | // extract error description |
139 | 170 | m_ErrorText = err.what(); |
140 | 170 | ASSIMP_LOG_ERROR(err.what()); |
141 | 170 | m_Exception = std::current_exception(); |
142 | 170 | return nullptr; |
143 | 170 | } |
144 | | |
145 | | // return what we gathered from the import. |
146 | 184 | return sc.release(); |
147 | 354 | } |
148 | | |
149 | | // ------------------------------------------------------------------------------------------------ |
150 | 160 | void BaseImporter::SetupProperties(const Importer *) { |
151 | | // the default implementation does nothing |
152 | 160 | } |
153 | | |
154 | | // ------------------------------------------------------------------------------------------------ |
155 | 20.2k | void BaseImporter::GetExtensionList(std::set<std::string> &extensions) { |
156 | 20.2k | const aiImporterDesc *desc = GetInfo(); |
157 | 20.2k | ai_assert(desc != nullptr); |
158 | | |
159 | 20.2k | const char *ext = desc->mFileExtensions; |
160 | 20.2k | ai_assert(ext != nullptr); |
161 | | |
162 | 20.2k | const char *last = ext; |
163 | 141k | do { |
164 | 141k | if (!*ext || *ext == ' ') { |
165 | 31.6k | extensions.insert(std::string(last, ext - last)); |
166 | 31.6k | ai_assert(ext - last > 0); |
167 | 31.6k | last = ext; |
168 | 43.0k | while (*last == ' ') { |
169 | 11.3k | ++last; |
170 | 11.3k | } |
171 | 31.6k | } |
172 | 141k | } while (*ext++); |
173 | 20.2k | } |
174 | | |
175 | | // ------------------------------------------------------------------------------------------------ |
176 | | /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, |
177 | | const std::string &pFile, |
178 | | const char **tokens, |
179 | | std::size_t numTokens, |
180 | | unsigned int searchBytes /* = 200 */, |
181 | | bool tokensSol /* false */, |
182 | 3.70k | bool noGraphBeforeTokens /* false */) { |
183 | 3.70k | ai_assert(nullptr != tokens); |
184 | 3.70k | ai_assert(0 != numTokens); |
185 | 3.70k | ai_assert(0 != searchBytes); |
186 | | |
187 | 3.70k | if (nullptr == pIOHandler) { |
188 | 0 | return false; |
189 | 0 | } |
190 | | |
191 | 3.70k | std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile)); |
192 | 3.70k | if (pStream) { |
193 | | // read 200 characters from the file |
194 | 3.70k | std::unique_ptr<char[]> _buffer(new char[searchBytes + 1 /* for the '\0' */]); |
195 | 3.70k | char *buffer(_buffer.get()); |
196 | 3.70k | const size_t read(pStream->Read(buffer, 1, searchBytes)); |
197 | 3.70k | if (0 == read) { |
198 | 0 | return false; |
199 | 0 | } |
200 | | |
201 | 667k | for (size_t i = 0; i < read; ++i) { |
202 | 663k | buffer[i] = static_cast<char>(::tolower((unsigned char)buffer[i])); |
203 | 663k | } |
204 | | |
205 | | // It is not a proper handling of unicode files here ... |
206 | | // ehm ... but it works in most cases. |
207 | 3.70k | char *cur = buffer, *cur2 = buffer, *end = &buffer[read]; |
208 | 667k | while (cur != end) { |
209 | 663k | if (*cur) { |
210 | 592k | *cur2++ = *cur; |
211 | 592k | } |
212 | 663k | ++cur; |
213 | 663k | } |
214 | 3.70k | *cur2 = '\0'; |
215 | | |
216 | 3.70k | std::string token; |
217 | 10.8k | for (unsigned int i = 0; i < numTokens; ++i) { |
218 | 7.41k | ai_assert(nullptr != tokens[i]); |
219 | 7.41k | const size_t len(strlen(tokens[i])); |
220 | 7.41k | token.clear(); |
221 | 7.41k | const char *ptr(tokens[i]); |
222 | 52.0k | for (size_t tokIdx = 0; tokIdx < len; ++tokIdx) { |
223 | 44.5k | token.push_back(static_cast<char>(tolower(static_cast<unsigned char>(*ptr)))); |
224 | 44.5k | ++ptr; |
225 | 44.5k | } |
226 | 7.41k | const char *r = strstr(buffer, token.c_str()); |
227 | 7.41k | if (!r) { |
228 | 7.15k | continue; |
229 | 7.15k | } |
230 | | // We need to make sure that we didn't accidentally identify the end of another token as our token, |
231 | | // e.g. in a previous version the "gltf " present in some gltf files was detected as "f ", or a |
232 | | // Blender-exported glb file containing "Khronos glTF Blender I/O " was detected as "o " |
233 | 260 | if (noGraphBeforeTokens && (r != buffer && isgraph(static_cast<unsigned char>(r[-1])))) { |
234 | 23 | continue; |
235 | 23 | } |
236 | | // We got a match, either we don't care where it is, or it happens to |
237 | | // be in the beginning of the file / line |
238 | 237 | if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { |
239 | 237 | ASSIMP_LOG_DEBUG("Found positive match for header keyword: ", tokens[i]); |
240 | 237 | return true; |
241 | 237 | } |
242 | 237 | } |
243 | 3.70k | } |
244 | | |
245 | 3.47k | return false; |
246 | 3.70k | } |
247 | | |
248 | | // ------------------------------------------------------------------------------------------------ |
249 | | // Simple check for file extension |
250 | | /*static*/ bool BaseImporter::SimpleExtensionCheck(const std::string &pFile, |
251 | | const char *ext0, |
252 | | const char *ext1, |
253 | | const char *ext2, |
254 | 938 | const char *ext3) { |
255 | 938 | std::set<std::string> extensions; |
256 | 3.75k | for (const char* ext : {ext0, ext1, ext2, ext3}) { |
257 | 3.75k | if (ext == nullptr) continue; |
258 | 1.51k | extensions.emplace(ext); |
259 | 1.51k | } |
260 | 938 | return HasExtension(pFile, extensions); |
261 | 938 | } |
262 | | |
263 | | // ------------------------------------------------------------------------------------------------ |
264 | | // Check for file extension |
265 | 21.1k | /*static*/ bool BaseImporter::HasExtension(const std::string &pFile, const std::set<std::string> &extensions) { |
266 | 21.1k | const std::string file = StripVersionHash(pFile); |
267 | | // CAUTION: Do not just search for the extension! |
268 | | // GetExtension() returns the part after the *last* dot, but some extensions |
269 | | // have dots inside them, e.g. ogre.mesh.xml. Compare the entire end of the |
270 | | // string. |
271 | 33.1k | for (const std::string& ext : extensions) { |
272 | | // Yay for C++<20 not having std::string::ends_with() |
273 | 33.1k | const std::string dotExt = "." + ext; |
274 | 33.1k | if (dotExt.length() > file.length()) continue; |
275 | | // Possible optimization: Fetch the lowercase filename! |
276 | 33.1k | if (0 == ASSIMP_stricmp(file.c_str() + file.length() - dotExt.length(), dotExt.c_str())) { |
277 | 36 | return true; |
278 | 36 | } |
279 | 33.1k | } |
280 | 21.1k | return false; |
281 | 21.1k | } |
282 | | |
283 | | // ------------------------------------------------------------------------------------------------ |
284 | | // Get file extension from path |
285 | 295 | std::string BaseImporter::GetExtension(const std::string &pFile) { |
286 | 295 | const std::string file = StripVersionHash(pFile); |
287 | 295 | std::string::size_type pos = file.find_last_of('.'); |
288 | | |
289 | | // no file extension at all |
290 | 295 | if (pos == std::string::npos) { |
291 | 3 | return std::string(); |
292 | 3 | } |
293 | | |
294 | | // thanks to Andy Maloney for the hint |
295 | 292 | std::string ret = file.substr(pos + 1); |
296 | 292 | ret = ai_tolower(ret); |
297 | | |
298 | 292 | return ret; |
299 | 295 | } |
300 | | |
301 | | |
302 | | // ------------------------------------------------------------------------------------------------ |
303 | | // Check for magic bytes at the beginning of the file. |
304 | | /* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile, |
305 | 2.56k | const void *_magic, std::size_t num, unsigned int offset, unsigned int size) { |
306 | 2.56k | ai_assert(size <= 16); |
307 | 2.56k | ai_assert(_magic); |
308 | | |
309 | 2.56k | if (!pIOHandler) { |
310 | 0 | return false; |
311 | 0 | } |
312 | 2.56k | const char *magic = reinterpret_cast<const char *>(_magic); |
313 | 2.56k | std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile)); |
314 | 2.56k | if (pStream) { |
315 | | |
316 | | // skip to offset |
317 | 2.56k | pStream->Seek(offset, aiOrigin_SET); |
318 | | |
319 | | // read 'size' characters from the file |
320 | 2.56k | union { |
321 | 2.56k | char data[16]; |
322 | 2.56k | uint16_t data_u16[8]; |
323 | 2.56k | uint32_t data_u32[4]; |
324 | 2.56k | }; |
325 | 2.56k | if (size != pStream->Read(data, 1, size)) { |
326 | 0 | return false; |
327 | 0 | } |
328 | | |
329 | 8.22k | for (unsigned int i = 0; i < num; ++i) { |
330 | | // also check against big endian versions of tokens with size 2,4 |
331 | | // that's just for convenience, the chance that we cause conflicts |
332 | | // is quite low and it can save some lines and prevent nasty bugs |
333 | 5.74k | if (2 == size) { |
334 | 568 | uint16_t magic_u16; |
335 | 568 | memcpy(&magic_u16, magic, 2); |
336 | 568 | if (data_u16[0] == magic_u16 || data_u16[0] == ByteSwap::Swapped(magic_u16)) { |
337 | 2 | return true; |
338 | 2 | } |
339 | 5.17k | } else if (4 == size) { |
340 | 5.17k | uint32_t magic_u32; |
341 | 5.17k | memcpy(&magic_u32, magic, 4); |
342 | 5.17k | if (data_u32[0] == magic_u32 || data_u32[0] == ByteSwap::Swapped(magic_u32)) { |
343 | 79 | return true; |
344 | 79 | } |
345 | 5.17k | } else { |
346 | | // any length ... just compare |
347 | 0 | if (!memcmp(magic, data, size)) { |
348 | 0 | return true; |
349 | 0 | } |
350 | 0 | } |
351 | 5.66k | magic += size; |
352 | 5.66k | } |
353 | 2.56k | } |
354 | 2.48k | return false; |
355 | 2.56k | } |
356 | | |
357 | | #include "utf8.h" |
358 | | |
359 | | // ------------------------------------------------------------------------------------------------ |
360 | | // Convert to UTF8 data |
361 | 2.87k | void BaseImporter::ConvertToUTF8(std::vector<char> &data) { |
362 | | //ConversionResult result; |
363 | 2.87k | if (data.size() < 8) { |
364 | 0 | throw DeadlyImportError("File is too small"); |
365 | 0 | } |
366 | | |
367 | | // UTF 8 with BOM |
368 | 2.87k | if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { |
369 | 0 | ASSIMP_LOG_DEBUG("Found UTF-8 BOM ..."); |
370 | |
|
371 | 0 | std::copy(data.begin() + 3, data.end(), data.begin()); |
372 | 0 | data.resize(data.size() - 3); |
373 | 0 | return; |
374 | 0 | } |
375 | | |
376 | | // UTF 32 BE with BOM |
377 | 2.87k | if (*((uint32_t *)&data.front()) == 0xFFFE0000) { |
378 | 0 | if (data.size() % sizeof(uint32_t) != 0) { |
379 | 0 | throw DeadlyImportError("Not valid UTF-32 BE"); |
380 | 0 | } |
381 | | |
382 | | // swap the endianness .. |
383 | 0 | for (uint32_t *p = (uint32_t *)&data.front(), *end = (uint32_t *)&data.back(); p <= end; ++p) { |
384 | 0 | AI_SWAP4P(p); |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | // UTF 32 LE with BOM |
389 | 2.87k | if (*((uint32_t *)&data.front()) == 0x0000FFFE) { |
390 | 0 | if (data.size() % sizeof(uint32_t) != 0) { |
391 | 0 | throw DeadlyImportError("Not valid UTF-32 LE"); |
392 | 0 | } |
393 | 0 | ASSIMP_LOG_DEBUG("Found UTF-32 BOM ..."); |
394 | |
|
395 | 0 | std::vector<char> output; |
396 | 0 | auto *ptr = (uint32_t *)&data[0]; |
397 | 0 | uint32_t *end = ptr + (data.size() / sizeof(uint32_t)) + 1; |
398 | 0 | utf8::utf32to8(ptr, end, back_inserter(output)); |
399 | 0 | return; |
400 | 0 | } |
401 | | |
402 | | // UTF 16 BE with BOM |
403 | 2.87k | if (*((uint16_t *)&data.front()) == 0xFFFE) { |
404 | | // Check to ensure no overflow can happen |
405 | 0 | if (data.size() % sizeof(uint16_t) != 0) { |
406 | 0 | throw DeadlyImportError("Not valid UTF-16 BE"); |
407 | 0 | } |
408 | | // swap the endianness .. |
409 | 0 | for (uint16_t *p = (uint16_t *)&data.front(), *end = (uint16_t *)&data.back(); p <= end; ++p) { |
410 | 0 | ByteSwap::Swap2(p); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | // UTF 16 LE with BOM |
415 | 2.87k | if (*((uint16_t *)&data.front()) == 0xFEFF) { |
416 | 0 | if (data.size() % sizeof(uint16_t) != 0) { |
417 | 0 | throw DeadlyImportError("Not valid UTF-16 LE"); |
418 | 0 | } |
419 | 0 | ASSIMP_LOG_DEBUG("Found UTF-16 BOM ..."); |
420 | |
|
421 | 0 | std::vector<unsigned char> output; |
422 | 0 | utf8::utf16to8(data.begin(), data.end(), back_inserter(output)); |
423 | 0 | return; |
424 | 0 | } |
425 | 2.87k | } |
426 | | |
427 | | // ------------------------------------------------------------------------------------------------ |
428 | | // Convert to UTF8 data to ISO-8859-1 |
429 | 0 | void BaseImporter::ConvertUTF8toISO8859_1(std::string &data) { |
430 | 0 | size_t size = data.size(); |
431 | 0 | size_t i = 0, j = 0; |
432 | |
|
433 | 0 | while (i < size) { |
434 | 0 | if ((unsigned char)data[i] < (size_t)0x80) { |
435 | 0 | data[j] = data[i]; |
436 | 0 | } else if (i < size - 1) { |
437 | 0 | if ((unsigned char)data[i] == 0xC2) { |
438 | 0 | data[j] = data[++i]; |
439 | 0 | } else if ((unsigned char)data[i] == 0xC3) { |
440 | 0 | data[j] = ((unsigned char)data[++i] + 0x40); |
441 | 0 | } else { |
442 | 0 | std::stringstream stream; |
443 | 0 | stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; |
444 | 0 | ASSIMP_LOG_ERROR(stream.str()); |
445 | |
|
446 | 0 | data[j++] = data[i++]; |
447 | 0 | data[j] = data[i]; |
448 | 0 | } |
449 | 0 | } else { |
450 | 0 | ASSIMP_LOG_ERROR("UTF8 code but only one character remaining"); |
451 | |
|
452 | 0 | data[j] = data[i]; |
453 | 0 | } |
454 | |
|
455 | 0 | i++; |
456 | 0 | j++; |
457 | 0 | } |
458 | |
|
459 | 0 | data.resize(j); |
460 | 0 | } |
461 | | |
462 | | // ------------------------------------------------------------------------------------------------ |
463 | | void BaseImporter::TextFileToBuffer(IOStream *stream, |
464 | | std::vector<char> &data, |
465 | 2.87k | TextFileMode mode) { |
466 | 2.87k | ai_assert(nullptr != stream); |
467 | | |
468 | 2.87k | const size_t fileSize = stream->FileSize(); |
469 | 2.87k | if (mode == FORBID_EMPTY) { |
470 | 557 | if (!fileSize) { |
471 | 0 | throw DeadlyImportError("File is empty"); |
472 | 0 | } |
473 | 557 | } |
474 | | |
475 | 2.87k | data.reserve(fileSize + 1); |
476 | 2.87k | data.resize(fileSize); |
477 | 2.87k | if (fileSize > 0) { |
478 | 2.87k | if (fileSize != stream->Read(&data[0], 1, fileSize)) { |
479 | 0 | throw DeadlyImportError("File read error"); |
480 | 0 | } |
481 | | |
482 | 2.87k | ConvertToUTF8(data); |
483 | 2.87k | } |
484 | | |
485 | | // append a binary zero to simplify string parsing |
486 | 2.87k | data.push_back(0); |
487 | 2.87k | } |
488 | | |
489 | | // ------------------------------------------------------------------------------------------------ |
490 | | namespace Assimp { |
491 | | // Represents an import request |
492 | | struct LoadRequest { |
493 | | LoadRequest(const std::string &_file, unsigned int _flags, const BatchLoader::PropertyMap *_map, unsigned int _id) : |
494 | 749 | file(_file), |
495 | 749 | flags(_flags), |
496 | 749 | refCnt(1), |
497 | 749 | scene(nullptr), |
498 | 749 | loaded(false), |
499 | 749 | id(_id) { |
500 | 749 | if (_map) { |
501 | 245 | map = *_map; |
502 | 245 | } |
503 | 749 | } |
504 | | |
505 | 0 | bool operator==(const std::string &f) const { |
506 | 0 | return file == f; |
507 | 0 | } |
508 | | |
509 | | const std::string file; |
510 | | unsigned int flags; |
511 | | unsigned int refCnt; |
512 | | aiScene *scene; |
513 | | bool loaded; |
514 | | BatchLoader::PropertyMap map; |
515 | | unsigned int id; |
516 | | }; |
517 | | } // namespace Assimp |
518 | | |
519 | | // ------------------------------------------------------------------------------------------------ |
520 | | // BatchLoader::pimpl data structure |
521 | | struct Assimp::BatchData { |
522 | | BatchData(IOSystem *pIO, bool validate) : |
523 | 76 | pIOSystem(pIO), pImporter(nullptr), next_id(0xffff), validate(validate) { |
524 | 76 | ai_assert(nullptr != pIO); |
525 | | |
526 | 76 | pImporter = new Importer(); |
527 | 76 | pImporter->SetIOHandler(pIO); |
528 | 76 | } |
529 | | |
530 | 76 | ~BatchData() { |
531 | 76 | pImporter->SetIOHandler(nullptr); /* get pointer back into our possession */ |
532 | 76 | delete pImporter; |
533 | 76 | } |
534 | | |
535 | | // IO system to be used for all imports |
536 | | IOSystem *pIOSystem; |
537 | | |
538 | | // Importer used to load all meshes |
539 | | Importer *pImporter; |
540 | | |
541 | | // List of all imports |
542 | | std::list<LoadRequest> requests; |
543 | | |
544 | | // Base path |
545 | | std::string pathBase; |
546 | | |
547 | | // Id for next item |
548 | | unsigned int next_id; |
549 | | |
550 | | // Validation enabled state |
551 | | bool validate; |
552 | | }; |
553 | | |
554 | | typedef std::list<LoadRequest>::iterator LoadReqIt; |
555 | | |
556 | | // ------------------------------------------------------------------------------------------------ |
557 | 76 | BatchLoader::BatchLoader(IOSystem *pIO, bool validate) { |
558 | 76 | ai_assert(nullptr != pIO); |
559 | | |
560 | 76 | m_data = new BatchData(pIO, validate); |
561 | 76 | } |
562 | | |
563 | | // ------------------------------------------------------------------------------------------------ |
564 | 76 | BatchLoader::~BatchLoader() { |
565 | | // delete all scenes what have not been polled by the user |
566 | 546 | for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { |
567 | 470 | delete (*it).scene; |
568 | 470 | } |
569 | 76 | delete m_data; |
570 | 76 | } |
571 | | |
572 | | // ------------------------------------------------------------------------------------------------ |
573 | 0 | void BatchLoader::setValidation(bool enabled) { |
574 | 0 | m_data->validate = enabled; |
575 | 0 | } |
576 | | |
577 | | // ------------------------------------------------------------------------------------------------ |
578 | 0 | bool BatchLoader::getValidation() const { |
579 | 0 | return m_data->validate; |
580 | 0 | } |
581 | | |
582 | | // ------------------------------------------------------------------------------------------------ |
583 | | unsigned int BatchLoader::AddLoadRequest(const std::string &file, |
584 | 8.92k | unsigned int steps /*= 0*/, const PropertyMap *map /*= nullptr*/) { |
585 | 8.92k | ai_assert(!file.empty()); |
586 | | |
587 | | // check whether we have this loading request already |
588 | 32.8k | for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { |
589 | | // Call IOSystem's path comparison function here |
590 | 32.0k | if (m_data->pIOSystem->ComparePaths((*it).file, file)) { |
591 | 8.20k | if (map) { |
592 | 1.73k | if (!((*it).map == *map)) { |
593 | 24 | continue; |
594 | 24 | } |
595 | 6.46k | } else if (!(*it).map.empty()) { |
596 | 0 | continue; |
597 | 0 | } |
598 | | |
599 | 8.17k | (*it).refCnt++; |
600 | 8.17k | return (*it).id; |
601 | 8.20k | } |
602 | 32.0k | } |
603 | | |
604 | | // no, we don't have it. So add it to the queue ... |
605 | 749 | m_data->requests.emplace_back(file, steps, map, m_data->next_id); |
606 | 749 | return m_data->next_id++; |
607 | 8.92k | } |
608 | | |
609 | | // ------------------------------------------------------------------------------------------------ |
610 | 2.70k | aiScene *BatchLoader::GetImport(unsigned int which) { |
611 | 5.54k | for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { |
612 | 5.54k | if ((*it).id == which && (*it).loaded) { |
613 | 2.70k | aiScene *sc = (*it).scene; |
614 | 2.70k | if (!(--(*it).refCnt)) { |
615 | 279 | m_data->requests.erase(it); |
616 | 279 | } |
617 | 2.70k | return sc; |
618 | 2.70k | } |
619 | 5.54k | } |
620 | 0 | return nullptr; |
621 | 2.70k | } |
622 | | |
623 | | // ------------------------------------------------------------------------------------------------ |
624 | 25 | void BatchLoader::LoadAll() { |
625 | | // no threaded implementation for the moment |
626 | 306 | for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) { |
627 | | // force validation in debug builds |
628 | 281 | unsigned int pp = (*it).flags; |
629 | 281 | if (m_data->validate) { |
630 | 0 | pp |= aiProcess_ValidateDataStructure; |
631 | 0 | } |
632 | | |
633 | | // setup config properties if necessary |
634 | 281 | ImporterPimpl *pimpl = m_data->pImporter->Pimpl(); |
635 | 281 | pimpl->mFloatProperties = (*it).map.floats; |
636 | 281 | pimpl->mIntProperties = (*it).map.ints; |
637 | 281 | pimpl->mStringProperties = (*it).map.strings; |
638 | 281 | pimpl->mMatrixProperties = (*it).map.matrices; |
639 | | |
640 | 281 | if (!DefaultLogger::isNullLogger()) { |
641 | 0 | ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); |
642 | 0 | ASSIMP_LOG_INFO("File: ", (*it).file); |
643 | 0 | } |
644 | 281 | m_data->pImporter->ReadFile((*it).file, pp); |
645 | 281 | (*it).scene = m_data->pImporter->GetOrphanedScene(); |
646 | 281 | (*it).loaded = true; |
647 | | |
648 | 281 | ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%"); |
649 | 281 | } |
650 | 25 | } |