Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/LWO/LWOLoader.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2025, 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  LWOLoader.cpp
43
 *  @brief Implementation of the LWO importer class
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
47
48
// internal headers
49
#include "LWOLoader.h"
50
#include "PostProcessing/ConvertToLHProcess.h"
51
#include "PostProcessing/ProcessHelper.h"
52
#include "Geometry/GeometryUtils.h"
53
54
#include <assimp/ByteSwapper.h>
55
#include <assimp/SGSpatialSort.h>
56
#include <assimp/StringComparison.h>
57
#include <assimp/importerdesc.h>
58
#include <assimp/IOSystem.hpp>
59
60
#include <iomanip>
61
#include <map>
62
#include <memory>
63
#include <sstream>
64
65
using namespace Assimp;
66
67
static const aiImporterDesc desc = {
68
    "LightWave/Modo Object Importer",
69
    "",
70
    "",
71
    "https://www.lightwave3d.com/lightwave_sdk/",
72
    aiImporterFlags_SupportTextFlavour,
73
    0,
74
    0,
75
    0,
76
    0,
77
    "lwo lxo"
78
};
79
80
// ------------------------------------------------------------------------------------------------
81
// Constructor to be privately used by Importer
82
LWOImporter::LWOImporter() :
83
        mIsLWO2(),
84
        mIsLXOB(),
85
        mIsLWO3(),
86
        mLayers(),
87
        mCurLayer(),
88
        mTags(),
89
        mMapping(),
90
        mSurfaces(),
91
        mFileBuffer(),
92
        fileSize(),
93
891
        mScene(nullptr),
94
        configSpeedFlag(),
95
        configLayerIndex(),
96
891
        hasNamedLayer() {
97
    // empty
98
891
}
99
100
// ------------------------------------------------------------------------------------------------
101
// Destructor, private as well
102
891
LWOImporter::~LWOImporter() = default;
103
104
// ------------------------------------------------------------------------------------------------
105
// Returns whether the class can handle the format of the given file.
106
479
bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
107
479
    static constexpr uint32_t tokens[] = {
108
479
        AI_LWO_FOURCC_LWOB,
109
479
        AI_LWO_FOURCC_LWO2,
110
479
        AI_LWO_FOURCC_LXOB
111
479
    };
112
479
    return CheckMagicToken(pIOHandler, file, tokens, AI_COUNT_OF(tokens), 8);
113
479
}
114
115
// ------------------------------------------------------------------------------------------------
116
// Setup configuration properties
117
0
void LWOImporter::SetupProperties(const Importer *pImp) {
118
0
    configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0) ? true : false);
119
0
    configLayerIndex = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY, UINT_MAX);
120
0
    configLayerName = pImp->GetPropertyString(AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY, "");
121
0
}
122
123
// ------------------------------------------------------------------------------------------------
124
// Get list of file extensions
125
877
const aiImporterDesc *LWOImporter::GetInfo() const {
126
877
    return &desc;
127
877
}
128
129
// ------------------------------------------------------------------------------------------------
130
// Imports the given file into the given scene structure.
131
void LWOImporter::InternReadFile(const std::string &pFile,
132
        aiScene *pScene,
133
0
        IOSystem *pIOHandler) {
134
0
    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
135
136
    // Check whether we can read from the file
137
0
    if (file == nullptr) {
138
0
        throw DeadlyImportError("Failed to open LWO file ", pFile, ".");
139
0
    }
140
141
0
    if ((this->fileSize = (unsigned int)file->FileSize()) < 12) {
142
0
        throw DeadlyImportError("LWO: The file is too small to contain the IFF header");
143
0
    }
144
145
    // Allocate storage and copy the contents of the file to a memory buffer
146
0
    std::vector<uint8_t> mBuffer(fileSize);
147
0
    file->Read(&mBuffer[0], 1, fileSize);
148
0
    mScene = pScene;
149
150
    // Determine the type of the file
151
0
    uint32_t fileType;
152
0
    const char *sz = IFF::ReadHeader(&mBuffer[0], fileType);
153
0
    if (sz) {
154
0
        throw DeadlyImportError(sz);
155
0
    }
156
157
0
    mFileBuffer = &mBuffer[0] + 12;
158
0
    fileSize -= 12;
159
160
    // Initialize some members with their default values
161
0
    hasNamedLayer = false;
162
163
    // Create temporary storage on the stack but store pointers to it in the class
164
    // instance. Therefore everything will be destructed properly if an exception
165
    // is thrown and we needn't take care of that.
166
0
    LayerList _mLayers;
167
0
    SurfaceList _mSurfaces;
168
0
    TagList _mTags;
169
0
    TagMappingTable _mMapping;
170
171
0
    mLayers = &_mLayers;
172
0
    mTags = &_mTags;
173
0
    mMapping = &_mMapping;
174
0
    mSurfaces = &_mSurfaces;
175
176
    // Allocate a default layer (layer indices are 1-based from now)
177
0
    mLayers->push_back(Layer());
178
0
    mCurLayer = &mLayers->back();
179
0
    mCurLayer->mName = "<LWODefault>";
180
0
    mCurLayer->mIndex = 1;
181
182
    // old lightwave file format (prior to v6)
183
0
    mIsLWO2 = false;
184
0
    mIsLWO3 = false;
185
0
    mIsLXOB = false;
186
187
0
    if (AI_LWO_FOURCC_LWOB == fileType) {
188
0
        ASSIMP_LOG_INFO("LWO file format: LWOB (<= LightWave 5.5)");
189
190
0
        LoadLWOBFile();
191
0
    } else if (AI_LWO_FOURCC_LWO2 == fileType) {
192
        // New lightwave format
193
0
        ASSIMP_LOG_INFO("LWO file format: LWO2 (>= LightWave 6)");
194
0
    } else if ( AI_LWO_FOURCC_LWO3 == fileType ) {
195
0
        ASSIMP_LOG_INFO("LWO file format: LWO3 (>= LightWave 2018)");
196
0
    } else if (AI_LWO_FOURCC_LXOB == fileType) {
197
        // MODO file format
198
0
        mIsLXOB = true;
199
0
        ASSIMP_LOG_INFO("LWO file format: LXOB (Modo)");
200
0
    }
201
0
    else {
202
0
        char szBuff[5];
203
0
        szBuff[0] = (char)(fileType >> 24u);
204
0
        szBuff[1] = (char)(fileType >> 16u);
205
0
        szBuff[2] = (char)(fileType >> 8u);
206
0
        szBuff[3] = (char)(fileType);
207
0
        szBuff[4] = '\0';
208
0
        throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
209
0
    }
210
211
0
    if (AI_LWO_FOURCC_LWOB != fileType) {   //
212
0
        if( AI_LWO_FOURCC_LWO3 == fileType ) {
213
0
            mIsLWO3 = true;
214
0
        } else {
215
0
            mIsLWO2 = true;
216
0
        }
217
218
0
        LoadLWO2File();
219
220
        // The newer lightwave format allows the user to configure the
221
        // loader that just one layer is used. If this is the case
222
        // we need to check now whether the requested layer has been found.
223
0
        if (UINT_MAX != configLayerIndex) {
224
0
            unsigned int layerCount = 0;
225
0
            for (std::list<LWO::Layer>::iterator itLayers = mLayers->begin(); itLayers != mLayers->end(); ++itLayers)
226
0
                if (!itLayers->skip)
227
0
                    layerCount++;
228
0
            if (layerCount != 2)
229
0
                throw DeadlyImportError("LWO2: The requested layer was not found");
230
0
        }
231
232
0
        if (configLayerName.length() && !hasNamedLayer) {
233
0
            throw DeadlyImportError("LWO2: Unable to find the requested layer: ", configLayerName);
234
0
        }
235
0
    }
236
237
    // now, as we have loaded all data, we can resolve cross-referenced tags and clips
238
0
    ResolveTags();
239
0
    ResolveClips();
240
241
    // now process all layers and build meshes and nodes
242
0
    std::vector<aiMesh *> apcMeshes;
243
0
    std::map<uint16_t, aiNode *> apcNodes;
244
245
0
    apcMeshes.reserve(mLayers->size() * std::min(((unsigned int)mSurfaces->size() / 2u), 1u));
246
247
0
    unsigned int iDefaultSurface = UINT_MAX; // index of the default surface
248
0
    for (LWO::Layer &layer : *mLayers) {
249
0
        if (layer.skip)
250
0
            continue;
251
252
        // I don't know whether there could be dummy layers, but it would be possible
253
0
        const unsigned int meshStart = (unsigned int)apcMeshes.size();
254
0
        if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) {
255
256
            // now sort all faces by the surfaces assigned to them
257
0
            std::vector<SortedRep> pSorted(mSurfaces->size() + 1);
258
259
0
            unsigned int i = 0;
260
0
            for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end(); it != end; ++it, ++i) {
261
                // Check whether we support this face's type
262
0
                if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH &&
263
0
                        (*it).type != AI_LWO_BONE && (*it).type != AI_LWO_SUBD) {
264
0
                    continue;
265
0
                }
266
267
0
                unsigned int idx = (*it).surfaceIndex;
268
0
                if (idx >= mTags->size()) {
269
0
                    ASSIMP_LOG_WARN("LWO: Invalid face surface index");
270
0
                    idx = UINT_MAX;
271
0
                }
272
0
                if (UINT_MAX == idx || UINT_MAX == (idx = _mMapping[idx])) {
273
0
                    if (UINT_MAX == iDefaultSurface) {
274
0
                        iDefaultSurface = (unsigned int)mSurfaces->size();
275
0
                        mSurfaces->push_back(LWO::Surface());
276
0
                        LWO::Surface &surf = mSurfaces->back();
277
0
                        surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f;
278
0
                        surf.mName = "LWODefaultSurface";
279
0
                    }
280
0
                    idx = iDefaultSurface;
281
0
                }
282
0
                pSorted[idx].push_back(i);
283
0
            }
