Coverage Report

Created: 2026-01-25 07:15

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