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