284
0
            if (UINT_MAX == iDefaultSurface) {
285
0
                pSorted.erase(pSorted.end() - 1);
286
0
            }
287
0
            for (unsigned int j = 0; j < mSurfaces->size(); ++j) {
288
0
                SortedRep &sorted = pSorted[j];
289
0
                if (sorted.empty())
290
0
                    continue;
291
292
                // generate the mesh
293
0
                aiMesh *mesh = new aiMesh();
294
0
                apcMeshes.push_back(mesh);
295
0
                mesh->mNumFaces = (unsigned int)sorted.size();
296
297
                // count the number of vertices
298
0
                SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
299
0
                for (; it != end; ++it) {
300
0
                    mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
301
0
                }
302
303
0
                aiVector3D *nrm = nullptr, *pv = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
304
0
                aiFace *pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
305
0
                mesh->mMaterialIndex = j;
306
307
                // find out which vertex color channels and which texture coordinate
308
                // channels are really required by the material attached to this mesh
309
0
                unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
310
0
                unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
311
312
0
#ifdef ASSIMP_BUILD_DEBUG
313
0
                for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++mui) {
314
0
                    vUVChannelIndices[mui] = UINT_MAX;
315
0
                }
316
0
                for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS; ++mui) {
317
0
                    vVColorIndices[mui] = UINT_MAX;
318
0
                }
319
0
#endif
320
321
0
                FindUVChannels(_mSurfaces[j], sorted, layer, vUVChannelIndices);
322
0
                FindVCChannels(_mSurfaces[j], sorted, layer, vVColorIndices);
323
324
                // allocate storage for UV and CV channels
325
0
                aiVector3D *pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
326
0
                for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++mui) {
327
0
                    if (UINT_MAX == vUVChannelIndices[mui]) {
328
0
                        break;
329
0
                    }
330
331
0
                    pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
332
333
                    // LightWave doesn't support more than 2 UV components (?)
334
0
                    mesh->mNumUVComponents[0] = 2;
335
0
                }
336
337
0
                if (layer.mNormals.name.length()) {
338
0
                    nrm = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
339
0
                }
340
341
0
                aiColor4D *pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
342
0
                for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS; ++mui) {
343
0
                    if (UINT_MAX == vVColorIndices[mui]) {
344
0
                        break;
345
0
                    }
346
0
                    pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
347
0
                }
348
349
                // we would not need this extra array, but the code is much cleaner if we use it
350
0
                std::vector<unsigned int> &smoothingGroups = layer.mPointReferrers;
351
0
                smoothingGroups.erase(smoothingGroups.begin(), smoothingGroups.end());
352
0
                smoothingGroups.resize(mesh->mNumFaces, 0);
353
354
                // now convert all faces
355
0
                unsigned int vert = 0;
356
0
                std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
357
0
                for (it = sorted.begin(); it != end; ++it, ++outIt) {
358
0
                    const LWO::Face &face = layer.mFaces[*it];
359
0
                    *outIt = face.smoothGroup;
360
361
                    // copy all vertices
362
0
                    for (unsigned int q = 0; q < face.mNumIndices; ++q, ++vert) {
363
0
                        unsigned int idx = face.mIndices[q];
364
0
                        *pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
365
366
                        // process UV coordinates
367
0
                        for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++w) {
368
0
                            if (UINT_MAX == vUVChannelIndices[w]) {
369
0
                                break;
370
0
                            }
371
0
                            aiVector3D *&pp = pvUV[w];
372
0
                            const aiVector2D &src = ((aiVector2D *)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
373
0
                            pp->x = src.x;
374
0
                            pp->y = src.y;
375
0
                            pp++;
376
0
                        }
377
378
                        // process normals (MODO extension)
379
0
                        if (nrm) {
380
0
                            *nrm = ((aiVector3D *)&layer.mNormals.rawData[0])[idx];
381
0
                            nrm->z *= -1.f;
382
0
                            ++nrm;
383
0
                        }
384
385
                        // process vertex colors
386
0
                        for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS; ++w) {
387
0
                            if (UINT_MAX == vVColorIndices[w]) {
388
0
                                break;
389
0
                            }
390
0
                            *pvVC[w] = ((aiColor4D *)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
391
392
                            // If a RGB color map is explicitly requested delete the
393
                            // alpha channel - it could theoretically be != 1.
394
0
                            if (_mSurfaces[j].mVCMapType == AI_LWO_RGB)
395
0
                                pvVC[w]->a = 1.f;
396
397
0
                            pvVC[w]++;
398
0
                        }
399
400
0
                        face.mIndices[q] = vert;
401
0
                    }
402
0
                    pf->mIndices = face.mIndices;
403
0
                    pf->mNumIndices = face.mNumIndices;
404
0
                    unsigned int **facePtr = (unsigned int **)&face.mIndices;
405
0
                    *facePtr = nullptr; // HACK: make sure it won't be deleted
406
0
                    pf++;
407
0
                }
408
409
0
                if (!mesh->mNormals) {
410
                    // Compute normal vectors for the mesh - we can't use our GenSmoothNormal-
411
                    // Step here since it wouldn't handle smoothing groups correctly for LWO.
412
                    // So we use a separate implementation.
413
0
                    ComputeNormals(mesh, smoothingGroups, _mSurfaces[j]);
414
0
                } else {
415
0
                    ASSIMP_LOG_VERBOSE_DEBUG("LWO2: No need to compute normals, they're already there");
416
0
                }
417
0
            }
418
0
        }
419
420
        // Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
421
0
        unsigned int num = static_cast<unsigned int>(apcMeshes.size() - meshStart);
422
0
        if (layer.mName != "<LWODefault>" || num > 0) {
423
0
            std::unique_ptr<aiNode> pcNode(new aiNode());
424
0
            pcNode->mName.Set(layer.mName);
425
0
            pcNode->mParent = (aiNode *)&layer;
426
0
            pcNode->mNumMeshes = num;
427
428
0
            if (pcNode->mNumMeshes) {
429
0
                pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
430
0
                for (unsigned int p = 0; p < pcNode->mNumMeshes; ++p)
431
0
                    pcNode->mMeshes[p] = p + meshStart;
432
0
            }
433
0
            ASSIMP_LOG_DEBUG("insert apcNode for layer ", layer.mIndex, " \"", layer.mName, "\"");
434
0
            apcNodes[layer.mIndex] = pcNode.release();
435
0
        }
436
0
    }
437
438
0
    if (apcNodes.empty() || apcMeshes.empty())
439
0
        throw DeadlyImportError("LWO: No meshes loaded");
440
441
    // The RemoveRedundantMaterials step will clean this up later
442
0
    pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
443
444
0
    for (unsigned int mat = 0; mat < pScene->mNumMaterials; ++mat) {
445
0
        aiMaterial *pcMat = new aiMaterial();
446
0
        pScene->mMaterials[mat] = pcMat;
447
0
        ConvertMaterial((*mSurfaces)[mat], pcMat);
448
0
    }
449
450
    // copy the meshes to the output structure
451
0
    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes = (unsigned int)apcMeshes.size()];
452
0
    ::memcpy(pScene->mMeshes, &apcMeshes[0], pScene->mNumMeshes * sizeof(void *));
453
454
    // generate the final node graph
455
0
    GenerateNodeGraph(apcNodes);
