/src/assimp/code/AssetLib/ASE/ASEParser.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 ASEParser.cpp |
43 | | * @brief Implementation of the ASE parser class |
44 | | */ |
45 | | |
46 | | #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER |
47 | | #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER |
48 | | |
49 | | // internal headers |
50 | | #include "ASELoader.h" |
51 | | #include "PostProcessing/TextureTransform.h" |
52 | | |
53 | | #include <assimp/fast_atof.h> |
54 | | #include <assimp/DefaultLogger.hpp> |
55 | | |
56 | | namespace Assimp { |
57 | | using namespace Assimp::ASE; |
58 | | |
59 | | // ------------------------------------------------------------------------------------------------ |
60 | | // Begin an ASE parsing function |
61 | | |
62 | | #define AI_ASE_PARSER_INIT() \ |
63 | 588 | int iDepth = 0; |
64 | | |
65 | | // ------------------------------------------------------------------------------------------------ |
66 | | // Handle a "top-level" section in the file. EOF is no error in this case. |
67 | | |
68 | | #define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ |
69 | 75.7k | else if ('{' == *mFilePtr) \ |
70 | 71.8k | ++iDepth; \ |
71 | 71.8k | else if ('}' == *mFilePtr) { \ |
72 | 1.75k | if (0 == --iDepth) { \ |
73 | 123 | ++mFilePtr; \ |
74 | 123 | SkipToNextToken(); \ |
75 | 123 | return; \ |
76 | 123 | } \ |
77 | 1.75k | } \ |
78 | 75.7k | if ('\0' == *mFilePtr) { \ |
79 | 14 | return; \ |
80 | 14 | } \ |
81 | 74.7k | if (IsLineEnd(*mFilePtr) && !bLastWasEndLine) {\ |
82 | 1.60k | ++iLineNumber; \ |
83 | 1.60k | bLastWasEndLine = true; \ |
84 | 1.60k | } else \ |
85 | 74.7k | bLastWasEndLine = false; \ |
86 | 74.7k | ++mFilePtr; |
87 | | |
88 | | // ------------------------------------------------------------------------------------------------ |
89 | | // Handle a nested section in the file. EOF is an error in this case |
90 | | // @param level "Depth" of the section |
91 | | // @param msg Full name of the section (including the asterisk) |
92 | | |
93 | | #define AI_ASE_HANDLE_SECTION(level, msg) \ |
94 | 287k | if ('{' == *mFilePtr) \ |
95 | 287k | iDepth++; \ |
96 | 287k | else if ('}' == *mFilePtr) { \ |
97 | 3.91k | if (0 == --iDepth) { \ |
98 | 407 | ++mFilePtr; \ |
99 | 407 | SkipToNextToken(); \ |
100 | 407 | return; \ |
101 | 407 | } \ |
102 | 282k | } else if ('\0' == *mFilePtr) { \ |
103 | 13 | LogError("Encountered unexpected EOL while parsing a " msg \ |
104 | 13 | " chunk (Level " level ")"); \ |
105 | 13 | } \ |
106 | 287k | if (IsLineEnd(*mFilePtr) && !bLastWasEndLine) { \ |
107 | 34.3k | ++iLineNumber; \ |
108 | 34.3k | bLastWasEndLine = true; \ |
109 | 34.3k | } else \ |
110 | 287k | bLastWasEndLine = false; \ |
111 | 287k | ++mFilePtr; |
112 | | |
113 | | // ------------------------------------------------------------------------------------------------ |
114 | | Parser::Parser(const char *file, size_t fileLen, unsigned int fileFormatDefault) : |
115 | 24 | mFilePtr(nullptr), mEnd (nullptr) { |
116 | 24 | ai_assert(file != nullptr); |
117 | | |
118 | 24 | mFilePtr = file; |
119 | 24 | mEnd = mFilePtr + fileLen; |
120 | 24 | iFileFormat = fileFormatDefault; |
121 | | |
122 | | // make sure that the color values are invalid |
123 | 24 | m_clrBackground.r = get_qnan(); |
124 | 24 | m_clrAmbient.r = get_qnan(); |
125 | | |
126 | | // setup some default values |
127 | 24 | iLineNumber = 0; |
128 | 24 | iFirstFrame = 0; |
129 | 24 | iLastFrame = 0; |
130 | 24 | iFrameSpeed = 30; // use 30 as default value for this property |
131 | 24 | iTicksPerFrame = 1; // use 1 as default value for this property |
132 | 24 | bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping |
133 | 24 | } |
134 | | |
135 | | // ------------------------------------------------------------------------------------------------ |
136 | 7.69k | void Parser::LogWarning(const char *szWarn) { |
137 | 7.69k | ai_assert(nullptr != szWarn); |
138 | | |
139 | 7.69k | char szTemp[2048]; |
140 | | #if _MSC_VER >= 1400 |
141 | | sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); |
142 | | #else |
143 | 7.69k | ai_snprintf(szTemp, sizeof(szTemp), "Line %u: %s", iLineNumber, szWarn); |
144 | 7.69k | #endif |
145 | | |
146 | | // output the warning to the logger ... |
147 | 7.69k | ASSIMP_LOG_WARN(szTemp); |
148 | 7.69k | } |
149 | | |
150 | | // ------------------------------------------------------------------------------------------------ |
151 | 6 | void Parser::LogInfo(const char *szWarn) { |
152 | 6 | ai_assert(nullptr != szWarn); |
153 | | |
154 | 6 | char szTemp[1024]; |
155 | | #if _MSC_VER >= 1400 |
156 | | sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); |
157 | | #else |
158 | 6 | ai_snprintf(szTemp, 1024, "Line %u: %s", iLineNumber, szWarn); |
159 | 6 | #endif |
160 | | |
161 | | // output the information to the logger ... |
162 | 6 | ASSIMP_LOG_INFO(szTemp); |
163 | 6 | } |
164 | | |
165 | | // ------------------------------------------------------------------------------------------------ |
166 | 13 | AI_WONT_RETURN void Parser::LogError(const char *szWarn) { |
167 | 13 | ai_assert(nullptr != szWarn); |
168 | | |
169 | 13 | char szTemp[1024]; |
170 | | #if _MSC_VER >= 1400 |
171 | | sprintf_s(szTemp, "Line %u: %s", iLineNumber, szWarn); |
172 | | #else |
173 | 13 | ai_snprintf(szTemp, 1024, "Line %u: %s", iLineNumber, szWarn); |
174 | 13 | #endif |
175 | | |
176 | | // throw an exception |
177 | 13 | throw DeadlyImportError(szTemp); |
178 | 13 | } |
179 | | |
180 | | // ------------------------------------------------------------------------------------------------ |
181 | 1.10k | bool Parser::SkipToNextToken() { |
182 | 15.6k | while (true) { |
183 | 15.6k | char me = *mFilePtr; |
184 | | |
185 | 15.6k | if (mFilePtr == mEnd) { |
186 | 0 | return false; |
187 | 0 | } |
188 | | |
189 | | // increase the line number counter if necessary |
190 | 15.6k | if (IsLineEnd(me) && !bLastWasEndLine) { |
191 | 1.51k | ++iLineNumber; |
192 | 1.51k | bLastWasEndLine = true; |
193 | 1.51k | } else |
194 | 14.1k | bLastWasEndLine = false; |
195 | 15.6k | if ('*' == me || '}' == me || '{' == me) { |
196 | 1.09k | return true; |
197 | 1.09k | } |
198 | 14.5k | if ('\0' == me) { |
199 | 10 | return false; |
200 | 10 | } |
201 | | |
202 | 14.5k | ++mFilePtr; |
203 | 14.5k | } |
204 | 1.10k | } |
205 | | |
206 | | // ------------------------------------------------------------------------------------------------ |
207 | 0 | bool Parser::SkipSection() { |
208 | | // must handle subsections ... |
209 | 0 | int iCnt = 0; |
210 | 0 | while (true) { |
211 | 0 | if ('}' == *mFilePtr) { |
212 | 0 | --iCnt; |
213 | 0 | if (0 == iCnt) { |
214 | | // go to the next valid token ... |
215 | 0 | ++mFilePtr; |
216 | 0 | SkipToNextToken(); |
217 | 0 | return true; |
218 | 0 | } |
219 | 0 | } else if ('{' == *mFilePtr) { |
220 | 0 | ++iCnt; |
221 | 0 | } else if ('\0' == *mFilePtr) { |
222 | 0 | LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); |
223 | 0 | return false; |
224 | 0 | } else if (IsLineEnd(*mFilePtr)) |
225 | 0 | ++iLineNumber; |
226 | 0 | ++mFilePtr; |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | // ------------------------------------------------------------------------------------------------ |
231 | 24 | void Parser::Parse() { |
232 | 24 | AI_ASE_PARSER_INIT(); |
233 | 14.3k | while (true) { |
234 | 14.3k | if ('*' == *mFilePtr) { |
235 | 678 | ++mFilePtr; |
236 | | |
237 | | // Version should be 200. Validate this ... |
238 | 678 | if (TokenMatch(mFilePtr, "3DSMAX_ASCIIEXPORT", 18)) { |
239 | 16 | unsigned int fmt; |
240 | 16 | ParseLV4MeshLong(fmt); |
241 | | |
242 | 16 | if (fmt > 200) { |
243 | 0 | LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ |
244 | 0 | be <= 200"); |
245 | 0 | } |
246 | | // ************************************************************* |
247 | | // - fmt will be 0 if we're unable to read the version number |
248 | | // there are some faulty files without a version number ... |
249 | | // in this case we'll guess the exact file format by looking |
250 | | // at the file extension (ASE, ASK, ASC) |
251 | | // ************************************************************* |
252 | | |
253 | 16 | if (fmt) { |
254 | 7 | iFileFormat = fmt; |
255 | 7 | } |
256 | 16 | continue; |
257 | 16 | } |
258 | | // main scene information |
259 | 662 | if (TokenMatch(mFilePtr, "SCENE", 5)) { |
260 | 6 | ParseLV1SceneBlock(); |
261 | 6 | continue; |
262 | 6 | } |
263 | | // "group" - no implementation yet, in facte |
264 | | // we're just ignoring them for the moment |
265 | 656 | if (TokenMatch(mFilePtr, "GROUP", 5)) { |
266 | 0 | Parse(); |
267 | 0 | continue; |
268 | 0 | } |
269 | | // material list |
270 | 656 | if (TokenMatch(mFilePtr, "MATERIAL_LIST", 13)) { |
271 | 102 | ParseLV1MaterialListBlock(); |
272 | 102 | continue; |
273 | 102 | } |
274 | | // geometric object (mesh) |
275 | 554 | if (TokenMatch(mFilePtr, "GEOMOBJECT", 10)) |
276 | | |
277 | 14 | { |
278 | 14 | m_vMeshes.emplace_back("UNNAMED"); |
279 | 14 | ParseLV1ObjectBlock(m_vMeshes.back()); |
280 | 14 | continue; |
281 | 14 | } |
282 | | // helper object = dummy in the hierarchy |
283 | 540 | if (TokenMatch(mFilePtr, "HELPEROBJECT", 12)) |
284 | | |
285 | 1 | { |
286 | 1 | m_vDummies.emplace_back(); |
287 | 1 | ParseLV1ObjectBlock(m_vDummies.back()); |
288 | 1 | continue; |
289 | 1 | } |
290 | | // light object |
291 | 539 | if (TokenMatch(mFilePtr, "LIGHTOBJECT", 11)) |
292 | | |
293 | 7 | { |
294 | 7 | m_vLights.emplace_back("UNNAMED"); |
295 | 7 | ParseLV1ObjectBlock(m_vLights.back()); |
296 | 7 | continue; |
297 | 7 | } |
298 | | // camera object |
299 | 532 | if (TokenMatch(mFilePtr, "CAMERAOBJECT", 12)) { |
300 | 9 | m_vCameras.emplace_back("UNNAMED"); |
301 | 9 | ParseLV1ObjectBlock(m_vCameras.back()); |
302 | 9 | continue; |
303 | 9 | } |
304 | | // comment - print it on the console |
305 | 523 | if (TokenMatch(mFilePtr, "COMMENT", 7)) { |
306 | 6 | std::string out = "<unknown>"; |
307 | 6 | ParseString(out, "*COMMENT"); |
308 | 6 | LogInfo(("Comment: " + out).c_str()); |
309 | 6 | continue; |
310 | 6 | } |
311 | | // ASC bone weights |
312 | 517 | if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(mFilePtr, "MESH_SOFTSKINVERTS", 18)) { |
313 | 1 | ParseLV1SoftSkinBlock(); |
314 | 1 | } |
315 | 517 | } |
316 | 28.3k | AI_ASE_HANDLE_TOP_LEVEL_SECTION(); |
317 | 28.3k | } |
318 | 24 | } |
319 | | |
320 | | // ------------------------------------------------------------------------------------------------ |
321 | 1 | void Parser::ParseLV1SoftSkinBlock() { |
322 | | // TODO: fix line counting here |
323 | | |
324 | | // ************************************************************** |
325 | | // The soft skin block is formatted differently. There are no |
326 | | // nested sections supported and the single elements aren't |
327 | | // marked by keywords starting with an asterisk. |
328 | | |
329 | | /** |
330 | | FORMAT BEGIN |
331 | | |
332 | | *MESH_SOFTSKINVERTS { |
333 | | <nodename> |
334 | | <number of vertices> |
335 | | |
336 | | [for <number of vertices> times:] |
337 | | <number of weights> [for <number of weights> times:] <bone name> <weight> |
338 | | } |
339 | | |
340 | | FORMAT END |
341 | | */ |
342 | | // ************************************************************** |
343 | 922 | while (true) { |
344 | 922 | if (*mFilePtr == '}') { |
345 | 0 | ++mFilePtr; |
346 | 0 | return; |
347 | 922 | } else if (*mFilePtr == '\0') |
348 | 0 | return; |
349 | 922 | else if (*mFilePtr == '{') |
350 | 0 | ++mFilePtr; |
351 | | |
352 | 922 | else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) |
353 | 922 | { |
354 | 922 | ASE::Mesh *curMesh = nullptr; |
355 | 922 | unsigned int numVerts = 0; |
356 | | |
357 | 922 | const char *sz = mFilePtr; |
358 | 10.7k | while (!IsSpaceOrNewLine(*mFilePtr)) { |
359 | 9.85k | ++mFilePtr; |
360 | 9.85k | } |
361 | | |
362 | 922 | const unsigned int diff = (unsigned int)(mFilePtr - sz); |
363 | 922 | if (diff) { |
364 | 910 | std::string name = std::string(sz, diff); |
365 | 910 | for (std::vector<ASE::Mesh>::iterator it = m_vMeshes.begin(); |
366 | 910 | it != m_vMeshes.end(); ++it) { |
367 | 0 | if ((*it).mName == name) { |
368 | 0 | curMesh = &(*it); |
369 | 0 | break; |
370 | 0 | } |
371 | 0 | } |
372 | 910 | if (!curMesh) { |
373 | 910 | LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); |
374 | | |
375 | | // Skip the mesh data - until we find a new mesh |
376 | | // or the end of the *MESH_SOFTSKINVERTS section |
377 | 1.04k | while (true) { |
378 | 1.04k | SkipSpacesAndLineEnd(&mFilePtr, mEnd); |
379 | 1.04k | if (*mFilePtr == '}') { |
380 | 0 | ++mFilePtr; |
381 | 0 | return; |
382 | 1.04k | } else if (!IsNumeric(*mFilePtr)) |
383 | 910 | break; |
384 | | |
385 | 139 | SkipLine(&mFilePtr, mEnd); |
386 | 139 | } |
387 | 910 | } else { |
388 | 0 | SkipSpacesAndLineEnd(&mFilePtr, mEnd); |
389 | 0 | ParseLV4MeshLong(numVerts); |
390 | | |
391 | | // Reserve enough storage |
392 | 0 | curMesh->mBoneVertices.reserve(numVerts); |
393 | |
|
394 | 0 | for (unsigned int i = 0; i < numVerts; ++i) { |
395 | 0 | SkipSpacesAndLineEnd(&mFilePtr, mEnd); |
396 | 0 | unsigned int numWeights; |
397 | 0 | ParseLV4MeshLong(numWeights); |
398 | |
|
399 | 0 | curMesh->mBoneVertices.emplace_back(); |
400 | 0 | ASE::BoneVertex &vert = curMesh->mBoneVertices.back(); |
401 | | |
402 | | // Reserve enough storage |
403 | 0 | vert.mBoneWeights.reserve(numWeights); |
404 | |
|
405 | 0 | std::string bone; |
406 | 0 | for (unsigned int w = 0; w < numWeights; ++w) { |
407 | 0 | bone.clear(); |
408 | 0 | ParseString(bone, "*MESH_SOFTSKINVERTS.Bone"); |
409 | | |
410 | | // Find the bone in the mesh's list |
411 | 0 | std::pair<int, ai_real> me; |
412 | 0 | me.first = -1; |
413 | |
|
414 | 0 | for (unsigned int n = 0; n < curMesh->mBones.size(); ++n) { |
415 | 0 | if (curMesh->mBones[n].mName == bone) { |
416 | 0 | me.first = n; |
417 | 0 | break; |
418 | 0 | } |
419 | 0 | } |
420 | 0 | if (-1 == me.first) { |
421 | | // We don't have this bone yet, so add it to the list |
422 | 0 | me.first = static_cast<int>(curMesh->mBones.size()); |
423 | 0 | curMesh->mBones.emplace_back(bone); |
424 | 0 | } |
425 | 0 | ParseLV4MeshReal(me.second); |
426 | | |
427 | | // Add the new bone weight to list |
428 | 0 | vert.mBoneWeights.push_back(me); |
429 | 0 | } |
430 | 0 | } |
431 | 0 | } |
432 | 910 | } |
433 | 922 | } |
434 | 922 | if (*mFilePtr == '\0') |
435 | 1 | return; |
436 | 921 | ++mFilePtr; |
437 | 921 | SkipSpacesAndLineEnd(&mFilePtr, mEnd); |
438 | 921 | } |
439 | 1 | } |
440 | | |
441 | | // ------------------------------------------------------------------------------------------------ |
442 | 6 | void Parser::ParseLV1SceneBlock() { |
443 | 6 | AI_ASE_PARSER_INIT(); |
444 | 327 | while (true) { |
445 | 327 | if ('*' == *mFilePtr) { |
446 | 42 | ++mFilePtr; |
447 | 42 | if (TokenMatch(mFilePtr, "SCENE_BACKGROUND_STATIC", 23)) |
448 | | |
449 | 6 | { |
450 | | // parse a color triple and assume it is really the bg color |
451 | 6 | ParseLV4MeshFloatTriple(&m_clrBackground.r); |
452 | 6 | continue; |
453 | 6 | } |
454 | 36 | if (TokenMatch(mFilePtr, "SCENE_AMBIENT_STATIC", 20)) |
455 | | |
456 | 6 | { |
457 | | // parse a color triple and assume it is really the bg color |
458 | 6 | ParseLV4MeshFloatTriple(&m_clrAmbient.r); |
459 | 6 | continue; |
460 | 6 | } |
461 | 30 | if (TokenMatch(mFilePtr, "SCENE_FIRSTFRAME", 16)) { |
462 | 6 | ParseLV4MeshLong(iFirstFrame); |
463 | 6 | continue; |
464 | 6 | } |
465 | 24 | if (TokenMatch(mFilePtr, "SCENE_LASTFRAME", 15)) { |
466 | 6 | ParseLV4MeshLong(iLastFrame); |
467 | 6 | continue; |
468 | 6 | } |
469 | 18 | if (TokenMatch(mFilePtr, "SCENE_FRAMESPEED", 16)) { |
470 | 6 | ParseLV4MeshLong(iFrameSpeed); |
471 | 6 | continue; |
472 | 6 | } |
473 | 12 | if (TokenMatch(mFilePtr, "SCENE_TICKSPERFRAME", 19)) { |
474 | 6 | ParseLV4MeshLong(iTicksPerFrame); |
475 | 6 | continue; |
476 | 6 | } |
477 | 12 | } |
478 | 570 | AI_ASE_HANDLE_TOP_LEVEL_SECTION(); |
479 | 570 | } |
480 | 6 | } |
481 | | |
482 | | // ------------------------------------------------------------------------------------------------ |
483 | 102 | void Parser::ParseLV1MaterialListBlock() { |
484 | 102 | AI_ASE_PARSER_INIT(); |
485 | | |
486 | 102 | unsigned int iMaterialCount = 0; |
487 | 102 | unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); |
488 | 51.6k | while (true) { |
489 | 51.6k | if ('*' == *mFilePtr) { |
490 | 2.78k | ++mFilePtr; |
491 | 2.78k | if (TokenMatch(mFilePtr, "MATERIAL_COUNT", 14)) { |
492 | 600 | ParseLV4MeshLong(iMaterialCount); |
493 | | |
494 | 600 | if (UINT_MAX - iOldMaterialCount < iMaterialCount) { |
495 | 0 | LogWarning("Out of range: material index is too large"); |
496 | 0 | return; |
497 | 0 | } |
498 | | |
499 | | // now allocate enough storage to hold all materials |
500 | 600 | m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); |
501 | 600 | continue; |
502 | 600 | } |
503 | 2.18k | if (TokenMatch(mFilePtr, "MATERIAL", 8)) { |
504 | | // ensure we have at least one material allocated |
505 | 0 | if (iMaterialCount == 0) { |
506 | 0 | LogWarning("*MATERIAL_COUNT unspecified or 0"); |
507 | 0 | iMaterialCount = 1; |
508 | 0 | m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); |
509 | 0 | } |
510 | |
|
511 | 0 | unsigned int iIndex = 0; |
512 | 0 | ParseLV4MeshLong(iIndex); |
513 | |
|
514 | 0 | if (iIndex >= iMaterialCount) { |
515 | 0 | LogWarning("Out of range: material index is too large"); |
516 | 0 | iIndex = iMaterialCount - 1; |
517 | 0 | } |
518 | | |
519 | | // get a reference to the material |
520 | 0 | Material &sMat = m_vMaterials[iIndex + iOldMaterialCount]; |
521 | | // parse the material block |
522 | 0 | ParseLV2MaterialBlock(sMat); |
523 | 0 | continue; |
524 | 0 | } |
525 | 2.18k | if( iDepth == 1 ){ |
526 | | // CRUDE HACK: support missing brace after "Ascii Scene Exporter v2.51" |
527 | 0 | LogWarning("Missing closing brace in material list"); |
528 | 0 | --mFilePtr; |
529 | 0 | return; |
530 | 0 | } |
531 | 2.18k | } |
532 | 101k | AI_ASE_HANDLE_TOP_LEVEL_SECTION(); |
533 | 101k | } |
534 | 102 | } |
535 | | |
536 | | // ------------------------------------------------------------------------------------------------ |
537 | 0 | void Parser::ParseLV2MaterialBlock(ASE::Material &mat) { |
538 | 0 | AI_ASE_PARSER_INIT(); |
539 | |
|
540 | 0 | unsigned int iNumSubMaterials = 0; |
541 | 0 | while (true) { |
542 | 0 | if ('*' == *mFilePtr) { |
543 | 0 | ++mFilePtr; |
544 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_NAME", 13)) { |
545 | 0 | if (!ParseString(mat.mName, "*MATERIAL_NAME")) |
546 | 0 | SkipToNextToken(); |
547 | 0 | continue; |
548 | 0 | } |
549 | | // ambient material color |
550 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_AMBIENT", 16)) { |
551 | 0 | ParseLV4MeshFloatTriple(&mat.mAmbient.r); |
552 | 0 | continue; |
553 | 0 | } |
554 | | // diffuse material color |
555 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_DIFFUSE", 16)) { |
556 | 0 | ParseLV4MeshFloatTriple(&mat.mDiffuse.r); |
557 | 0 | continue; |
558 | 0 | } |
559 | | // specular material color |
560 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_SPECULAR", 17)) { |
561 | 0 | ParseLV4MeshFloatTriple(&mat.mSpecular.r); |
562 | 0 | continue; |
563 | 0 | } |
564 | | // material shading type |
565 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_SHADING", 16)) { |
566 | 0 | if (TokenMatch(mFilePtr, "Blinn", 5)) { |
567 | 0 | mat.mShading = Discreet3DS::Blinn; |
568 | 0 | } else if (TokenMatch(mFilePtr, "Phong", 5)) { |
569 | 0 | mat.mShading = Discreet3DS::Phong; |
570 | 0 | } else if (TokenMatch(mFilePtr, "Flat", 4)) { |
571 | 0 | mat.mShading = Discreet3DS::Flat; |
572 | 0 | } else if (TokenMatch(mFilePtr, "Wire", 4)) { |
573 | 0 | mat.mShading = Discreet3DS::Wire; |
574 | 0 | } else { |
575 | | // assume gouraud shading |
576 | 0 | mat.mShading = Discreet3DS::Gouraud; |
577 | 0 | SkipToNextToken(); |
578 | 0 | } |
579 | 0 | continue; |
580 | 0 | } |
581 | | // material transparency |
582 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_TRANSPARENCY", 21)) { |
583 | 0 | ParseLV4MeshReal(mat.mTransparency); |
584 | 0 | mat.mTransparency = ai_real(1.0) - mat.mTransparency; |
585 | 0 | continue; |
586 | 0 | } |
587 | | // material self illumination |
588 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_SELFILLUM", 18)) { |
589 | 0 | ai_real f = 0.0; |
590 | 0 | ParseLV4MeshReal(f); |
591 | |
|
592 | 0 | mat.mEmissive.r = f; |
593 | 0 | mat.mEmissive.g = f; |
594 | 0 | mat.mEmissive.b = f; |
595 | 0 | continue; |
596 | 0 | } |
597 | | // material shininess |
598 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_SHINE", 14)) { |
599 | 0 | ParseLV4MeshReal(mat.mSpecularExponent); |
600 | 0 | mat.mSpecularExponent *= 15; |
601 | 0 | continue; |
602 | 0 | } |
603 | | // two-sided material |
604 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_TWOSIDED", 17)) { |
605 | 0 | mat.mTwoSided = true; |
606 | 0 | continue; |
607 | 0 | } |
608 | | // material shininess strength |
609 | 0 | if (TokenMatch(mFilePtr, "MATERIAL_SHINESTRENGTH", 22)) { |
610 | 0 | ParseLV4MeshReal(mat.mShininessStrength); |
611 | 0 | continue; |
612 | 0 | } |
613 | | // diffuse color map |
614 | 0 | if (TokenMatch(mFilePtr, "MAP_DIFFUSE", 11)) { |
615 | | // parse the texture block |
616 | 0 | ParseLV3MapBlock(mat.sTexDiffuse); |
617 | 0 | continue; |
618 | 0 | } |
619 | | // ambient color map |
620 | 0 | if (TokenMatch(mFilePtr, "MAP_AMBIENT", 11)) { |
621 | | // parse the texture block |
622 | 0 | ParseLV3MapBlock(mat.sTexAmbient); |
623 | 0 | continue; |
624 | 0 | } |
625 | | // specular color map |
626 | 0 | if (TokenMatch(mFilePtr, "MAP_SPECULAR", 12)) { |
627 | | // parse the texture block |
628 | 0 | ParseLV3MapBlock(mat.sTexSpecular); |
629 | 0 | continue; |
630 | 0 | } |
631 | | // opacity map |
632 | 0 | if (TokenMatch(mFilePtr, "MAP_OPACITY", 11)) { |
633 | | // parse the texture block |
634 | 0 | ParseLV3MapBlock(mat.sTexOpacity); |
635 | 0 | continue; |
636 | 0 | } |
637 | | // emissive map |
638 | 0 | if (TokenMatch(mFilePtr, "MAP_SELFILLUM", 13)) { |
639 | | // parse the texture block |
640 | 0 | ParseLV3MapBlock(mat.sTexEmissive); |
641 | 0 | continue; |
642 | 0 | } |
643 | | // bump map |
644 | 0 | if (TokenMatch(mFilePtr, "MAP_BUMP", 8)) { |
645 | | // parse the texture block |
646 | 0 | ParseLV3MapBlock(mat.sTexBump); |
647 | 0 | } |
648 | | // specular/shininess map |
649 | 0 | if (TokenMatch(mFilePtr, "MAP_SHINESTRENGTH", 17)) { |
650 | | // parse the texture block |
651 | 0 | ParseLV3MapBlock(mat.sTexShininess); |
652 | 0 | continue; |
653 | 0 | } |
654 | | // number of submaterials |
655 | 0 | if (TokenMatch(mFilePtr, "NUMSUBMTLS", 10)) { |
656 | 0 | ParseLV4MeshLong(iNumSubMaterials); |
657 | | |
658 | | // allocate enough storage |
659 | 0 | mat.avSubMaterials.resize(iNumSubMaterials, Material("INVALID SUBMATERIAL")); |
660 | 0 | } |
661 | | // submaterial chunks |
662 | 0 | if (TokenMatch(mFilePtr, "SUBMATERIAL", 11)) { |
663 | | // ensure we have at least one material allocated |
664 | 0 | if (iNumSubMaterials == 0) { |
665 | 0 | LogWarning("*NUMSUBMTLS unspecified or 0"); |
666 | 0 | iNumSubMaterials = 1; |
667 | 0 | mat.avSubMaterials.resize(iNumSubMaterials, Material("INVALID SUBMATERIAL")); |
668 | 0 | } |
669 | |
|
670 | 0 | unsigned int iIndex = 0; |
671 | 0 | ParseLV4MeshLong(iIndex); |
672 | |
|
673 | 0 | if (iIndex >= iNumSubMaterials) { |
674 | 0 | LogWarning("Out of range: submaterial index is too large"); |
675 | 0 | iIndex = iNumSubMaterials - 1; |
676 | 0 | } |
677 | | |
678 | | // get a reference to the material |
679 | 0 | if (iIndex < mat.avSubMaterials.size()) { |
680 | 0 | Material &sMat = mat.avSubMaterials[iIndex]; |
681 | | |
682 | | // parse the material block |
683 | 0 | ParseLV2MaterialBlock(sMat); |
684 | 0 | } |
685 | |
|
686 | 0 | continue; |
687 | 0 | } |
688 | 0 | } |
689 | 0 | AI_ASE_HANDLE_SECTION("2", "*MATERIAL"); |
690 | 0 | } |
691 | 0 | } |
692 | | |
693 | | // ------------------------------------------------------------------------------------------------ |
694 | 0 | void Parser::ParseLV3MapBlock(Texture &map) { |
695 | 0 | AI_ASE_PARSER_INIT(); |
696 | | |
697 | | // *********************************************************** |
698 | | // *BITMAP should not be there if *MAP_CLASS is not BITMAP, |
699 | | // but we need to expect that case ... if the path is |
700 | | // empty the texture won't be used later. |
701 | | // *********************************************************** |
702 | 0 | bool parsePath = true; |
703 | 0 | std::string temp; |
704 | 0 | while (true) { |
705 | 0 | if ('*' == *mFilePtr) { |
706 | 0 | ++mFilePtr; |
707 | | // type of map |
708 | 0 | if (TokenMatch(mFilePtr, "MAP_CLASS", 9)) { |
709 | 0 | temp.clear(); |
710 | 0 | if (!ParseString(temp, "*MAP_CLASS")) |
711 | 0 | SkipToNextToken(); |
712 | 0 | if (temp != "Bitmap" && temp != "Normal Bump") { |
713 | 0 | ASSIMP_LOG_WARN("ASE: Skipping unknown map type: ", temp); |
714 | 0 | parsePath = false; |
715 | 0 | } |
716 | 0 | continue; |
717 | 0 | } |
718 | | // path to the texture |
719 | 0 | if (parsePath && TokenMatch(mFilePtr, "BITMAP", 6)) { |
720 | 0 | if (!ParseString(map.mMapName, "*BITMAP")) |
721 | 0 | SkipToNextToken(); |
722 | |
|
723 | 0 | if (map.mMapName == "None") { |
724 | | // Files with 'None' as map name are produced by |
725 | | // an Maja to ASE exporter which name I forgot .. |
726 | 0 | ASSIMP_LOG_WARN("ASE: Skipping invalid map entry"); |
727 | 0 | map.mMapName = std::string(); |
728 | 0 | } |
729 | |
|
730 | 0 | continue; |
731 | 0 | } |
732 | | // offset on the u axis |
733 | 0 | if (TokenMatch(mFilePtr, "UVW_U_OFFSET", 12)) { |
734 | 0 | ParseLV4MeshReal(map.mOffsetU); |
735 | 0 | continue; |
736 | 0 | } |
737 | | // offset on the v axis |
738 | 0 | if (TokenMatch(mFilePtr, "UVW_V_OFFSET", 12)) { |
739 | 0 | ParseLV4MeshReal(map.mOffsetV); |
740 | 0 | continue; |
741 | 0 | } |
742 | | // tiling on the u axis |
743 | 0 | if (TokenMatch(mFilePtr, "UVW_U_TILING", 12)) { |
744 | 0 | ParseLV4MeshReal(map.mScaleU); |
745 | 0 | continue; |
746 | 0 | } |
747 | | // tiling on the v axis |
748 | 0 | if (TokenMatch(mFilePtr, "UVW_V_TILING", 12)) { |
749 | 0 | ParseLV4MeshReal(map.mScaleV); |
750 | 0 | continue; |
751 | 0 | } |
752 | | // rotation around the z-axis |
753 | 0 | if (TokenMatch(mFilePtr, "UVW_ANGLE", 9)) { |
754 | 0 | ParseLV4MeshReal(map.mRotation); |
755 | 0 | continue; |
756 | 0 | } |
757 | | // map blending factor |
758 | 0 | if (TokenMatch(mFilePtr, "MAP_AMOUNT", 10)) { |
759 | 0 | ParseLV4MeshReal(map.mTextureBlend); |
760 | 0 | continue; |
761 | 0 | } |
762 | 0 | } |
763 | 0 | AI_ASE_HANDLE_SECTION("3", "*MAP_XXXXXX"); |
764 | 0 | } |
765 | 0 | } |
766 | | |
767 | | // ------------------------------------------------------------------------------------------------ |
768 | 254 | bool Parser::ParseString(std::string &out, const char *szName) { |
769 | 254 | char szBuffer[1024]; |
770 | 254 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
771 | |
|
772 | 0 | ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Unexpected EOL", szName); |
773 | 0 | LogWarning(szBuffer); |
774 | 0 | return false; |
775 | 0 | } |
776 | | // there must be '"' |
777 | 254 | if ('\"' != *mFilePtr) { |
778 | | |
779 | 7 | ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Strings are expected " |
780 | 7 | "to be enclosed in double quotation marks", |
781 | 7 | szName); |
782 | 7 | LogWarning(szBuffer); |
783 | 7 | return false; |
784 | 7 | } |
785 | 247 | ++mFilePtr; |
786 | 247 | const char *sz = mFilePtr; |
787 | 11.9k | while (true) { |
788 | 11.9k | if ('\"' == *sz) |
789 | 247 | break; |
790 | 11.7k | else if ('\0' == *sz) { |
791 | 0 | ai_snprintf(szBuffer, 1024, "Unable to parse %s block: Strings are expected to " |
792 | 0 | "be enclosed in double quotation marks but EOF was reached before " |
793 | 0 | "a closing quotation mark was encountered", |
794 | 0 | szName); |
795 | 0 | LogWarning(szBuffer); |
796 | 0 | return false; |
797 | 0 | } |
798 | 11.7k | sz++; |
799 | 11.7k | } |
800 | 247 | out = std::string(mFilePtr, (uintptr_t)sz - (uintptr_t)mFilePtr); |
801 | 247 | mFilePtr = sz + 1; |
802 | 247 | return true; |
803 | 247 | } |
804 | | |
805 | | // ------------------------------------------------------------------------------------------------ |
806 | 31 | void Parser::ParseLV1ObjectBlock(ASE::BaseNode &node) { |
807 | 31 | AI_ASE_PARSER_INIT(); |
808 | 9.49k | while (true) { |
809 | 9.48k | if ('*' == *mFilePtr) { |
810 | 423 | ++mFilePtr; |
811 | | |
812 | | // first process common tokens such as node name and transform |
813 | | // name of the mesh/node |
814 | 423 | if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { |
815 | 32 | if (!ParseString(node.mName, "*NODE_NAME")) |
816 | 5 | SkipToNextToken(); |
817 | 32 | continue; |
818 | 32 | } |
819 | | // name of the parent of the node |
820 | 391 | if (TokenMatch(mFilePtr, "NODE_PARENT", 11)) { |
821 | 1 | if (!ParseString(node.mParent, "*NODE_PARENT")) |
822 | 0 | SkipToNextToken(); |
823 | 1 | continue; |
824 | 1 | } |
825 | | // transformation matrix of the node |
826 | 390 | if (TokenMatch(mFilePtr, "NODE_TM", 7)) { |
827 | 22 | ParseLV2NodeTransformBlock(node); |
828 | 22 | continue; |
829 | 22 | } |
830 | | // animation data of the node |
831 | 368 | if (TokenMatch(mFilePtr, "TM_ANIMATION", 12)) { |
832 | 18 | ParseLV2AnimationBlock(node); |
833 | 18 | continue; |
834 | 18 | } |
835 | | |
836 | 350 | if (node.mType == BaseNode::Light) { |
837 | | // light settings |
838 | 138 | if (TokenMatch(mFilePtr, "LIGHT_SETTINGS", 14)) { |
839 | 3 | ParseLV2LightSettingsBlock((ASE::Light &)node); |
840 | 3 | continue; |
841 | 3 | } |
842 | | // type of the light source |
843 | 135 | if (TokenMatch(mFilePtr, "LIGHT_TYPE", 10)) { |
844 | 3 | if (!ASSIMP_strincmp("omni", mFilePtr, 4)) { |
845 | 0 | ((ASE::Light &)node).mLightType = ASE::Light::OMNI; |
846 | 3 | } else if (!ASSIMP_strincmp("target", mFilePtr, 6)) { |
847 | 0 | ((ASE::Light &)node).mLightType = ASE::Light::TARGET; |
848 | 3 | } else if (!ASSIMP_strincmp("free", mFilePtr, 4)) { |
849 | 1 | ((ASE::Light &)node).mLightType = ASE::Light::FREE; |
850 | 2 | } else if (!ASSIMP_strincmp("directional", mFilePtr, 11)) { |
851 | 2 | ((ASE::Light &)node).mLightType = ASE::Light::DIRECTIONAL; |
852 | 2 | } else { |
853 | 0 | LogWarning("Unknown kind of light source"); |
854 | 0 | } |
855 | 3 | continue; |
856 | 3 | } |
857 | 212 | } else if (node.mType == BaseNode::Camera) { |
858 | | // Camera settings |
859 | 159 | if (TokenMatch(mFilePtr, "CAMERA_SETTINGS", 15)) { |
860 | 20 | ParseLV2CameraSettingsBlock((ASE::Camera &)node); |
861 | 20 | continue; |
862 | 139 | } else if (TokenMatch(mFilePtr, "CAMERA_TYPE", 11)) { |
863 | 4 | if (!ASSIMP_strincmp("target", mFilePtr, 6)) { |
864 | 4 | ((ASE::Camera &)node).mCameraType = ASE::Camera::TARGET; |
865 | 4 | } else if (!ASSIMP_strincmp("free", mFilePtr, 4)) { |
866 | 0 | ((ASE::Camera &)node).mCameraType = ASE::Camera::FREE; |
867 | 0 | } else { |
868 | 0 | LogWarning("Unknown kind of camera"); |
869 | 0 | } |
870 | 4 | continue; |
871 | 4 | } |
872 | 159 | } else if (node.mType == BaseNode::Mesh) { |
873 | | // mesh data |
874 | | // FIX: Older files use MESH_SOFTSKIN |
875 | 50 | if (TokenMatch(mFilePtr, "MESH", 4) || |
876 | 38 | TokenMatch(mFilePtr, "MESH_SOFTSKIN", 13)) { |
877 | 13 | ParseLV2MeshBlock((ASE::Mesh &)node); |
878 | 13 | continue; |
879 | 13 | } |
880 | | // mesh material index |
881 | 37 | if (TokenMatch(mFilePtr, "MATERIAL_REF", 12)) { |
882 | 0 | ParseLV4MeshLong(((ASE::Mesh &)node).iMaterialIndex); |
883 | 0 | continue; |
884 | 0 | } |
885 | 37 | } |
886 | 350 | } |
887 | 18.7k | AI_ASE_HANDLE_TOP_LEVEL_SECTION(); |
888 | 18.7k | } |
889 | 31 | } |
890 | | |
891 | | // ------------------------------------------------------------------------------------------------ |
892 | 20 | void Parser::ParseLV2CameraSettingsBlock(ASE::Camera &camera) { |
893 | 20 | AI_ASE_PARSER_INIT(); |
894 | 33.5k | while (true) { |
895 | 33.5k | if ('*' == *mFilePtr) { |
896 | 740 | ++mFilePtr; |
897 | 740 | if (TokenMatch(mFilePtr, "CAMERA_NEAR", 11)) { |
898 | 4 | ParseLV4MeshReal(camera.mNear); |
899 | 4 | continue; |
900 | 4 | } |
901 | 736 | if (TokenMatch(mFilePtr, "CAMERA_FAR", 10)) { |
902 | 4 | ParseLV4MeshReal(camera.mFar); |
903 | 4 | continue; |
904 | 4 | } |
905 | 732 | if (TokenMatch(mFilePtr, "CAMERA_FOV", 10)) { |
906 | 4 | ParseLV4MeshReal(camera.mFOV); |
907 | 4 | continue; |
908 | 4 | } |
909 | 732 | } |
910 | 67.1k | AI_ASE_HANDLE_SECTION("2", "CAMERA_SETTINGS"); |
911 | 67.1k | } |
912 | 20 | } |
913 | | |
914 | | // ------------------------------------------------------------------------------------------------ |
915 | 3 | void Parser::ParseLV2LightSettingsBlock(ASE::Light &light) { |
916 | 3 | AI_ASE_PARSER_INIT(); |
917 | 503 | while (true) { |
918 | 503 | if ('*' == *mFilePtr) { |
919 | 33 | ++mFilePtr; |
920 | 33 | if (TokenMatch(mFilePtr, "LIGHT_COLOR", 11)) { |
921 | 3 | ParseLV4MeshFloatTriple(&light.mColor.r); |
922 | 3 | continue; |
923 | 3 | } |
924 | 30 | if (TokenMatch(mFilePtr, "LIGHT_INTENS", 12)) { |
925 | 3 | ParseLV4MeshReal(light.mIntensity); |
926 | 3 | continue; |
927 | 3 | } |
928 | 27 | if (TokenMatch(mFilePtr, "LIGHT_HOTSPOT", 13)) { |
929 | 3 | ParseLV4MeshReal(light.mAngle); |
930 | 3 | continue; |
931 | 3 | } |
932 | 24 | if (TokenMatch(mFilePtr, "LIGHT_FALLOFF", 13)) { |
933 | 3 | ParseLV4MeshReal(light.mFalloff); |
934 | 3 | continue; |
935 | 3 | } |
936 | 24 | } |
937 | 979 | AI_ASE_HANDLE_SECTION("2", "LIGHT_SETTINGS"); |
938 | 979 | } |
939 | 3 | } |
940 | | |
941 | | // ------------------------------------------------------------------------------------------------ |
942 | 18 | void Parser::ParseLV2AnimationBlock(ASE::BaseNode &mesh) { |
943 | 18 | AI_ASE_PARSER_INIT(); |
944 | | |
945 | 18 | ASE::Animation *anim = &mesh.mAnim; |
946 | 20.6k | while (true) { |
947 | 20.6k | if ('*' == *mFilePtr) { |
948 | 748 | ++mFilePtr; |
949 | 748 | if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { |
950 | 11 | std::string temp; |
951 | 11 | if (!ParseString(temp, "*NODE_NAME")) |
952 | 0 | SkipToNextToken(); |
953 | | |
954 | | // If the name of the node contains .target it |
955 | | // represents an animated camera or spot light |
956 | | // target. |
957 | 11 | if (std::string::npos != temp.find(".Target")) { |
958 | 3 | if ((mesh.mType != BaseNode::Camera || ((ASE::Camera &)mesh).mCameraType != ASE::Camera::TARGET) && |
959 | 0 | (mesh.mType != BaseNode::Light || ((ASE::Light &)mesh).mLightType != ASE::Light::TARGET)) { |
960 | |
|
961 | 0 | ASSIMP_LOG_ERROR("ASE: Found target animation channel " |
962 | 0 | "but the node is neither a camera nor a spot light"); |
963 | 0 | anim = nullptr; |
964 | 0 | } else |
965 | 3 | anim = &mesh.mTargetAnim; |
966 | 3 | } |
967 | 11 | continue; |
968 | 11 | } |
969 | | |
970 | | // position keyframes |
971 | 737 | if (TokenMatch(mFilePtr, "CONTROL_POS_TRACK", 17) || |
972 | 729 | TokenMatch(mFilePtr, "CONTROL_POS_BEZIER", 18) || |
973 | 729 | TokenMatch(mFilePtr, "CONTROL_POS_TCB", 15)) { |
974 | 8 | if (!anim) |
975 | 0 | SkipSection(); |
976 | 8 | else |
977 | 8 | ParseLV3PosAnimationBlock(*anim); |
978 | 8 | continue; |
979 | 8 | } |
980 | | // scaling keyframes |
981 | 729 | if (TokenMatch(mFilePtr, "CONTROL_SCALE_TRACK", 19) || |
982 | 728 | TokenMatch(mFilePtr, "CONTROL_SCALE_BEZIER", 20) || |
983 | 728 | TokenMatch(mFilePtr, "CONTROL_SCALE_TCB", 17)) { |
984 | 207 | if (!anim || anim == &mesh.mTargetAnim) { |
985 | | // Target animation channels may have no rotation channels |
986 | 0 | ASSIMP_LOG_ERROR("ASE: Ignoring scaling channel in target animation"); |
987 | 0 | SkipSection(); |
988 | 0 | } else |
989 | 207 | ParseLV3ScaleAnimationBlock(*anim); |
990 | 207 | continue; |
991 | 207 | } |
992 | | // rotation keyframes |
993 | 522 | if (TokenMatch(mFilePtr, "CONTROL_ROT_TRACK", 17) || |
994 | 442 | TokenMatch(mFilePtr, "CONTROL_ROT_BEZIER", 18) || |
995 | 437 | TokenMatch(mFilePtr, "CONTROL_ROT_TCB", 15)) { |
996 | 85 | if (!anim || anim == &mesh.mTargetAnim) { |
997 | | // Target animation channels may have no rotation channels |
998 | 0 | ASSIMP_LOG_ERROR("ASE: Ignoring rotation channel in target animation"); |
999 | 0 | SkipSection(); |
1000 | 0 | } else |
1001 | 85 | ParseLV3RotAnimationBlock(*anim); |
1002 | 85 | continue; |
1003 | 85 | } |
1004 | 522 | } |
1005 | 40.7k | AI_ASE_HANDLE_SECTION("2", "TM_ANIMATION"); |
1006 | 40.7k | } |
1007 | 18 | } |
1008 | | // ------------------------------------------------------------------------------------------------ |
1009 | 207 | void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation &anim) { |
1010 | 207 | AI_ASE_PARSER_INIT(); |
1011 | 207 | unsigned int iIndex; |
1012 | | |
1013 | 3.04k | while (true) { |
1014 | 3.04k | if ('*' == *mFilePtr) { |
1015 | 45 | ++mFilePtr; |
1016 | | |
1017 | 45 | bool b = false; |
1018 | | |
1019 | | // For the moment we're just reading the three floats - |
1020 | | // we ignore the additional information for bezier's and TCBs |
1021 | | |
1022 | | // simple scaling keyframe |
1023 | 45 | if (TokenMatch(mFilePtr, "CONTROL_SCALE_SAMPLE", 20)) { |
1024 | 45 | b = true; |
1025 | 45 | anim.mScalingType = ASE::Animation::TRACK; |
1026 | 45 | } |
1027 | | |
1028 | | // Bezier scaling keyframe |
1029 | 45 | if (TokenMatch(mFilePtr, "CONTROL_BEZIER_SCALE_KEY", 24)) { |
1030 | 0 | b = true; |
1031 | 0 | anim.mScalingType = ASE::Animation::BEZIER; |
1032 | 0 | } |
1033 | | // TCB scaling keyframe |
1034 | 45 | if (TokenMatch(mFilePtr, "CONTROL_TCB_SCALE_KEY", 21)) { |
1035 | 0 | b = true; |
1036 | 0 | anim.mScalingType = ASE::Animation::TCB; |
1037 | 0 | } |
1038 | 45 | if (b) { |
1039 | 45 | anim.akeyScaling.emplace_back(); |
1040 | 45 | aiVectorKey &key = anim.akeyScaling.back(); |
1041 | 45 | ParseLV4MeshRealTriple(&key.mValue.x, iIndex); |
1042 | 45 | key.mTime = (double)iIndex; |
1043 | 45 | } |
1044 | 45 | } |
1045 | 3.04k | AI_ASE_HANDLE_SECTION("3", "*CONTROL_POS_TRACK"); |
1046 | 2.83k | } |
1047 | 207 | } |
1048 | | // ------------------------------------------------------------------------------------------------ |
1049 | 8 | void Parser::ParseLV3PosAnimationBlock(ASE::Animation &anim) { |
1050 | 8 | AI_ASE_PARSER_INIT(); |
1051 | 8 | unsigned int iIndex; |
1052 | 2.22k | while (true) { |
1053 | 2.22k | if ('*' == *mFilePtr) { |
1054 | 546 | ++mFilePtr; |
1055 | | |
1056 | 546 | bool b = false; |
1057 | | |
1058 | | // For the moment we're just reading the three floats - |
1059 | | // we ignore the additional information for bezier's and TCBs |
1060 | | |
1061 | | // simple scaling keyframe |
1062 | 546 | if (TokenMatch(mFilePtr, "CONTROL_POS_SAMPLE", 18)) { |
1063 | 546 | b = true; |
1064 | 546 | anim.mPositionType = ASE::Animation::TRACK; |
1065 | 546 | } |
1066 | | |
1067 | | // Bezier scaling keyframe |
1068 | 546 | if (TokenMatch(mFilePtr, "CONTROL_BEZIER_POS_KEY", 22)) { |
1069 | 0 | b = true; |
1070 | 0 | anim.mPositionType = ASE::Animation::BEZIER; |
1071 | 0 | } |
1072 | | // TCB scaling keyframe |
1073 | 546 | if (TokenMatch(mFilePtr, "CONTROL_TCB_POS_KEY", 19)) { |
1074 | 0 | b = true; |
1075 | 0 | anim.mPositionType = ASE::Animation::TCB; |
1076 | 0 | } |
1077 | 546 | if (b) { |
1078 | 546 | anim.akeyPositions.emplace_back(); |
1079 | 546 | aiVectorKey &key = anim.akeyPositions.back(); |
1080 | 546 | ParseLV4MeshRealTriple(&key.mValue.x, iIndex); |
1081 | 546 | key.mTime = (double)iIndex; |
1082 | 546 | } |
1083 | 546 | } |
1084 | 2.22k | AI_ASE_HANDLE_SECTION("3", "*CONTROL_POS_TRACK"); |
1085 | 2.21k | } |
1086 | 8 | } |
1087 | | // ------------------------------------------------------------------------------------------------ |
1088 | 85 | void Parser::ParseLV3RotAnimationBlock(ASE::Animation &anim) { |
1089 | 85 | AI_ASE_PARSER_INIT(); |
1090 | 85 | unsigned int iIndex; |
1091 | 60.7k | while (true) { |
1092 | 60.7k | if ('*' == *mFilePtr) { |
1093 | 2.79k | ++mFilePtr; |
1094 | | |
1095 | 2.79k | bool b = false; |
1096 | | |
1097 | | // For the moment we're just reading the floats - |
1098 | | // we ignore the additional information for bezier's and TCBs |
1099 | | |
1100 | | // simple scaling keyframe |
1101 | 2.79k | if (TokenMatch(mFilePtr, "CONTROL_ROT_SAMPLE", 18)) { |
1102 | 1.81k | b = true; |
1103 | 1.81k | anim.mRotationType = ASE::Animation::TRACK; |
1104 | 1.81k | } |
1105 | | |
1106 | | // Bezier scaling keyframe |
1107 | 2.79k | if (TokenMatch(mFilePtr, "CONTROL_BEZIER_ROT_KEY", 22)) { |
1108 | 26 | b = true; |
1109 | 26 | anim.mRotationType = ASE::Animation::BEZIER; |
1110 | 26 | } |
1111 | | // TCB scaling keyframe |
1112 | 2.79k | if (TokenMatch(mFilePtr, "CONTROL_TCB_ROT_KEY", 19)) { |
1113 | 0 | b = true; |
1114 | 0 | anim.mRotationType = ASE::Animation::TCB; |
1115 | 0 | } |
1116 | 2.79k | if (b) { |
1117 | 1.84k | anim.akeyRotations.emplace_back(); |
1118 | 1.84k | aiQuatKey &key = anim.akeyRotations.back(); |
1119 | 1.84k | aiVector3D v; |
1120 | 1.84k | ai_real f; |
1121 | 1.84k | ParseLV4MeshRealTriple(&v.x, iIndex); |
1122 | 1.84k | ParseLV4MeshReal(f); |
1123 | 1.84k | key.mTime = (double)iIndex; |
1124 | 1.84k | key.mValue = aiQuaternion(v, f); |
1125 | 1.84k | } |
1126 | 2.79k | } |
1127 | 60.7k | AI_ASE_HANDLE_SECTION("3", "*CONTROL_ROT_TRACK"); |
1128 | 60.6k | } |
1129 | 85 | } |
1130 | | // ------------------------------------------------------------------------------------------------ |
1131 | 22 | void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { |
1132 | 22 | AI_ASE_PARSER_INIT(); |
1133 | 22 | int mode = 0; |
1134 | 89.5k | while (true) { |
1135 | 89.5k | if ('*' == *mFilePtr) { |
1136 | 5.73k | ++mFilePtr; |
1137 | | // name of the node |
1138 | 5.73k | if (TokenMatch(mFilePtr, "NODE_NAME", 9)) { |
1139 | 204 | std::string temp; |
1140 | 204 | if (!ParseString(temp, "*NODE_NAME")) |
1141 | 2 | SkipToNextToken(); |
1142 | | |
1143 | 204 | std::string::size_type s; |
1144 | 204 | if (temp == mesh.mName) { |
1145 | 16 | mode = 1; |
1146 | 188 | } else if (std::string::npos != (s = temp.find(".Target")) && |
1147 | 185 | mesh.mName == temp.substr(0, s)) { |
1148 | | // This should be either a target light or a target camera |
1149 | 4 | if ((mesh.mType == BaseNode::Light && ((ASE::Light &)mesh).mLightType == ASE::Light::TARGET) || |
1150 | 4 | (mesh.mType == BaseNode::Camera && ((ASE::Camera &)mesh).mCameraType == ASE::Camera::TARGET)) { |
1151 | 4 | mode = 2; |
1152 | 4 | } else { |
1153 | 0 | ASSIMP_LOG_ERROR("ASE: Ignoring target transform, " |
1154 | 0 | "this is no spot light or target camera"); |
1155 | 0 | } |
1156 | 184 | } else { |
1157 | 184 | ASSIMP_LOG_ERROR("ASE: Unknown node transformation: ", temp); |
1158 | | // mode = 0 |
1159 | 184 | } |
1160 | 204 | continue; |
1161 | 204 | } |
1162 | 5.52k | if (mode) { |
1163 | | // fourth row of the transformation matrix - and also the |
1164 | | // only information here that is interesting for targets |
1165 | 260 | if (TokenMatch(mFilePtr, "TM_ROW3", 7)) { |
1166 | 20 | ParseLV4MeshRealTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); |
1167 | 20 | continue; |
1168 | 20 | } |
1169 | 240 | if (mode == 1) { |
1170 | | // first row of the transformation matrix |
1171 | 192 | if (TokenMatch(mFilePtr, "TM_ROW0", 7)) { |
1172 | 16 | ParseLV4MeshRealTriple(mesh.mTransform[0]); |
1173 | 16 | continue; |
1174 | 16 | } |
1175 | | // second row of the transformation matrix |
1176 | 176 | if (TokenMatch(mFilePtr, "TM_ROW1", 7)) { |
1177 | 16 | ParseLV4MeshRealTriple(mesh.mTransform[1]); |
1178 | 16 | continue; |
1179 | 16 | } |
1180 | | // third row of the transformation matrix |
1181 | 160 | if (TokenMatch(mFilePtr, "TM_ROW2", 7)) { |
1182 | 16 | ParseLV4MeshRealTriple(mesh.mTransform[2]); |
1183 | 16 | continue; |
1184 | 16 | } |
1185 | | // inherited position axes |
1186 | 144 | if (TokenMatch(mFilePtr, "INHERIT_POS", 11)) { |
1187 | 16 | unsigned int aiVal[3]; |
1188 | 16 | ParseLV4MeshLongTriple(aiVal); |
1189 | | |
1190 | 64 | for (unsigned int i = 0; i < 3; ++i) |
1191 | 48 | mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; |
1192 | 16 | continue; |
1193 | 16 | } |
1194 | | // inherited rotation axes |
1195 | 128 | if (TokenMatch(mFilePtr, "INHERIT_ROT", 11)) { |
1196 | 16 | unsigned int aiVal[3]; |
1197 | 16 | ParseLV4MeshLongTriple(aiVal); |
1198 | | |
1199 | 64 | for (unsigned int i = 0; i < 3; ++i) |
1200 | 48 | mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; |
1201 | 16 | continue; |
1202 | 16 | } |
1203 | | // inherited scaling axes |
1204 | 112 | if (TokenMatch(mFilePtr, "INHERIT_SCL", 11)) { |
1205 | 16 | unsigned int aiVal[3]; |
1206 | 16 | ParseLV4MeshLongTriple(aiVal); |
1207 | | |
1208 | 64 | for (unsigned int i = 0; i < 3; ++i) |
1209 | 48 | mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; |
1210 | 16 | continue; |
1211 | 16 | } |
1212 | 112 | } |
1213 | 240 | } |
1214 | 5.52k | } |
1215 | 178k | AI_ASE_HANDLE_SECTION("2", "*NODE_TM"); |
1216 | 178k | } |
1217 | 22 | } |
1218 | | // ------------------------------------------------------------------------------------------------ |
1219 | 13 | void Parser::ParseLV2MeshBlock(ASE::Mesh &mesh) { |
1220 | 13 | AI_ASE_PARSER_INIT(); |
1221 | | |
1222 | 13 | unsigned int iNumVertices = 0; |
1223 | 13 | unsigned int iNumFaces = 0; |
1224 | 13 | unsigned int iNumTVertices = 0; |
1225 | 13 | unsigned int iNumTFaces = 0; |
1226 | 13 | unsigned int iNumCVertices = 0; |
1227 | 13 | unsigned int iNumCFaces = 0; |
1228 | 31.9k | while (true) { |
1229 | 31.9k | if ('*' == *mFilePtr) { |
1230 | 1.50k | ++mFilePtr; |
1231 | | // Number of vertices in the mesh |
1232 | 1.50k | if (TokenMatch(mFilePtr, "MESH_NUMVERTEX", 14)) { |
1233 | 9 | ParseLV4MeshLong(iNumVertices); |
1234 | 9 | continue; |
1235 | 9 | } |
1236 | | // Number of texture coordinates in the mesh |
1237 | 1.49k | if (TokenMatch(mFilePtr, "MESH_NUMTVERTEX", 15)) { |
1238 | 9 | ParseLV4MeshLong(iNumTVertices); |
1239 | 9 | continue; |
1240 | 9 | } |
1241 | | // Number of vertex colors in the mesh |
1242 | 1.49k | if (TokenMatch(mFilePtr, "MESH_NUMCVERTEX", 15)) { |
1243 | 9 | ParseLV4MeshLong(iNumCVertices); |
1244 | 9 | continue; |
1245 | 9 | } |
1246 | | // Number of regular faces in the mesh |
1247 | 1.48k | if (TokenMatch(mFilePtr, "MESH_NUMFACES", 13)) { |
1248 | 9 | ParseLV4MeshLong(iNumFaces); |
1249 | 9 | continue; |
1250 | 9 | } |
1251 | | // Number of UVWed faces in the mesh |
1252 | 1.47k | if (TokenMatch(mFilePtr, "MESH_NUMTVFACES", 15)) { |
1253 | 9 | ParseLV4MeshLong(iNumTFaces); |
1254 | 9 | continue; |
1255 | 9 | } |
1256 | | // Number of colored faces in the mesh |
1257 | 1.46k | if (TokenMatch(mFilePtr, "MESH_NUMCVFACES", 15)) { |
1258 | 0 | ParseLV4MeshLong(iNumCFaces); |
1259 | 0 | continue; |
1260 | 0 | } |
1261 | | // mesh vertex list block |
1262 | 1.46k | if (TokenMatch(mFilePtr, "MESH_VERTEX_LIST", 16)) { |
1263 | 9 | ParseLV3MeshVertexListBlock(iNumVertices, mesh); |
1264 | 9 | continue; |
1265 | 9 | } |
1266 | | // mesh face list block |
1267 | 1.45k | if (TokenMatch(mFilePtr, "MESH_FACE_LIST", 14)) { |
1268 | 12 | ParseLV3MeshFaceListBlock(iNumFaces, mesh); |
1269 | 12 | continue; |
1270 | 12 | } |
1271 | | // mesh texture vertex list block |
1272 | 1.44k | if (TokenMatch(mFilePtr, "MESH_TVERTLIST", 14)) { |
1273 | 9 | ParseLV3MeshTListBlock(iNumTVertices, mesh); |
1274 | 9 | continue; |
1275 | 9 | } |
1276 | | // mesh texture face block |
1277 | 1.43k | if (TokenMatch(mFilePtr, "MESH_TFACELIST", 14)) { |
1278 | 9 | ParseLV3MeshTFaceListBlock(iNumTFaces, mesh); |
1279 | 9 | continue; |
1280 | 9 | } |
1281 | | // mesh color vertex list block |
1282 | 1.42k | if (TokenMatch(mFilePtr, "MESH_CVERTLIST", 14)) { |
1283 | 0 | ParseLV3MeshCListBlock(iNumCVertices, mesh); |
1284 | 0 | continue; |
1285 | 0 | } |
1286 | | // mesh color face block |
1287 | 1.42k | if (TokenMatch(mFilePtr, "MESH_CFACELIST", 14)) { |
1288 | 0 | ParseLV3MeshCFaceListBlock(iNumCFaces, mesh); |
1289 | 0 | continue; |
1290 | 0 | } |
1291 | | // mesh normals |
1292 | 1.42k | if (TokenMatch(mFilePtr, "MESH_NORMALS", 12)) { |
1293 | 9 | ParseLV3MeshNormalListBlock(mesh); |
1294 | 9 | continue; |
1295 | 9 | } |
1296 | | // another mesh UV channel ... |
1297 | 1.41k | if (TokenMatch(mFilePtr, "MESH_MAPPINGCHANNEL", 19)) { |
1298 | 0 | unsigned int iIndex(0); |
1299 | 0 | ParseLV4MeshLong(iIndex); |
1300 | 0 | if (0 == iIndex) { |
1301 | 0 | LogWarning("Mapping channel has an invalid index. Skipping UV channel"); |
1302 | | // skip it ... |
1303 | 0 | SkipSection(); |
1304 | 0 | } else { |
1305 | 0 | if (iIndex < 2) { |
1306 | 0 | LogWarning("Mapping channel has an invalid index. Skipping UV channel"); |
1307 | | // skip it ... |
1308 | 0 | SkipSection(); |
1309 | 0 | } |
1310 | 0 | if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) { |
1311 | 0 | LogWarning("Too many UV channels specified. Skipping channel .."); |
1312 | | // skip it ... |
1313 | 0 | SkipSection(); |
1314 | 0 | } else { |
1315 | | // parse the mapping channel |
1316 | 0 | ParseLV3MappingChannel(iIndex - 1, mesh); |
1317 | 0 | } |
1318 | 0 | continue; |
1319 | 0 | } |
1320 | 0 | } |
1321 | | // mesh animation keyframe. Not supported |
1322 | 1.41k | if (TokenMatch(mFilePtr, "MESH_ANIMATION", 14)) { |
1323 | |
|
1324 | 0 | LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " |
1325 | 0 | "Keyframe animation is not supported by Assimp, this element " |
1326 | 0 | "will be ignored"); |
1327 | | //SkipSection(); |
1328 | 0 | continue; |
1329 | 0 | } |
1330 | 1.41k | if (TokenMatch(mFilePtr, "MESH_WEIGHTS", 12)) { |
1331 | 1 | ParseLV3MeshWeightsBlock(mesh); |
1332 | 1 | continue; |
1333 | 1 | } |
1334 | 1.41k | } |
1335 | 63.7k | AI_ASE_HANDLE_SECTION("2", "*MESH"); |
1336 | 63.7k | } |
1337 | 13 | } |
1338 | | // ------------------------------------------------------------------------------------------------ |
1339 | 1 | void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh &mesh) { |
1340 | 1 | AI_ASE_PARSER_INIT(); |
1341 | | |
1342 | 1 | unsigned int iNumVertices = 0, iNumBones = 0; |
1343 | 41 | while (true) { |
1344 | 41 | if ('*' == *mFilePtr) { |
1345 | 1 | ++mFilePtr; |
1346 | | |
1347 | | // Number of bone vertices ... |
1348 | 1 | if (TokenMatch(mFilePtr, "MESH_NUMVERTEX", 14)) { |
1349 | 0 | ParseLV4MeshLong(iNumVertices); |
1350 | 0 | continue; |
1351 | 0 | } |
1352 | | // Number of bones |
1353 | 1 | if (TokenMatch(mFilePtr, "MESH_NUMBONE", 12)) { |
1354 | 0 | ParseLV4MeshLong(iNumBones); |
1355 | 0 | continue; |
1356 | 0 | } |
1357 | | // parse the list of bones |
1358 | 1 | if (TokenMatch(mFilePtr, "MESH_BONE_LIST", 14)) { |
1359 | 0 | ParseLV4MeshBones(iNumBones, mesh); |
1360 | 0 | continue; |
1361 | 0 | } |
1362 | | // parse the list of bones vertices |
1363 | 1 | if (TokenMatch(mFilePtr, "MESH_BONE_VERTEX_LIST", 21)) { |
1364 | 0 | ParseLV4MeshBonesVertices(iNumVertices, mesh); |
1365 | 0 | continue; |
1366 | 0 | } |
1367 | 1 | } |
1368 | 81 | AI_ASE_HANDLE_SECTION("3", "*MESH_WEIGHTS"); |
1369 | 81 | } |
1370 | 1 | } |
1371 | | // ------------------------------------------------------------------------------------------------ |
1372 | 0 | void Parser::ParseLV4MeshBones(unsigned int iNumBones, ASE::Mesh &mesh) { |
1373 | 0 | AI_ASE_PARSER_INIT(); |
1374 | 0 | mesh.mBones.resize(iNumBones, Bone("UNNAMED")); |
1375 | 0 | while (true) { |
1376 | 0 | if ('*' == *mFilePtr) { |
1377 | 0 | ++mFilePtr; |
1378 | | |
1379 | | // Mesh bone with name ... |
1380 | 0 | if (TokenMatch(mFilePtr, "MESH_BONE_NAME", 14)) { |
1381 | | // parse an index ... |
1382 | 0 | if (SkipSpaces(&mFilePtr, mEnd)) { |
1383 | 0 | unsigned int iIndex = strtoul10(mFilePtr, &mFilePtr); |
1384 | 0 | if (iIndex >= iNumBones) { |
1385 | 0 | LogWarning("Bone index is out of bounds"); |
1386 | 0 | continue; |
1387 | 0 | } |
1388 | 0 | if (!ParseString(mesh.mBones[iIndex].mName, "*MESH_BONE_NAME")) |
1389 | 0 | SkipToNextToken(); |
1390 | 0 | continue; |
1391 | 0 | } |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 | AI_ASE_HANDLE_SECTION("3", "*MESH_BONE_LIST"); |
1395 | 0 | } |
1396 | 0 | } |
1397 | | // ------------------------------------------------------------------------------------------------ |
1398 | 0 | void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices, ASE::Mesh &mesh) { |
1399 | 0 | AI_ASE_PARSER_INIT(); |
1400 | 0 | mesh.mBoneVertices.resize(iNumVertices); |
1401 | 0 | while (true) { |
1402 | 0 | if ('*' == *mFilePtr) { |
1403 | 0 | ++mFilePtr; |
1404 | | |
1405 | | // Mesh bone vertex |
1406 | 0 | if (TokenMatch(mFilePtr, "MESH_BONE_VERTEX", 16)) { |
1407 | | // read the vertex index |
1408 | 0 | unsigned int iIndex = strtoul10(mFilePtr, &mFilePtr); |
1409 | 0 | if (mesh.mBoneVertices.empty()) { |
1410 | 0 | SkipSection(); |
1411 | 0 | } |
1412 | 0 | if (iIndex >= mesh.mBoneVertices.size() ) { |
1413 | 0 | LogWarning("Bone vertex index is out of bounds. Using the largest valid " |
1414 | 0 | "bone vertex index instead"); |
1415 | 0 | iIndex = (unsigned int)mesh.mBoneVertices.size() - 1; |
1416 | 0 | } |
1417 | | |
1418 | | // --- ignored |
1419 | 0 | ai_real afVert[3]; |
1420 | 0 | ParseLV4MeshRealTriple(afVert); |
1421 | |
|
1422 | 0 | std::pair<int, float> pairOut; |
1423 | 0 | while (true) { |
1424 | | // first parse the bone index ... |
1425 | 0 | if (!SkipSpaces(&mFilePtr, mEnd)) break; |
1426 | 0 | pairOut.first = strtoul10(mFilePtr, &mFilePtr); |
1427 | | |
1428 | | // then parse the vertex weight |
1429 | 0 | if (!SkipSpaces(&mFilePtr, mEnd)) break; |
1430 | 0 | mFilePtr = fast_atoreal_move(mFilePtr, pairOut.second); |
1431 | | |
1432 | | // -1 marks unused entries |
1433 | 0 | if (-1 != pairOut.first) { |
1434 | 0 | mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); |
1435 | 0 | } |
1436 | 0 | } |
1437 | 0 | continue; |
1438 | 0 | } |
1439 | 0 | } |
1440 | 0 | AI_ASE_HANDLE_SECTION("4", "*MESH_BONE_VERTEX"); |
1441 | 0 | } |
1442 | 0 | } |
1443 | | // ------------------------------------------------------------------------------------------------ |
1444 | | void Parser::ParseLV3MeshVertexListBlock( |
1445 | 9 | unsigned int iNumVertices, ASE::Mesh &mesh) { |
1446 | 9 | AI_ASE_PARSER_INIT(); |
1447 | | |
1448 | | // allocate enough storage in the array |
1449 | 9 | mesh.mPositions.resize(iNumVertices); |
1450 | 405 | while (true) { |
1451 | 405 | if ('*' == *mFilePtr) { |
1452 | 72 | ++mFilePtr; |
1453 | | |
1454 | | // Vertex entry |
1455 | 72 | if (TokenMatch(mFilePtr, "MESH_VERTEX", 11)) { |
1456 | | |
1457 | 72 | aiVector3D vTemp; |
1458 | 72 | unsigned int iIndex; |
1459 | 72 | ParseLV4MeshRealTriple(&vTemp.x, iIndex); |
1460 | | |
1461 | 72 | if (iIndex >= iNumVertices) { |
1462 | 0 | LogWarning("Invalid vertex index. It will be ignored"); |
1463 | 0 | } else |
1464 | 72 | mesh.mPositions[iIndex] = vTemp; |
1465 | 72 | continue; |
1466 | 72 | } |
1467 | 72 | } |
1468 | 657 | AI_ASE_HANDLE_SECTION("3", "*MESH_VERTEX_LIST"); |
1469 | 657 | } |
1470 | 9 | } |
1471 | | // ------------------------------------------------------------------------------------------------ |
1472 | 12 | void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) { |
1473 | 12 | AI_ASE_PARSER_INIT(); |
1474 | | |
1475 | | // allocate enough storage in the face array |
1476 | 12 | mesh.mFaces.resize(iNumFaces); |
1477 | 43.2k | while (true) { |
1478 | 43.2k | if ('*' == *mFilePtr) { |
1479 | 2.39k | ++mFilePtr; |
1480 | | |
1481 | | // Face entry |
1482 | 2.39k | if (TokenMatch(mFilePtr, "MESH_FACE", 9)) { |
1483 | | |
1484 | 676 | ASE::Face mFace; |
1485 | 676 | ParseLV4MeshFace(mFace); |
1486 | | |
1487 | 676 | if (mFace.iFace >= iNumFaces) { |
1488 | 568 | LogWarning("Face has an invalid index. It will be ignored"); |
1489 | 568 | } else |
1490 | 108 | mesh.mFaces[mFace.iFace] = mFace; |
1491 | 676 | continue; |
1492 | 676 | } |
1493 | 2.39k | } |
1494 | 85.2k | AI_ASE_HANDLE_SECTION("3", "*MESH_FACE_LIST"); |
1495 | 85.2k | } |
1496 | 12 | } |
1497 | | // ------------------------------------------------------------------------------------------------ |
1498 | | void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, |
1499 | 9 | ASE::Mesh &mesh, unsigned int iChannel) { |
1500 | 9 | AI_ASE_PARSER_INIT(); |
1501 | | |
1502 | | // allocate enough storage in the array |
1503 | 9 | mesh.amTexCoords[iChannel].resize(iNumVertices); |
1504 | 585 | while (true) { |
1505 | 585 | if ('*' == *mFilePtr) { |
1506 | 108 | ++mFilePtr; |
1507 | | |
1508 | | // Vertex entry |
1509 | 108 | if (TokenMatch(mFilePtr, "MESH_TVERT", 10)) { |
1510 | 108 | aiVector3D vTemp; |
1511 | 108 | unsigned int iIndex; |
1512 | 108 | ParseLV4MeshRealTriple(&vTemp.x, iIndex); |
1513 | | |
1514 | 108 | if (iIndex >= iNumVertices) { |
1515 | 0 | LogWarning("Tvertex has an invalid index. It will be ignored"); |
1516 | 0 | } else |
1517 | 108 | mesh.amTexCoords[iChannel][iIndex] = vTemp; |
1518 | | |
1519 | 108 | if (0.0f != vTemp.z) { |
1520 | | // we need 3 coordinate channels |
1521 | 0 | mesh.mNumUVComponents[iChannel] = 3; |
1522 | 0 | } |
1523 | 108 | continue; |
1524 | 108 | } |
1525 | 108 | } |
1526 | 945 | AI_ASE_HANDLE_SECTION("3", "*MESH_TVERT_LIST"); |
1527 | 945 | } |
1528 | 9 | } |
1529 | | // ------------------------------------------------------------------------------------------------ |
1530 | | void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, |
1531 | 9 | ASE::Mesh &mesh, unsigned int iChannel) { |
1532 | 9 | AI_ASE_PARSER_INIT(); |
1533 | 585 | while (true) { |
1534 | 585 | if ('*' == *mFilePtr) { |
1535 | 108 | ++mFilePtr; |
1536 | | |
1537 | | // Face entry |
1538 | 108 | if (TokenMatch(mFilePtr, "MESH_TFACE", 10)) { |
1539 | 108 | unsigned int aiValues[3]; |
1540 | 108 | unsigned int iIndex = 0; |
1541 | | |
1542 | 108 | ParseLV4MeshLongTriple(aiValues, iIndex); |
1543 | 108 | if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { |
1544 | 0 | LogWarning("UV-Face has an invalid index. It will be ignored"); |
1545 | 108 | } else { |
1546 | | // copy UV indices |
1547 | 108 | mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; |
1548 | 108 | mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; |
1549 | 108 | mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; |
1550 | 108 | } |
1551 | 108 | continue; |
1552 | 108 | } |
1553 | 108 | } |
1554 | 945 | AI_ASE_HANDLE_SECTION("3", "*MESH_TFACE_LIST"); |
1555 | 945 | } |
1556 | 9 | } |
1557 | | // ------------------------------------------------------------------------------------------------ |
1558 | 0 | void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh &mesh) { |
1559 | 0 | AI_ASE_PARSER_INIT(); |
1560 | |
|
1561 | 0 | unsigned int iNumTVertices = 0; |
1562 | 0 | unsigned int iNumTFaces = 0; |
1563 | 0 | while (true) { |
1564 | 0 | if ('*' == *mFilePtr) { |
1565 | 0 | ++mFilePtr; |
1566 | | |
1567 | | // Number of texture coordinates in the mesh |
1568 | 0 | if (TokenMatch(mFilePtr, "MESH_NUMTVERTEX", 15)) { |
1569 | 0 | ParseLV4MeshLong(iNumTVertices); |
1570 | 0 | continue; |
1571 | 0 | } |
1572 | | // Number of UVWed faces in the mesh |
1573 | 0 | if (TokenMatch(mFilePtr, "MESH_NUMTVFACES", 15)) { |
1574 | 0 | ParseLV4MeshLong(iNumTFaces); |
1575 | 0 | continue; |
1576 | 0 | } |
1577 | | // mesh texture vertex list block |
1578 | 0 | if (TokenMatch(mFilePtr, "MESH_TVERTLIST", 14)) { |
1579 | 0 | ParseLV3MeshTListBlock(iNumTVertices, mesh, iChannel); |
1580 | 0 | continue; |
1581 | 0 | } |
1582 | | // mesh texture face block |
1583 | 0 | if (TokenMatch(mFilePtr, "MESH_TFACELIST", 14)) { |
1584 | 0 | ParseLV3MeshTFaceListBlock(iNumTFaces, mesh, iChannel); |
1585 | 0 | continue; |
1586 | 0 | } |
1587 | 0 | } |
1588 | 0 | AI_ASE_HANDLE_SECTION("3", "*MESH_MAPPING_CHANNEL"); |
1589 | 0 | } |
1590 | 0 | } |
1591 | | // ------------------------------------------------------------------------------------------------ |
1592 | 0 | void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh &mesh) { |
1593 | 0 | AI_ASE_PARSER_INIT(); |
1594 | | |
1595 | | // allocate enough storage in the array |
1596 | 0 | mesh.mVertexColors.resize(iNumVertices); |
1597 | 0 | while (true) { |
1598 | 0 | if ('*' == *mFilePtr) { |
1599 | 0 | ++mFilePtr; |
1600 | | |
1601 | | // Vertex entry |
1602 | 0 | if (TokenMatch(mFilePtr, "MESH_VERTCOL", 12)) { |
1603 | 0 | aiColor4D vTemp; |
1604 | 0 | vTemp.a = 1.0f; |
1605 | 0 | unsigned int iIndex; |
1606 | 0 | ParseLV4MeshFloatTriple(&vTemp.r, iIndex); |
1607 | |
|
1608 | 0 | if (iIndex >= iNumVertices) { |
1609 | 0 | LogWarning("Vertex color has an invalid index. It will be ignored"); |
1610 | 0 | } else |
1611 | 0 | mesh.mVertexColors[iIndex] = vTemp; |
1612 | 0 | continue; |
1613 | 0 | } |
1614 | 0 | } |
1615 | 0 | AI_ASE_HANDLE_SECTION("3", "*MESH_CVERTEX_LIST"); |
1616 | 0 | } |
1617 | 0 | } |
1618 | | // ------------------------------------------------------------------------------------------------ |
1619 | 0 | void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh &mesh) { |
1620 | 0 | AI_ASE_PARSER_INIT(); |
1621 | 0 | while (true) { |
1622 | 0 | if ('*' == *mFilePtr) { |
1623 | 0 | ++mFilePtr; |
1624 | | |
1625 | | // Face entry |
1626 | 0 | if (TokenMatch(mFilePtr, "MESH_CFACE", 10)) { |
1627 | 0 | unsigned int aiValues[3]; |
1628 | 0 | unsigned int iIndex = 0; |
1629 | |
|
1630 | 0 | ParseLV4MeshLongTriple(aiValues, iIndex); |
1631 | 0 | if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) { |
1632 | 0 | LogWarning("UV-Face has an invalid index. It will be ignored"); |
1633 | 0 | } else { |
1634 | | // copy color indices |
1635 | 0 | mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; |
1636 | 0 | mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; |
1637 | 0 | mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; |
1638 | 0 | } |
1639 | 0 | continue; |
1640 | 0 | } |
1641 | 0 | } |
1642 | 0 | AI_ASE_HANDLE_SECTION("3", "*MESH_CFACE_LIST"); |
1643 | 0 | } |
1644 | 0 | } |
1645 | | // ------------------------------------------------------------------------------------------------ |
1646 | 9 | void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh &sMesh) { |
1647 | 9 | AI_ASE_PARSER_INIT(); |
1648 | | |
1649 | | // Allocate enough storage for the normals |
1650 | 9 | sMesh.mNormals.resize(sMesh.mFaces.size() * 3, aiVector3D(0.f, 0.f, 0.f)); |
1651 | 9 | unsigned int index, faceIdx = UINT_MAX; |
1652 | | |
1653 | | // FIXME: rewrite this and find out how to interpret the normals |
1654 | | // correctly. This is crap. |
1655 | | |
1656 | | // Smooth the vertex and face normals together. The result |
1657 | | // will be edgy then, but otherwise everything would be soft ... |
1658 | 2.52k | while (true) { |
1659 | 2.52k | if ('*' == *mFilePtr) { |
1660 | 432 | ++mFilePtr; |
1661 | 432 | if (faceIdx != UINT_MAX && TokenMatch(mFilePtr, "MESH_VERTEXNORMAL", 17)) { |
1662 | 324 | aiVector3D vNormal; |
1663 | 324 | ParseLV4MeshRealTriple(&vNormal.x, index); |
1664 | 324 | if (faceIdx >= sMesh.mFaces.size()) |
1665 | 0 | continue; |
1666 | | |
1667 | | // Make sure we assign it to the correct face |
1668 | 324 | const ASE::Face &face = sMesh.mFaces[faceIdx]; |
1669 | 324 | if (index == face.mIndices[0]) |
1670 | 108 | index = 0; |
1671 | 216 | else if (index == face.mIndices[1]) |
1672 | 108 | index = 1; |
1673 | 108 | else if (index == face.mIndices[2]) |
1674 | 108 | index = 2; |
1675 | 0 | else { |
1676 | 0 | ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); |
1677 | 0 | continue; |
1678 | 0 | } |
1679 | | // We'll renormalize later |
1680 | 324 | sMesh.mNormals[faceIdx * 3 + index] += vNormal; |
1681 | 324 | continue; |
1682 | 324 | } |
1683 | 108 | if (TokenMatch(mFilePtr, "MESH_FACENORMAL", 15)) { |
1684 | 108 | aiVector3D vNormal; |
1685 | 108 | ParseLV4MeshRealTriple(&vNormal.x, faceIdx); |
1686 | | |
1687 | 108 | if (faceIdx >= sMesh.mFaces.size()) { |
1688 | 0 | ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_FACENORMAL section"); |
1689 | 0 | continue; |
1690 | 0 | } |
1691 | | |
1692 | | // We'll renormalize later |
1693 | 108 | sMesh.mNormals[faceIdx * 3] += vNormal; |
1694 | 108 | sMesh.mNormals[faceIdx * 3 + 1] += vNormal; |
1695 | 108 | sMesh.mNormals[faceIdx * 3 + 2] += vNormal; |
1696 | 108 | continue; |
1697 | 108 | } |
1698 | 108 | } |
1699 | 4.18k | AI_ASE_HANDLE_SECTION("3", "*MESH_NORMALS"); |
1700 | 4.18k | } |
1701 | 9 | } |
1702 | | // ------------------------------------------------------------------------------------------------ |
1703 | 676 | void Parser::ParseLV4MeshFace(ASE::Face &out) { |
1704 | | // skip spaces and tabs |
1705 | 676 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1706 | 0 | LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); |
1707 | 0 | SkipToNextToken(); |
1708 | 0 | return; |
1709 | 0 | } |
1710 | | |
1711 | | // parse the face index |
1712 | 676 | out.iFace = strtoul10(mFilePtr, &mFilePtr); |
1713 | | |
1714 | | // next character should be ':' |
1715 | 676 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1716 | | // FIX: there are some ASE files which haven't got : here .... |
1717 | 0 | LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); |
1718 | 0 | SkipToNextToken(); |
1719 | 0 | return; |
1720 | 0 | } |
1721 | | // FIX: There are some ASE files which haven't got ':' here |
1722 | 676 | if (':' == *mFilePtr) ++mFilePtr; |
1723 | | |
1724 | | // Parse all mesh indices |
1725 | 1.30k | for (unsigned int i = 0; i < 3; ++i) { |
1726 | 1.19k | unsigned int iIndex = 0; |
1727 | 1.19k | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1728 | 56 | LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); |
1729 | 56 | SkipToNextToken(); |
1730 | 56 | return; |
1731 | 56 | } |
1732 | 1.13k | switch (*mFilePtr) { |
1733 | 468 | case 'A': |
1734 | 476 | case 'a': |
1735 | 476 | break; |
1736 | 172 | case 'B': |
1737 | 323 | case 'b': |
1738 | 323 | iIndex = 1; |
1739 | 323 | break; |
1740 | 116 | case 'C': |
1741 | 194 | case 'c': |
1742 | 194 | iIndex = 2; |
1743 | 194 | break; |
1744 | 143 | default: |
1745 | 143 | LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " |
1746 | 143 | "A,B or C expected [#3]"); |
1747 | 143 | SkipToNextToken(); |
1748 | 143 | return; |
1749 | 1.13k | }; |
1750 | 993 | ++mFilePtr; |
1751 | | |
1752 | | // next character should be ':' |
1753 | 993 | if (!SkipSpaces(&mFilePtr, mEnd) || ':' != *mFilePtr) { |
1754 | 317 | LogWarning("Unable to parse *MESH_FACE Element: " |
1755 | 317 | "Unexpected EOL. \':\' expected [#2]"); |
1756 | 317 | SkipToNextToken(); |
1757 | 317 | return; |
1758 | 317 | } |
1759 | | |
1760 | 676 | ++mFilePtr; |
1761 | 676 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1762 | 52 | LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " |
1763 | 52 | "Vertex index expected [#4]"); |
1764 | 52 | SkipToNextToken(); |
1765 | 52 | return; |
1766 | 52 | } |
1767 | 624 | out.mIndices[iIndex] = strtoul10(mFilePtr, &mFilePtr); |
1768 | 624 | } |
1769 | | |
1770 | | // now we need to skip the AB, BC, CA blocks. |
1771 | 3.24k | while (true) { |
1772 | 3.24k | if ('*' == *mFilePtr) break; |
1773 | 3.13k | if (IsLineEnd(*mFilePtr)) { |
1774 | | //iLineNumber++; |
1775 | 0 | return; |
1776 | 0 | } |
1777 | 3.13k | mFilePtr++; |
1778 | 3.13k | } |
1779 | | |
1780 | | // parse the smoothing group of the face |
1781 | 108 | if (TokenMatch(mFilePtr, "*MESH_SMOOTHING", 15)) { |
1782 | 108 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1783 | 0 | LogWarning("Unable to parse *MESH_SMOOTHING Element: " |
1784 | 0 | "Unexpected EOL. Smoothing group(s) expected [#5]"); |
1785 | 0 | SkipToNextToken(); |
1786 | 0 | return; |
1787 | 0 | } |
1788 | | |
1789 | | // Parse smoothing groups until we don't anymore see commas |
1790 | | // FIX: There needn't always be a value, sad but true |
1791 | 108 | while (true) { |
1792 | 108 | if (*mFilePtr < '9' && *mFilePtr >= '0') { |
1793 | 108 | uint32_t value = strtoul10(mFilePtr, &mFilePtr); |
1794 | 108 | if (value < 32) { |
1795 | 108 | out.iSmoothGroup |= (1 << strtoul10(mFilePtr, &mFilePtr)); |
1796 | 108 | } else { |
1797 | 0 | const std::string message = std::string("Unable to set smooth group, value with ") + ai_to_string(value) + std::string(" out of range"); |
1798 | 0 | LogWarning(message.c_str()); |
1799 | 0 | } |
1800 | 108 | } |
1801 | 108 | SkipSpaces(&mFilePtr, mEnd); |
1802 | 108 | if (',' != *mFilePtr) { |
1803 | 108 | break; |
1804 | 108 | } |
1805 | 0 | ++mFilePtr; |
1806 | 0 | SkipSpaces(&mFilePtr, mEnd); |
1807 | 0 | } |
1808 | 108 | } |
1809 | | |
1810 | | // *MESH_MTLID is optional, too |
1811 | 108 | while (true) { |
1812 | 108 | if ('*' == *mFilePtr) { |
1813 | 108 | break; |
1814 | 108 | } |
1815 | 0 | if (IsLineEnd(*mFilePtr)) { |
1816 | 0 | return; |
1817 | 0 | } |
1818 | 0 | mFilePtr++; |
1819 | 0 | } |
1820 | | |
1821 | 108 | if (TokenMatch(mFilePtr, "*MESH_MTLID", 11)) { |
1822 | 108 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1823 | 0 | LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " |
1824 | 0 | "Material index expected [#6]"); |
1825 | 0 | SkipToNextToken(); |
1826 | 0 | return; |
1827 | 0 | } |
1828 | 108 | out.iMaterial = strtoul10(mFilePtr, &mFilePtr); |
1829 | 108 | } |
1830 | 108 | return; |
1831 | 108 | } |
1832 | | // ------------------------------------------------------------------------------------------------ |
1833 | 156 | void Parser::ParseLV4MeshLongTriple(unsigned int *apOut) { |
1834 | 156 | ai_assert(nullptr != apOut); |
1835 | | |
1836 | 624 | for (unsigned int i = 0; i < 3; ++i) |
1837 | 468 | ParseLV4MeshLong(apOut[i]); |
1838 | 156 | } |
1839 | | // ------------------------------------------------------------------------------------------------ |
1840 | 108 | void Parser::ParseLV4MeshLongTriple(unsigned int *apOut, unsigned int &rIndexOut) { |
1841 | 108 | ai_assert(nullptr != apOut); |
1842 | | |
1843 | | // parse the index |
1844 | 108 | ParseLV4MeshLong(rIndexOut); |
1845 | | |
1846 | | // parse the three others |
1847 | 108 | ParseLV4MeshLongTriple(apOut); |
1848 | 108 | } |
1849 | | // ------------------------------------------------------------------------------------------------ |
1850 | 3.04k | void Parser::ParseLV4MeshRealTriple(ai_real *apOut, unsigned int &rIndexOut) { |
1851 | 3.04k | ai_assert(nullptr != apOut); |
1852 | | |
1853 | | // parse the index |
1854 | 3.04k | ParseLV4MeshLong(rIndexOut); |
1855 | | |
1856 | | // parse the three others |
1857 | 3.04k | ParseLV4MeshRealTriple(apOut); |
1858 | 3.04k | } |
1859 | | // ------------------------------------------------------------------------------------------------ |
1860 | 0 | void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) { |
1861 | 0 | ai_assert(nullptr != apOut); |
1862 | | |
1863 | | // parse the index |
1864 | 0 | ParseLV4MeshLong(rIndexOut); |
1865 | | |
1866 | | // parse the three others |
1867 | 0 | ParseLV4MeshFloatTriple(apOut); |
1868 | 0 | } |
1869 | | // ------------------------------------------------------------------------------------------------ |
1870 | 3.11k | void Parser::ParseLV4MeshRealTriple(ai_real *apOut) { |
1871 | 3.11k | ai_assert(nullptr != apOut); |
1872 | | |
1873 | 12.4k | for (unsigned int i = 0; i < 3; ++i) { |
1874 | 9.34k | ParseLV4MeshReal(apOut[i]); |
1875 | 9.34k | } |
1876 | 3.11k | } |
1877 | | // ------------------------------------------------------------------------------------------------ |
1878 | 15 | void Parser::ParseLV4MeshFloatTriple(float* apOut) { |
1879 | 15 | ai_assert(nullptr != apOut); |
1880 | | |
1881 | 60 | for (unsigned int i = 0; i < 3; ++i) { |
1882 | 45 | ParseLV4MeshFloat(apOut[i]); |
1883 | 45 | } |
1884 | 15 | } |
1885 | | // ------------------------------------------------------------------------------------------------ |
1886 | 11.2k | void Parser::ParseLV4MeshReal(ai_real &fOut) { |
1887 | | // skip spaces and tabs |
1888 | 11.2k | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1889 | | // LOG |
1890 | 4.50k | LogWarning("Unable to parse float: unexpected EOL [#1]"); |
1891 | 4.50k | fOut = 0.0; |
1892 | 4.50k | ++iLineNumber; |
1893 | 4.50k | return; |
1894 | 4.50k | } |
1895 | | // parse the first float |
1896 | 6.71k | mFilePtr = fast_atoreal_move(mFilePtr, fOut); |
1897 | 6.71k | } |
1898 | | // ------------------------------------------------------------------------------------------------ |
1899 | 45 | void Parser::ParseLV4MeshFloat(float &fOut) { |
1900 | | // skip spaces and tabs |
1901 | 45 | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1902 | | // LOG |
1903 | 0 | LogWarning("Unable to parse float: unexpected EOL [#1]"); |
1904 | 0 | fOut = 0.0; |
1905 | 0 | ++iLineNumber; |
1906 | 0 | return; |
1907 | 0 | } |
1908 | | // parse the first float |
1909 | 45 | mFilePtr = fast_atoreal_move(mFilePtr, fOut); |
1910 | 45 | } |
1911 | | // ------------------------------------------------------------------------------------------------ |
1912 | 4.30k | void Parser::ParseLV4MeshLong(unsigned int &iOut) { |
1913 | | // Skip spaces and tabs |
1914 | 4.30k | if (!SkipSpaces(&mFilePtr, mEnd)) { |
1915 | | // LOG |
1916 | 1.13k | LogWarning("Unable to parse long: unexpected EOL [#1]"); |
1917 | 1.13k | iOut = 0; |
1918 | 1.13k | ++iLineNumber; |
1919 | 1.13k | return; |
1920 | 1.13k | } |
1921 | | // parse the value |
1922 | 3.17k | iOut = strtoul10(mFilePtr, &mFilePtr); |
1923 | 3.17k | } |
1924 | | |
1925 | | } |
1926 | | |
1927 | | #endif // ASSIMP_BUILD_NO_3DS_IMPORTER |
1928 | | |
1929 | | #endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER |