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