456
0
}
457
458
// ------------------------------------------------------------------------------------------------
459
void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &smoothingGroups,
460
0
        const LWO::Surface &surface) {
461
    // Allocate output storage
462
0
    mesh->mNormals = new aiVector3D[mesh->mNumVertices];
463
464
    // First generate per-face normals
465
0
    aiVector3D *out;
466
0
    std::vector<aiVector3D> faceNormals;
467
468
    // ... in some cases that's already enough
469
0
    if (!surface.mMaximumSmoothAngle)
470
0
        out = mesh->mNormals;
471
0
    else {
472
0
        faceNormals.resize(mesh->mNumVertices);
473
0
        out = &faceNormals[0];
474
0
    }
475
476
0
    aiFace *begin = mesh->mFaces, *const end = mesh->mFaces + mesh->mNumFaces;
477
0
    for (; begin != end; ++begin) {
478
0
        aiFace &face = *begin;
479
480
0
        if (face.mNumIndices < 3) {
481
0
            continue;
482
0
        }
483
484
        // LWO doc: "the normal is defined as the cross product of the first and last edges"
485
0
        aiVector3D *pV1 = mesh->mVertices + face.mIndices[0];
486
0
        aiVector3D *pV2 = mesh->mVertices + face.mIndices[1];
487
0
        aiVector3D *pV3 = mesh->mVertices + face.mIndices[face.mNumIndices - 1];
488
489
0
        aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
490
0
        for (unsigned int i = 0; i < face.mNumIndices; ++i)
491
0
            out[face.mIndices[i]] = vNor;
492
0
    }
493
0
    if (!surface.mMaximumSmoothAngle) return;
494
0
    const float posEpsilon = ComputePositionEpsilon(mesh);
495
496
    // Now generate the spatial sort tree
497
0
    SGSpatialSort sSort;
498
0
    std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
499
0
    for (begin = mesh->mFaces; begin != end; ++begin, ++it) {
500
0
        aiFace &face = *begin;
501
0
        for (unsigned int i = 0; i < face.mNumIndices; ++i) {
502
0
            unsigned int tt = face.mIndices[i];
503
0
            sSort.Add(mesh->mVertices[tt], tt, *it);
504
0
        }
505
0
    }
506
    // Sort everything - this takes O(nlogn) time
507
0
    sSort.Prepare();
508
0
    std::vector<unsigned int> poResult;
509
0
    poResult.reserve(20);
510
511
    // Generate vertex normals. We have O(logn) for the binary lookup, which we need
512
    // for n elements, thus the EXPECTED complexity is O(nlogn)
513
0
    if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) {
514
0
        const float fLimit = std::cos(surface.mMaximumSmoothAngle);
515
516
0
        for (begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
517
0
            const aiFace &face = *begin;
518
0
            unsigned int *beginIdx = face.mIndices, *const endIdx = face.mIndices + face.mNumIndices;
519
0
            for (; beginIdx != endIdx; ++beginIdx) {
520
0
                unsigned int idx = *beginIdx;
521
0
                sSort.FindPositions(mesh->mVertices[idx], *it, posEpsilon, poResult, true);
522
523
0
                aiVector3D vNormals;
524
0
                 for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
525
0
                    const aiVector3D &v = faceNormals[*a];
526
0
                    if (v * faceNormals[idx] < fLimit)
527
0
                        continue;
528
0
                    vNormals += v;
529
0
                }
530
0
                mesh->mNormals[idx] = vNormals.Normalize();
531
0
            }
532
0
        }
533
0
    }
534
    // faster code path in case there is no smooth angle
535
0
    else {
536
0
        std::vector<bool> vertexDone(mesh->mNumVertices, false);
537
0
        for (begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
538
0
            const aiFace &face = *begin;
539
0
            unsigned int *beginIdx = face.mIndices, *const endIdx = face.mIndices + face.mNumIndices;
540
0
            for (; beginIdx != endIdx; ++beginIdx) {
541
0
                unsigned int idx = *beginIdx;
542
0
                if (vertexDone[idx])
543
0
                    continue;
544
0
                sSort.FindPositions(mesh->mVertices[idx], *it, posEpsilon, poResult, true);
545
546
0
                aiVector3D vNormals;
547
0
                 for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
548
0
                    const aiVector3D &v = faceNormals[*a];
549
0
                    vNormals += v;
550
0
                }
551
0
                vNormals.Normalize();
552
0
                for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
553
0
                    mesh->mNormals[*a] = vNormals;
554
0
                    vertexDone[*a] = true;
555
0
                }
556
0
            }
557
0
        }
558
0
    }
559
0
    GeometryUtils::normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
560
0
}
561
562
// ------------------------------------------------------------------------------------------------
563
0
void LWOImporter::GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes) {
564
    // now generate the final nodegraph - generate a root node and attach children
565
0
    aiNode *root = mScene->mRootNode = new aiNode();
566
0
    root->mName.Set("<LWORoot>");
567
568
0
    ASSIMP_LOG_DEBUG("apcNodes initial size: ", apcNodes.size());
569
0
    if (!apcNodes.empty()) {
570
0
        ASSIMP_LOG_DEBUG("first apcNode is: ", apcNodes.begin()->first, " \"", apcNodes.begin()->second->mName.C_Str(), "\"");
571
0
    }
572
573
    //Set parent of all children, inserting pivots
574
0
    {
575
0
        std::map<uint16_t, aiNode *> mapPivot;
576
0
        for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
577
578
            //Get the parent index
579
0
            LWO::Layer *nodeLayer = (LWO::Layer *)(itapcNodes->second->mParent);
580
0
            uint16_t parentIndex = nodeLayer->mParent;
581
582
            //Create pivot node, store it into the pivot map, and set the parent as the pivot
583
0
            std::unique_ptr<aiNode> pivotNode(new aiNode());
584
0
            pivotNode->mName.Set("Pivot-" + std::string(itapcNodes->second->mName.data));
585
0
            itapcNodes->second->mParent = pivotNode.get();
586
587
            //Look for the parent node to attach the pivot to
588
0
            if (apcNodes.find(parentIndex) != apcNodes.end()) {
589
0
                pivotNode->mParent = apcNodes[parentIndex];
590
0
            } else {
591
                //If not, attach to the root node
592
0
                pivotNode->mParent = root;
593
0
            }
594
595
            //Set the node and the pivot node transformation
596
0
            itapcNodes->second->mTransformation.a4 = -nodeLayer->mPivot.x;
597
0
            itapcNodes->second->mTransformation.b4 = -nodeLayer->mPivot.y;
598
0
            itapcNodes->second->mTransformation.c4 = -nodeLayer->mPivot.z;
599
0
            pivotNode->mTransformation.a4 = nodeLayer->mPivot.x;
600
0
            pivotNode->mTransformation.b4 = nodeLayer->mPivot.y;
601
0
            pivotNode->mTransformation.c4 = nodeLayer->mPivot.z;
602
0
            uint16_t pivotNodeId = static_cast<uint16_t>(-(itapcNodes->first + 2));
603
0
            ASSIMP_LOG_DEBUG("insert pivot node: ", pivotNodeId);
604
0
            auto oldNodeIt = mapPivot.find(pivotNodeId);
605
0
            if (oldNodeIt != mapPivot.end()) {
606
0
                ASSIMP_LOG_ERROR("attempted to insert pivot node which already exists in pivot map ", pivotNodeId, " \"", pivotNode->mName.C_Str(), "\"");
607
0
            } else {
608
0
                mapPivot.emplace(pivotNodeId, pivotNode.release());
609
0
            }
610
0
        }
611
612
0
        ASSIMP_LOG_DEBUG("pivot nodes: ", mapPivot.size());
613
        //Merge pivot map into node map
614
0
        for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end();) {
615
0
            uint16_t pivotNodeId = itMapPivot->first;
616
0
            auto oldApcNodeIt = apcNodes.find(pivotNodeId);
617
0
            if (oldApcNodeIt != apcNodes.end()) {
618
0
                ASSIMP_LOG_ERROR("attempted to insert pivot node which already exists in apc nodes ", pivotNodeId, " \"", itMapPivot->second->mName.C_Str(), "\"");
619
0
            } else {
620
0
                apcNodes.emplace(pivotNodeId, itMapPivot->second);
621
0
            }
622
0
            itMapPivot->second = nullptr;
623
0
            itMapPivot = mapPivot.erase(itMapPivot);
624
0
        }
625
0
        ASSIMP_LOG_DEBUG("total nodes: ", apcNodes.size());
626
0
    }
627
628
    //Set children of all parents
