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