Coverage Report

Created: 2026-03-12 06:25

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