629
0
    apcNodes[(uint16_t)-1] = root;
630
0
    for (auto itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) {
631
0
        for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
632
0
            if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
633
0
                ++(itMapParentNodes->second->mNumChildren);
634
0
            }
635
0
        }
636
0
        if (itMapParentNodes->second->mNumChildren) {
637
0
            itMapParentNodes->second->mChildren = new aiNode *[itMapParentNodes->second->mNumChildren];
638
0
            uint16_t p = 0;
639
0
            for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
640
0
                if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
641
0
                    itMapParentNodes->second->mChildren[p++] = itMapChildNodes->second;
642
0
                }
643
0
            }
644
0
        }
645
0
    }
646
647
0
    if (!mScene->mRootNode->mNumChildren) {
648
0
        ASSIMP_LOG_DEBUG("All apcNodes:");
649
0
        for (auto nodeIt = apcNodes.begin(); nodeIt != apcNodes.end(); ) {
650
0
            ASSIMP_LOG_DEBUG("Node ", nodeIt->first, " \"", nodeIt->second->mName.C_Str(), "\"");
651
0
            nodeIt->second = nullptr;
652
0
            nodeIt = apcNodes.erase(nodeIt);
653
0
        }
654
0
        throw DeadlyImportError("LWO: Unable to build a valid node graph");
655
0
    }
656
657
    // Remove a single root node with no meshes assigned to it ...
658
0
    if (1 == mScene->mRootNode->mNumChildren) {
659
0
        aiNode *pc = mScene->mRootNode->mChildren[0];
660
0
        pc->mParent = mScene->mRootNode->mChildren[0] = nullptr;
661
0
        delete mScene->mRootNode;
662
0
        mScene->mRootNode = pc;
663
0
    }
664
665
    // convert the whole stuff to RH with CCW winding
666
0
    MakeLeftHandedProcess maker;
667
0
    maker.Execute(mScene);
668
669
0
    FlipWindingOrderProcess flipper;
670
0
    flipper.Execute(mScene);
671
0
}
672
673
// ------------------------------------------------------------------------------------------------
674
0
void LWOImporter::ResolveTags() {
675
    // --- this function is used for both LWO2 and LWOB
676
0
    mMapping->resize(mTags->size(), UINT_MAX);
677
0
    for (unsigned int a = 0; a < mTags->size(); ++a) {
678
679
0
        const std::string &c = (*mTags)[a];
680
0
        for (unsigned int i = 0; i < mSurfaces->size(); ++i) {
681
682
0
            const std::string &d = (*mSurfaces)[i].mName;
683
0
            if (!ASSIMP_stricmp(c, d)) {
684
685
0
                (*mMapping)[a] = i;
686
0
                break;
687
0
            }
688
0
        }
689
0
    }
690
0
}
691
692
// ------------------------------------------------------------------------------------------------
693
0
void LWOImporter::ResolveClips() {
694
0
    for (unsigned int i = 0; i < mClips.size(); ++i) {
695
696
0
        Clip &clip = mClips[i];
697
0
        if (Clip::REF == clip.type) {
698
699
0
            if (clip.clipRef >= mClips.size()) {
700
0
                ASSIMP_LOG_ERROR("LWO2: Clip referrer index is out of range");
701
0
                clip.clipRef = 0;
702
0
            }
703
704
0
            Clip &dest = mClips[clip.clipRef];
705
0
            if (Clip::REF == dest.type) {
706
0
                ASSIMP_LOG_ERROR("LWO2: Clip references another clip reference");
707
0
                clip.type = Clip::UNSUPPORTED;
708
0
            }
709
710
0
            else {
711
0
                clip.path = dest.path;
712
0
                clip.type = dest.type;
713
0
            }
714
0
        }
715
0
    }
716
0
}
717
718
// ------------------------------------------------------------------------------------------------
719
0
void LWOImporter::AdjustTexturePath(std::string &out) {
720
    // --- this function is used for both LWO2 and LWOB
721
0
    if (!mIsLWO2 && !mIsLWO3 && ::strstr(out.c_str(), "(sequence)")) {
722
723
        // remove the (sequence) and append 000
724
0
        ASSIMP_LOG_INFO("LWOB: Sequence of animated texture found. It will be ignored");
725
0
        out = out.substr(0, out.length() - 10) + "000";
726
0
    }
727
728
    // format: drive:path/file - we just need to insert a slash after the drive
729
0
    std::string::size_type n = out.find_first_of(':');
730
0
    if (std::string::npos != n) {
731
0
        out.insert(n + 1, "/");
732
0
    }
733
0
}
734
735
// ------------------------------------------------------------------------------------------------
736
0
void LWOImporter::LoadLWOTags(unsigned int size) {
737
    // --- this function is used for both LWO2 and LWOB
738
739
0
    const char *szCur = (const char *)mFileBuffer, *szLast = szCur;
740
0
    const char *const szEnd = szLast + size;
741
0
    while (szCur < szEnd) {
742
0
        if (!(*szCur)) {
743
0
            const size_t len = (size_t)(szCur - szLast);
744
            // FIX: skip empty-sized tags
745
0
            if (len)
746
0
                mTags->push_back(std::string(szLast, len));
747
0
            szCur += (len & 0x1 ? 1 : 2);
748
0
            szLast = szCur;
749
0
        }
750
0
        szCur++;
751
0
    }
752
0
}
753
754
// ------------------------------------------------------------------------------------------------
755
0
void LWOImporter::LoadLWOPoints(unsigned int length) {
756
    // --- this function is used for both LWO2 and LWOB but for
757
    // LWO2 we need to allocate 25% more storage - it could be we'll
758
    // need to duplicate some points later.
759
0
    const size_t vertexLen = 12;
760
0
    if ((length % vertexLen) != 0) {
761
0
        throw DeadlyImportError("LWO2: Points chunk length is not multiple of vertexLen (12)");
762
0
    }
763
0
    unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
764
0
    if (mIsLWO2 || mIsLWO3) {
765
0
        mCurLayer->mTempPoints.reserve(regularSize + (regularSize >> 2u));
766
0
        mCurLayer->mTempPoints.resize(regularSize);
767
768
        // initialize all point referrers with the default values
769
0
        mCurLayer->mPointReferrers.reserve(regularSize + (regularSize >> 2u));
770
0
        mCurLayer->mPointReferrers.resize(regularSize, UINT_MAX);
771
0
    } else
772
0
        mCurLayer->mTempPoints.resize(regularSize);
773
774
        // perform endianness conversions
775
0
#ifndef AI_BUILD_BIG_ENDIAN
776
0
    for (unsigned int i = 0; i<length >> 2; ++i)
777
0
        ByteSwap::Swap4(mFileBuffer + (i << 2));
778
0
#endif
779
0
    ::memcpy(&mCurLayer->mTempPoints[0], mFileBuffer, length);
780
0
}
781
782
// ------------------------------------------------------------------------------------------------
783
0
void LWOImporter::LoadLWO2Polygons(unsigned int length) {
784
0
    LE_NCONST uint16_t *const end = (LE_NCONST uint16_t *)(mFileBuffer + length);
785
0
    const uint32_t type = GetU4();
786
787
    // Determine the type of the polygons
788
0
    switch (type) {
789
            // read unsupported stuff too (although we won't process it)
790
0
        case AI_LWO_MBAL:
791
0
            ASSIMP_LOG_WARN("LWO2: Encountered unsupported primitive chunk (METABALL)");
792
0
            break;
793
0
        case AI_LWO_CURV:
794
0
            ASSIMP_LOG_WARN("LWO2: Encountered unsupported primitive chunk (SPLINE)");
795
0
            ;
796
0
            break;
797
798
            // These are ok with no restrictions
799
0
        case AI_LWO_PTCH:
800
0
        case AI_LWO_FACE:
801
0
        case AI_LWO_BONE:
802
0
        case AI_LWO_SUBD:
803
0
            break;
804
0
        default:
805
806
            // hm!? wtf is this? ok ...
807
0
            ASSIMP_LOG_ERROR("LWO2: Ignoring unknown polygon type.");
808
0
            break;
809
0
    }
810
811
    // first find out how many faces and vertices we'll finally need
812
0
    uint16_t *cursor = (uint16_t *)mFileBuffer;
813
814
0
    unsigned int iNumFaces = 0, iNumVertices = 0;
815
0
    CountVertsAndFacesLWO2(iNumVertices, iNumFaces, cursor, end);
816
817
    // allocate the output array and copy face indices
818
0
    if (iNumFaces) {
819
0
        cursor = (uint16_t *)mFileBuffer;
820
821
0
        mCurLayer->mFaces.resize(iNumFaces, LWO::Face(type));
822
0
        FaceList::iterator it = mCurLayer->mFaces.begin();
823
0
        CopyFaceIndicesLWO2(it, cursor, end);
824
0
    }
825
0
}
826
827
// ------------------------------------------------------------------------------------------------
828
void LWOImporter::CountVertsAndFacesLWO2(unsigned int &verts, unsigned int &faces,
829
0
        uint16_t *&cursor, const uint16_t *const end, unsigned int max) {
830
0
    while (cursor < end && max--) {
831
0
        uint16_t numIndices;
832
0
        ::memcpy(&numIndices, cursor++, 2);
833
0
        AI_LSWAP2(numIndices);
834
0
        numIndices &= 0x03FF;
835
836
0
        verts += numIndices;
837
0
        ++faces;
838
839
0
        for (uint16_t i = 0; i < numIndices; i++) {
840
0
            ReadVSizedIntLWO2((uint8_t *&)cursor);
841
0
        }
842
0
    }
843
0
}
844
845
// ------------------------------------------------------------------------------------------------
846
void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator &it,
847
        uint16_t *&cursor,
848
0
        const uint16_t *const end) {
849
0
    while (cursor < end) {
850
0
        LWO::Face &face = *it++;
851
0
        uint16_t numIndices;
852
0
        ::memcpy(&numIndices, cursor++, 2);
853
0
        AI_LSWAP2(numIndices);
854
0
        face.mNumIndices = numIndices & 0x03FF;
855
856
0
        if (face.mNumIndices) /* byte swapping has already been done */
857
0
        {
858
0
            face.mIndices = new unsigned int[face.mNumIndices];
859
0
            for (unsigned int i = 0; i < face.mNumIndices; i++) {
860
0
                face.mIndices[i] = ReadVSizedIntLWO2((uint8_t *&)cursor) + mCurLayer->mPointIDXOfs;
861
0
                if (face.mIndices[i] > mCurLayer->mTempPoints.size()) {
862
0
                    ASSIMP_LOG_WARN("LWO2: Failure evaluating face record, index is out of range");
863
0
                    face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size() - 1;
864
0
                }
865
0
            }
866
0
        } else
867
0
            throw DeadlyImportError("LWO2: Encountered invalid face record with zero indices");
868
0
    }
869
0
}
870
871
// ------------------------------------------------------------------------------------------------
872
0
void LWOImporter::LoadLWO2PolygonTags(unsigned int length) {
873
0
    LE_NCONST uint8_t *const end = mFileBuffer + length;
874
875
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, PTAG, 4);
876
0
    uint32_t type = GetU4();
877
878
0
    if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
879
0
        return;
880
881
0
    while (mFileBuffer < end) {
882
0
        unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
883
0
        unsigned int j = GetU2();
884
885
0
        if (i >= mCurLayer->mFaces.size()) {
886
0
            ASSIMP_LOG_WARN("LWO2: face index in PTAG is out of range");
887
0
            continue;
888
0
        }
889
890
0
        switch (type) {
891
892
0
            case AI_LWO_SURF:
893
0
                mCurLayer->mFaces[i].surfaceIndex = j;
894
0
                break;
895
0
            case AI_LWO_SMGP: /* is that really used? */
896
0
                mCurLayer->mFaces[i].smoothGroup = j;
897
0
                break;
898
0
        };
899
0
    }
900
0
}
901
902
// ------------------------------------------------------------------------------------------------
903
template <class T>
904
0
VMapEntry *FindEntry(std::vector<T> &list, const std::string &name, bool perPoly) {
905
0
    for (auto &elem : list) {
906
0
        if (elem.name == name) {
907
0
            if (!perPoly) {
908
0
                ASSIMP_LOG_WARN("LWO2: Found two VMAP sections with equal names");
909
0
            }
910
0
            return &elem;
911
0
        }
912
0
    }
913
0
    list.push_back(T());
914
0
    VMapEntry *p = &list.back();
915
0
    p->name = name;
916
0
    return p;
917
0
}
Unexecuted instantiation: Assimp::LWO::VMapEntry* FindEntry<Assimp::LWO::UVChannel>(std::__1::vector<Assimp::LWO::UVChannel, std::__1::allocator<Assimp::LWO::UVChannel> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
Unexecuted instantiation: Assimp::LWO::VMapEntry* FindEntry<Assimp::LWO::WeightChannel>(std::__1::vector<Assimp::LWO::WeightChannel, std::__1::allocator<Assimp::LWO::WeightChannel> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
Unexecuted instantiation: Assimp::LWO::VMapEntry* FindEntry<Assimp::LWO::VColorChannel>(std::__1::vector<Assimp::LWO::VColorChannel, std::__1::allocator<Assimp::LWO::VColorChannel> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
918
919
// ------------------------------------------------------------------------------------------------
920
template <class T>
921
0
inline void CreateNewEntry(T &chan, unsigned int srcIdx) {
922
0
    if (!chan.name.length())
923
0
        return;
924
925
0
    chan.abAssigned[srcIdx] = true;
926
0
    chan.abAssigned.resize(chan.abAssigned.size() + 1, false);
927
928
0
    for (unsigned int a = 0; a < chan.dims; ++a)
929
0
        chan.rawData.push_back(chan.rawData[srcIdx * chan.dims + a]);
930
0
}
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::VColorChannel>(Assimp::LWO::VColorChannel&, unsigned int)
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::UVChannel>(Assimp::LWO::UVChannel&, unsigned int)
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::WeightChannel>(Assimp::LWO::WeightChannel&, unsigned int)
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::NormalChannel>(Assimp::LWO::NormalChannel&, unsigned int)
931
932
// ------------------------------------------------------------------------------------------------
933
template <class T>
934
0
inline void CreateNewEntry(std::vector<T> &list, unsigned int srcIdx) {
935
0
    for (auto &elem : list) {
936
0
        CreateNewEntry(elem, srcIdx);
937
0
    }
938
0
}
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::VColorChannel>(std::__1::vector<Assimp::LWO::VColorChannel, std::__1::allocator<Assimp::LWO::VColorChannel> >&, unsigned int)
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::UVChannel>(std::__1::vector<Assimp::LWO::UVChannel, std::__1::allocator<Assimp::LWO::UVChannel> >&, unsigned int)
Unexecuted instantiation: void CreateNewEntry<Assimp::LWO::WeightChannel>(std::__1::vector<Assimp::LWO::WeightChannel, std::__1::allocator<Assimp::LWO::WeightChannel> >&, unsigned int)
939
940
// ------------------------------------------------------------------------------------------------
941
inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry *base, unsigned int numRead,
942
0
        unsigned int idx, float *data) {
943
0
    ai_assert(nullptr != data);
944
0
    LWO::ReferrerList &refList = mCurLayer->mPointReferrers;
945
0
    unsigned int i;
946
947
0
    if (idx >= base->abAssigned.size()) {
948
0
        throw DeadlyImportError("Bad index");
949
0
    }
950
0
    base->abAssigned[idx] = true;
951
0
    for (i = 0; i < numRead; ++i) {
952
0
        base->rawData[idx * base->dims + i] = data[i];
953
0
    }
954
955
0
    if (UINT_MAX != (i = refList[idx])) {
956
0
        DoRecursiveVMAPAssignment(base, numRead, i, data);
957
0
    }
958
0
}
959
960
// ------------------------------------------------------------------------------------------------
961
0
inline void AddToSingleLinkedList(ReferrerList &refList, unsigned int srcIdx, unsigned int destIdx) {
962
0
    if (UINT_MAX == refList[srcIdx]) {
963
0
        refList[srcIdx] = destIdx;
964
0
        return;
965
0
    }
966
0
    AddToSingleLinkedList(refList, refList[srcIdx], destIdx);
967
0
}
968
969
// ------------------------------------------------------------------------------------------------
970
// Load LWO2 vertex map
971
0
void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) {
972
0
    LE_NCONST uint8_t *const end = mFileBuffer + length;
973
974
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, VMAP, 6);
975
0
    unsigned int type = GetU4();
976
0
    unsigned int dims = GetU2();
977
978
0
    VMapEntry *base;
979
980
    // read the name of the vertex map
981
0
    std::string name;
982
0
    GetS0(name, length);
983
984
0
    switch (type) {
985
0
        case AI_LWO_TXUV:
986
0
            if (dims != 2) {
987
0
                ASSIMP_LOG_WARN("LWO2: Skipping UV channel \'", name, "\' with !2 components");
988
0
                return;
989
0
            }
990
0
            base = FindEntry(mCurLayer->mUVChannels, name, perPoly);
991
0
            break;
992
0
        case AI_LWO_WGHT:
993
0
        case AI_LWO_MNVW:
994
0
            if (dims != 1) {
995
0
                ASSIMP_LOG_WARN("LWO2: Skipping Weight Channel \'", name, "\' with !1 components");
996
0
                return;
997
0
            }
998
0
            base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels : mCurLayer->mSWeightChannels), name, perPoly);
999
0
            break;
1000
0
        case AI_LWO_RGB:
1001
0
        case AI_LWO_RGBA:
1002
0
            if (dims != 3 && dims != 4) {
1003
0
                ASSIMP_LOG_WARN("LWO2: Skipping Color Map \'", name, "\' with a dimension > 4 or < 3");
1004
0
                return;
1005
0
            }
1006
0
            base = FindEntry(mCurLayer->mVColorChannels, name, perPoly);
1007
0
            break;
1008
1009
0
        case AI_LWO_MODO_NORM:
1010
            /*  This is a non-standard extension chunk used by Luxology's MODO.
1011
         *  It stores per-vertex normals. This VMAP exists just once, has
1012
         *  3 dimensions and is btw extremely beautiful.
1013
         */
1014
0
            if (name != "vert_normals" || dims != 3 || mCurLayer->mNormals.name.length())
1015
0
                return;
1016
1017
0
            ASSIMP_LOG_INFO("Processing non-standard extension: MODO VMAP.NORM.vert_normals");
1018
1019
0
            mCurLayer->mNormals.name = name;
1020
0
            base = &mCurLayer->mNormals;
1021
0
            break;
1022
1023
0
        case AI_LWO_PICK: /* these VMAPs are just silently dropped */
1024
0
        case AI_LWO_MORF:
1025
0
        case AI_LWO_SPOT:
1026
0
            return;
1027
1028
0
        default:
1029
0
            if (name == "APS.Level") {
1030
                // XXX handle this (seems to be subdivision-related).
1031
0
            }
1032
0
            ASSIMP_LOG_WARN("LWO2: Skipping unknown VMAP/VMAD channel \'", name, "\'");
1033
0
            return;
1034
0
    };
1035
0
    base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
1036
1037
    // now read all entries in the map
1038
0
    type = std::min(dims, base->dims);
1039
0
    const unsigned int diff = (dims - type) << 2u;
1040
1041
0
    LWO::FaceList &list = mCurLayer->mFaces;
1042
0
    LWO::PointList &pointList = mCurLayer->mTempPoints;
1043
0
    LWO::ReferrerList &refList = mCurLayer->mPointReferrers;
1044
1045
0
    const unsigned int numPoints = (unsigned int)pointList.size();
1046
0
    const unsigned int numFaces = (unsigned int)list.size();
1047
1048
0
    while (mFileBuffer < end) {
1049
1050
0
        unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
1051
0
        if (idx >= numPoints) {
1052
0
            ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAP/VMAD entry \'", name, "\', vertex index is out of range");
1053
0
            mFileBuffer += base->dims << 2u;
1054
0
            continue;
1055
0
        }
1056
0
        if (perPoly) {
1057
0
            unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
1058
0
            if (base->abAssigned[idx]) {
1059
                // we have already a VMAP entry for this vertex - thus
1060
                // we need to duplicate the corresponding polygon.
1061
0
                if (polyIdx >= numFaces) {
1062
0
                    ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAD entry \'", name, "\', polygon index is out of range");
1063
0
                    mFileBuffer += base->dims << 2u;
1064
0
                    continue;
1065
0
                }
1066
1067
0
                LWO::Face &src = list[polyIdx];
1068
1069
                // generate a new unique vertex for the corresponding index - but only
1070
                // if we can find the index in the face
1071
0
                bool had = false;
1072
0
                for (unsigned int i = 0; i < src.mNumIndices; ++i) {
1073
1074
0
                    unsigned int srcIdx = src.mIndices[i], tmp = idx;
1075
0
                    do {
1076
0
                        if (tmp == srcIdx)
1077
0
                            break;
1078
0
                    } while ((tmp = refList[tmp]) != UINT_MAX);
1079
0
                    if (tmp == UINT_MAX) {
1080
0
                        continue;
1081
0
                    }
1082
1083
0
                    had = true;
1084
0
                    refList.resize(refList.size() + 1, UINT_MAX);
1085
1086
0
                    idx = (unsigned int)pointList.size();
1087
0
                    src.mIndices[i] = (unsigned int)pointList.size();
1088
1089
                    // store the index of the new vertex in the old vertex
1090
                    // so we get a single linked list we can traverse in
1091
                    // only one direction
1092
0
                    AddToSingleLinkedList(refList, srcIdx, src.mIndices[i]);
1093
0
                    pointList.push_back(pointList[srcIdx]);
1094
1095
0
                    CreateNewEntry(mCurLayer->mVColorChannels, srcIdx);
1096
0
                    CreateNewEntry(mCurLayer->mUVChannels, srcIdx);
1097
0
                    CreateNewEntry(mCurLayer->mWeightChannels, srcIdx);
1098
0
                    CreateNewEntry(mCurLayer->mSWeightChannels, srcIdx);
1099
0
                    CreateNewEntry(mCurLayer->mNormals, srcIdx);
1100
0
                }
1101
0
                if (!had) {
1102
0
                    ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAD entry \'", name, "\', vertex index wasn't found in that polygon");
1103
0
                    ai_assert(had);
1104
0
                }
1105
0
            }
1106
0
        }
1107
1108
0
        std::unique_ptr<float[]> temp(new float[type]);
1109
0
        for (unsigned int l = 0; l < type; ++l)
1110
0
            temp[l] = GetF4();
1111
1112
0
        DoRecursiveVMAPAssignment(base, type, idx, temp.get());
1113
0
        mFileBuffer += diff;
1114
0
    }
1115
0
}
1116
1117
// ------------------------------------------------------------------------------------------------
1118
// Load LWO2 clip
1119
0
void LWOImporter::LoadLWO2Clip(unsigned int length) {
1120
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, CLIP, 10);
1121
1122
0
    mClips.emplace_back();
1123
0
    LWO::Clip &clip = mClips.back();
1124
1125
    // first - get the index of the clip
1126
0
    clip.idx = GetU4();
1127
1128
0
    IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
1129
0
    switch (head.type) {
1130
0
        case AI_LWO_STIL:
1131
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, STIL, 1);
1132
1133
            // "Normal" texture
1134
0
            GetS0(clip.path, head.length);
1135
0
            clip.type = Clip::STILL;
1136
0
            break;
1137
1138
0
        case AI_LWO_ISEQ:
1139
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ISEQ, 16);
1140
            // Image sequence. We'll later take the first.
1141
0
            {
1142
0
                uint8_t digits = GetU1();
1143
0
                mFileBuffer++;
1144
0
                int16_t offset = GetU2();
1145
0
                mFileBuffer += 4;
1146
0
                int16_t start = GetU2();
1147
0
                mFileBuffer += 4;
1148
1149
0
                std::string s;
1150
0
                std::ostringstream ss;
1151
0
                GetS0(s, head.length);
1152
1153
0
                head.length -= (uint16_t)s.length() + 1;
1154
0
                ss << s;
1155
0
                ss << std::setw(digits) << offset + start;
1156
0
                GetS0(s, head.length);
1157
0
                ss << s;
1158
0
                clip.path = ss.str();
1159
0
                clip.type = Clip::SEQ;
1160
0
            }
1161
0
            break;
1162
1163
0
        case AI_LWO_STCC:
1164
0
            ASSIMP_LOG_WARN("LWO2: Color shifted images are not supported");
1165
0
            break;
1166
1167
0
        case AI_LWO_ANIM:
1168
0
            ASSIMP_LOG_WARN("LWO2: Animated textures are not supported");
1169
0
            break;
1170
1171
0
        case AI_LWO_XREF:
1172
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, XREF, 4);
1173
1174
            // Just a cross-reference to another CLIp
1175
0
            clip.type = Clip::REF;
1176
0
            clip.clipRef = GetU4();
1177
0
            break;
1178
1179
0
        case AI_LWO_NEGA:
1180
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, NEGA, 2);
1181
0
            clip.negate = (0 != GetU2());
1182
0
            break;
1183
1184
0
        default:
1185
0
            ASSIMP_LOG_WARN("LWO2: Encountered unknown CLIP sub-chunk");
1186
0
    }
1187
0
}
1188
1189
0
void LWOImporter::LoadLWO3Clip(unsigned int length) {
1190
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, CLIP, 12);
1191
1192
0
    mClips.emplace_back();
1193
0
    LWO::Clip &clip = mClips.back();
1194
1195
    // first - get the index of the clip
1196
0
    clip.idx = GetU4();
1197
1198
0
    IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
1199
0
    switch (head.type) {
1200
0
        case AI_LWO_STIL:
1201
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, STIL, 1);
1202
1203
            // "Normal" texture
1204
0
            GetS0(clip.path, head.length);
1205
0
            clip.type = Clip::STILL;
1206
0
            break;
1207
1208
0
        case AI_LWO_ISEQ:
1209
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ISEQ, 16);
1210
            // Image sequence. We'll later take the first.
1211
0
            {
1212
0
                uint8_t digits = GetU1();
1213
0
                mFileBuffer++;
1214
0
                int16_t offset = GetU2();
1215
0
                mFileBuffer += 4;
1216
0
                int16_t start = GetU2();
1217
0
                mFileBuffer += 4;
1218
1219
0
                std::string s;
1220
0
                std::ostringstream ss;
1221
0
                GetS0(s, head.length);
1222
1223
0
                head.length -= (uint16_t)s.length() + 1;
1224
0
                ss << s;
1225
0
                ss << std::setw(digits) << offset + start;
1226
0
                GetS0(s, head.length);
1227
0
                ss << s;
1228
0
                clip.path = ss.str();
1229
0
                clip.type = Clip::SEQ;
1230
0
            }
1231
0
            break;
1232
1233
0
        case AI_LWO_STCC:
1234
0
            ASSIMP_LOG_WARN("LWO3: Color shifted images are not supported");
1235
0
            break;
1236
1237
0
        case AI_LWO_ANIM:
1238
0
            ASSIMP_LOG_WARN("LWO3: Animated textures are not supported");
1239
0
            break;
1240
1241
0
        case AI_LWO_XREF:
1242
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, XREF, 4);
1243
1244
            // Just a cross-reference to another CLIp
1245
0
            clip.type = Clip::REF;
1246
0
            clip.clipRef = GetU4();
1247
0
            break;
1248
1249
0
        case AI_LWO_NEGA:
1250
0
            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, NEGA, 2);
1251
0
            clip.negate = (0 != GetU2());
1252
0
            break;
1253
1254
0
        default:
1255
0
            ASSIMP_LOG_WARN("LWO3: Encountered unknown CLIP sub-chunk");
1256
0
    }
1257
0
}
1258
1259
// ------------------------------------------------------------------------------------------------
1260
// Load envelope description
1261
0
void LWOImporter::LoadLWO2Envelope(unsigned int length) {
1262
0
    LE_NCONST uint8_t *const end = mFileBuffer + length;
1263
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, ENVL, 4);
1264
1265
0
    mEnvelopes.emplace_back();
1266
0
    LWO::Envelope &envelope = mEnvelopes.back();
1267
1268
    // Get the index of the envelope
1269
0
    envelope.index = ReadVSizedIntLWO2(mFileBuffer);
1270
1271
    // It looks like there might be an extra U4 right after the index,
1272
    // at least in modo (LXOB) files: we'll ignore it if it's zero,
1273
    // otherwise it represents the start of a subchunk, so we backtrack.
1274
0
    if (mIsLXOB) {
1275
0
        uint32_t extra = GetU4();
1276
0
        if (extra) {
1277
0
            mFileBuffer -= 4;
1278
0
        }
1279
0
    }
1280
1281
    // ... and read all subchunks
1282
0
    while (true) {
1283
0
        if (mFileBuffer + 6 >= end) break;
1284
0
        LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
1285
1286
0
        if (mFileBuffer + head.length > end)
1287
0
            throw DeadlyImportError("LWO2: Invalid envelope chunk length");
1288
1289
0
        uint8_t *const next = mFileBuffer + head.length;
1290
0
        switch (head.type) {
1291
                // Type & representation of the envelope
1292
0
            case AI_LWO_TYPE:
1293
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TYPE, 2);
1294
0
                mFileBuffer++; // skip user format
1295
1296
                // Determine type of envelope
1297
0
                envelope.type = (LWO::EnvelopeType)*mFileBuffer;
1298
0
                ++mFileBuffer;
1299
0
                break;
1300
1301
                // precondition
1302
0
            case AI_LWO_PRE:
1303
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, PRE, 2);
1304
0
                envelope.pre = (LWO::PrePostBehaviour)GetU2();
1305
0
                break;
1306
1307
                // postcondition
1308
0
            case AI_LWO_POST:
1309
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, POST, 2);
1310
0
                envelope.post = (LWO::PrePostBehaviour)GetU2();
1311
0
                break;
1312
1313
                // keyframe
1314
0
            case AI_LWO_KEY: {
1315
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, KEY, 8);
1316
1317
0
                envelope.keys.emplace_back();
1318
0
                LWO::Key &key = envelope.keys.back();
1319
1320
0
                key.time = GetF4();
1321
0
                key.value = GetF4();
1322
0
                break;
1323
0
            }
1324
1325
                // interval interpolation
1326
0
            case AI_LWO_SPAN: {
1327
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPAN, 4);
1328
0
                if (envelope.keys.size() < 2)
1329
0
                    ASSIMP_LOG_WARN("LWO2: Unexpected SPAN chunk");
1330
0
                else {
1331
0
                    LWO::Key &key = envelope.keys.back();
1332
0
                    switch (GetU4()) {
1333
0
                        case AI_LWO_STEP:
1334
0
                            key.inter = LWO::IT_STEP;
1335
0
                            break;
1336
0
                        case AI_LWO_LINE:
1337
0
                            key.inter = LWO::IT_LINE;
1338
0
                            break;
1339
0
                        case AI_LWO_TCB:
1340
0
                            key.inter = LWO::IT_TCB;
1341
0
                            break;
1342
0
                        case AI_LWO_HERM:
1343
0
                            key.inter = LWO::IT_HERM;
1344
0
                            break;
1345
0
                        case AI_LWO_BEZI:
1346
0
                            key.inter = LWO::IT_BEZI;
1347
0
                            break;
1348
0
                        case AI_LWO_BEZ2:
1349
0
                            key.inter = LWO::IT_BEZ2;
1350
0
                            break;
1351
0
                        default:
1352
0
                            ASSIMP_LOG_WARN("LWO2: Unknown interval interpolation mode");
1353
0
                    };
1354
1355
                    // todo ... read params
1356
0
                }
1357
0
                break;
1358
0
            }
1359
1360
0
            default:
1361
0
                ASSIMP_LOG_WARN("LWO2: Encountered unknown ENVL subchunk");
1362
0
                break;
1363
0
        }
1364
        // regardless how much we did actually read, go to the next chunk
1365
0
        mFileBuffer = next;
1366
0
    }
1367
0
}
1368
1369
0
void LWOImporter::LoadLWO3Envelope(unsigned int length) {
1370
0
    LE_NCONST uint8_t *const end = mFileBuffer + length;
1371
0
    AI_LWO_VALIDATE_CHUNK_LENGTH(length, ENVL, 4);
1372
1373
0
    mEnvelopes.emplace_back();
1374
0
    LWO::Envelope &envelope = mEnvelopes.back();
1375
1376
    // Get the index of the envelope
1377
0
    envelope.index = ReadVSizedIntLWO2(mFileBuffer);
1378
1379
    // ... and read all blocks
1380
0
    while (true) {
1381
0
        if (mFileBuffer + 8 >= end) break;
1382
0
        LE_NCONST IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
1383
1384
0
        if (mFileBuffer + head.length > end)
1385
0
            throw DeadlyImportError("LWO3: Invalid envelope chunk length");
1386
1387
0
        uint8_t *const next = mFileBuffer + head.length;
1388
0
        switch (head.type) {
1389
                // Type & representation of the envelope
1390
0
            case AI_LWO_TYPE:
1391
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TYPE, 4);
1392
0
                mFileBuffer++; // skip user format
1393
1394
                // Determine type of envelope
1395
0
                envelope.type = (LWO::EnvelopeType)*mFileBuffer;
1396
0
                ++mFileBuffer;
1397
0
                break;
1398
1399
                // precondition
1400
0
            case AI_LWO_PRE:
1401
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, PRE, 4);
1402
0
                envelope.pre = (LWO::PrePostBehaviour)GetU2();
1403
0
                break;
1404
1405
                // postcondition
1406
0
            case AI_LWO_POST:
1407
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, POST, 4);
1408
0
                envelope.post = (LWO::PrePostBehaviour)GetU2();
1409
0
                break;
1410
1411
                // keyframe
1412
0
            case AI_LWO_KEY: {
1413
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, KEY, 10);
1414
1415
0
                envelope.keys.emplace_back();
1416
0
                LWO::Key &key = envelope.keys.back();
1417
1418
0
                key.time = GetF4();
1419
0
                key.value = GetF4();
1420
0
                break;
1421
0
            }
1422
1423
                // interval interpolation
1424
0
            case AI_LWO_SPAN: {
1425
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPAN, 6);
1426
0
                if (envelope.keys.size() < 2)
1427
0
                    ASSIMP_LOG_WARN("LWO3: Unexpected SPAN chunk");
1428
0
                else {
1429
0
                    LWO::Key &key = envelope.keys.back();
1430
0
                    switch (GetU4()) {
1431
0
                        case AI_LWO_STEP:
1432
0
                            key.inter = LWO::IT_STEP;
1433
0
                            break;
1434
0
                        case AI_LWO_LINE:
1435
0
                            key.inter = LWO::IT_LINE;
1436
0
                            break;
1437
0
                        case AI_LWO_TCB:
1438
0
                            key.inter = LWO::IT_TCB;
1439
0
                            break;
1440
0
                        case AI_LWO_HERM:
1441
0
                            key.inter = LWO::IT_HERM;
1442
0
                            break;
1443
0
                        case AI_LWO_BEZI:
1444
0
                            key.inter = LWO::IT_BEZI;
1445
0
                            break;
1446
0
                        case AI_LWO_BEZ2:
1447
0
                            key.inter = LWO::IT_BEZ2;
1448
0
                            break;
1449
0
                        default:
1450
0
                            ASSIMP_LOG_WARN("LWO3: Unknown interval interpolation mode");
1451
0
                    };
1452
1453
                    // todo ... read params
1454
0
                }
1455
0
                break;
1456
0
            }
1457
1458
0
            default:
1459
0
                ASSIMP_LOG_WARN("LWO3: Encountered unknown ENVL subchunk");
1460
0
                break;
1461
0
        }
1462
        // regardless how much we did actually read, go to the next chunk
1463
0
        mFileBuffer = next;
1464
0
    }
1465
0
}
1466
1467
// ------------------------------------------------------------------------------------------------
1468
// Load file - master function
1469
0
void LWOImporter::LoadLWO2File() {
1470
0
    bool skip = false;
1471
1472
0
    LE_NCONST uint8_t *const end = mFileBuffer + fileSize;
1473
0
    unsigned int iUnnamed = 0;
1474
1475
0
    while (true) {
1476
0
        if (mFileBuffer + sizeof(IFF::ChunkHeader) > end) break;
1477
1478
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
1479
1480
0
        int bufOffset = 0;
1481
0
        if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
1482
0
            mFileBuffer -= 8;
1483
0
            head = IFF::LoadForm(mFileBuffer);
1484
0
            bufOffset = 4;
1485
0
        }
1486
1487
0
        if (mFileBuffer + head.length > end) {
1488
0
            throw DeadlyImportError("LWO2: Chunk length points behind the file");
1489
0
        }
1490
0
        uint8_t *const next = mFileBuffer + head.length;
1491
0
        mFileBuffer += bufOffset;
1492
0
        if (!head.length) {
1493
0
            mFileBuffer = next;
1494
0
            continue;
1495
0
        }
1496
1497
0
        switch (head.type) {
1498
                // new layer
1499
0
            case AI_LWO_LAYR: {
1500
                // add a new layer to the list ....
1501
0
                mLayers->push_back(LWO::Layer());
1502
0
                LWO::Layer &layer = mLayers->back();
1503
0
                mCurLayer = &layer;
1504
1505
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, LAYR, 16);
1506
1507
                // layer index.
1508
0
                layer.mIndex = GetU2();
1509
1510
                // Continue loading this layer or ignore it? Check the layer index property
1511
0
                if (UINT_MAX != configLayerIndex && (configLayerIndex - 1) != layer.mIndex) {
1512
0
                    skip = true;
1513
0
                } else
1514
0
                    skip = false;
1515
1516
                // pivot point
1517
0
                mFileBuffer += 2; /* unknown */
1518
0
                mCurLayer->mPivot.x = GetF4();
1519
0
                mCurLayer->mPivot.y = GetF4();
1520
0
                mCurLayer->mPivot.z = GetF4();
1521
0
                GetS0(layer.mName, head.length - 16);
1522
1523
                // if the name is empty, generate a default name
1524
0
                if (layer.mName.empty()) {
1525
0
                    char buffer[128]; // should be sufficiently large
1526
0
                    ::ai_snprintf(buffer, 128, "Layer_%i", iUnnamed++);
1527
0
                    layer.mName = buffer;
1528
0
                }
1529
1530
                // load this layer or ignore it? Check the layer name property
1531
0
                if (configLayerName.length() && configLayerName != layer.mName) {
1532
0
                    skip = true;
1533
0
                } else
1534
0
                    hasNamedLayer = true;
1535
1536
                // optional: parent of this layer
1537
0
                if (mFileBuffer + 2 <= next)
1538
0
                    layer.mParent = GetU2();
1539
0
                else
1540
0
                    layer.mParent = (uint16_t) -1;
1541
1542
                // Set layer skip parameter
1543
0
                layer.skip = skip;
1544
1545
0
                break;
1546
0
            }
1547
                // vertex list
1548
0
            case AI_LWO_PNTS: {
1549
0
                if (skip)
1550
0
                    break;
1551
1552
0
                unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
1553
0
                LoadLWOPoints(head.length);
1554
0
                mCurLayer->mPointIDXOfs = old;
1555
0
                break;
1556
0
            }
1557
                // vertex tags
1558
0
            case AI_LWO_VMAD:
1559
0
                if (mCurLayer->mFaces.empty()) {
1560
0
                    ASSIMP_LOG_WARN("LWO2: Unexpected VMAD chunk");
1561
0
                    break;
1562
0
                }
1563
                // --- intentionally no break here
1564
                // fallthrough
1565
0
            case AI_LWO_VMAP: {
1566
0
                if (skip)
1567
0
                    break;
1568
1569
0
                if (mCurLayer->mTempPoints.empty())
1570
0
                    ASSIMP_LOG_WARN("LWO2: Unexpected VMAP chunk");
1571
0
                else
1572
0
                    LoadLWO2VertexMap(head.length, head.type == AI_LWO_VMAD);
1573
0
                break;
1574
0
            }
1575
                // face list
1576
0
            case AI_LWO_POLS: {
1577
0
                if (skip)
1578
0
                    break;
1579
1580
0
                unsigned int old = (unsigned int)mCurLayer->mFaces.size();
1581
0
                LoadLWO2Polygons(head.length);
1582
0
                mCurLayer->mFaceIDXOfs = old;
1583
0
                break;
1584
0
            }
1585
                // polygon tags
1586
0
            case AI_LWO_PTAG: {
1587
0
                if (skip)
1588
0
                    break;
1589
1590
0
                if (mCurLayer->mFaces.empty()) {
1591
0
                    ASSIMP_LOG_WARN("LWO2: Unexpected PTAG");
1592
0
                } else {
1593
0
                    LoadLWO2PolygonTags(head.length);
1594
0
                }
1595
0
                break;
1596
0
            }
1597
                // list of tags
1598
0
            case AI_LWO_TAGS: {
1599
0
                if (!mTags->empty()) {
1600
0
                    ASSIMP_LOG_WARN("LWO2: SRFS chunk encountered twice");
1601
0
                } else {
1602
0
                    LoadLWOTags(head.length);
1603
0
                }
1604
0
                break;
1605
0
            }
1606
1607
                // surface chunk
1608
0
            case AI_LWO_SURF: {
1609
0
                if( mIsLWO3 )
1610
0
                    LoadLWO3Surface(head.length);
1611
0
                else
1612
0
                    LoadLWO2Surface(head.length);
1613
1614
0
                break;
1615
0
            }
1616
1617
                // clip chunk
1618
0
            case AI_LWO_CLIP: {
1619
0
                if( mIsLWO3 )
1620
0
                    LoadLWO3Clip(head.length);
1621
0
                else
1622
0
                    LoadLWO2Clip(head.length);
1623
0
                break;
1624
0
            }
1625
1626
                // envelope chunk
1627
0
            case AI_LWO_ENVL: {
1628
0
                if( mIsLWO3 )
1629
0
                    LoadLWO3Envelope(head.length);
1630
0
                else
1631
0
                    LoadLWO2Envelope(head.length);
1632
0
                break;
1633
0
            }
1634
0
        }
1635
0
        mFileBuffer = next;
1636
0
    }
1637
0
}
1638
1639
#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER