Coverage Report

Created: 2026-10-28 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/LWO/LWOMaterial.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 Implementation of the material oart of the LWO importer class */
43
44
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
45
46
// internal headers
47
#include "LWOLoader.h"
48
#include <assimp/ByteSwapper.h>
49
50
using namespace Assimp;
51
52
// ------------------------------------------------------------------------------------------------
53
template <class T>
54
618
T lerp(const T &one, const T &two, float val) {
55
618
    return one + (two - one) * val;
56
618
}
57
58
// ------------------------------------------------------------------------------------------------
59
// Convert a lightwave mapping mode to our's
60
338
inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in) {
61
338
    switch (in) {
62
338
        case LWO::Texture::REPEAT:
63
338
            return aiTextureMapMode_Wrap;
64
65
0
        case LWO::Texture::MIRROR:
66
0
            return aiTextureMapMode_Mirror;
67
68
0
        case LWO::Texture::RESET:
69
0
            ASSIMP_LOG_WARN("LWO2: Unsupported texture map mode: RESET");
70
71
            // fall though here
72
0
        case LWO::Texture::EDGE:
73
0
            return aiTextureMapMode_Clamp;
74
338
    }
75
0
    return (aiTextureMapMode)0;
76
338
}
77
78
// ------------------------------------------------------------------------------------------------
79
3.80k
bool LWOImporter::HandleTextures(aiMaterial *pcMat, const TextureList &in, aiTextureType type) {
80
3.80k
    ai_assert(nullptr != pcMat);
81
82
3.80k
    unsigned int cur = 0, temp = 0;
83
3.80k
    aiString s;
84
3.80k
    bool ret = false;
85
86
3.80k
    for (const auto &texture : in) {
87
522
        if (!texture.enabled || !texture.bCanUse)
88
0
            continue;
89
522
        ret = true;
90
91
        // Convert lightwave's mapping modes to ours. We let them
92
        // as they are, the GenUVcoords step will compute UV
93
        // channels if they're not there.
94
95
522
        aiTextureMapping mapping = aiTextureMapping_OTHER;
96
522
        switch (texture.mapMode) {
97
10
            case LWO::Texture::Planar:
98
10
                mapping = aiTextureMapping_PLANE;
99
10
                break;
100
6
            case LWO::Texture::Cylindrical:
101
6
                mapping = aiTextureMapping_CYLINDER;
102
6
                break;
103
39
            case LWO::Texture::Spherical:
104
39
                mapping = aiTextureMapping_SPHERE;
105
39
                break;
106
0
            case LWO::Texture::Cubic:
107
0
                mapping = aiTextureMapping_BOX;
108
0
                break;
109
0
            case LWO::Texture::FrontProjection:
110
0
                ASSIMP_LOG_ERROR("LWO2: Unsupported texture mapping: FrontProjection");
111
0
                mapping = aiTextureMapping_OTHER;
112
0
                break;
113
467
            case LWO::Texture::UV: {
114
467
                if (UINT_MAX == texture.mRealUVIndex) {
115
                    // We have no UV index for this texture, so we can't display it
116
353
                    continue;
117
353
                }
118
119
                // add the UV source index
120
114
                temp = texture.mRealUVIndex;
121
114
                pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_UVWSRC(type, cur));
122
123
114
                mapping = aiTextureMapping_UV;
124
114
            } break;
125
0
            default:
126
0
                ai_assert(false);
127
522
        };
128
129
169
        if (mapping != aiTextureMapping_UV) {
130
            // Setup the main axis
131
55
            aiVector3D v;
132
55
            switch (texture.majorAxis) {
133
7
                case Texture::AXIS_X:
134
7
                    v = aiVector3D(1.0, 0.0, 0.0);
135
7
                    break;
136
14
                case Texture::AXIS_Y:
137
14
                    v = aiVector3D(0.0, 1.0, 0.0);
138
14
                    break;
139
34
                default: // case Texture::AXIS_Z:
140
34
                    v = aiVector3D(0.0, 0.0, 1.0);
141
34
                    break;
142
55
            }
143
144
55
            pcMat->AddProperty(&v, 1, AI_MATKEY_TEXMAP_AXIS(type, cur));
145
146
            // Setup UV scalings for cylindric and spherical projections
147
55
            if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) {
148
45
                aiUVTransform trafo;
149
45
                trafo.mScaling.x = texture.wrapAmountW;
150
45
                trafo.mScaling.y = texture.wrapAmountH;
151
152
45
                static_assert(sizeof(aiUVTransform) / sizeof(ai_real) == 5, "sizeof(aiUVTransform)/sizeof(ai_real) == 5");
153
45
                pcMat->AddProperty(&trafo, 1, AI_MATKEY_UVTRANSFORM(type, cur));
154
45
            }
155
55
            ASSIMP_LOG_VERBOSE_DEBUG("LWO2: Setting up non-UV mapping");
156
55
        }
157
158
        // The older LWOB format does not use indirect references to clips.
159
        // The file name of a texture is directly specified in the tex chunk.
160
169
        if (mIsLWO2 || mIsLWO3) {
161
            // find the corresponding clip (take the last one if multiple
162
            // share the same index)
163
169
            ClipList::iterator end = mClips.end(), candidate = end;
164
169
            temp = texture.mClipIdx;
165
338
            for (ClipList::iterator clip = mClips.begin(); clip != end; ++clip) {
166
169
                if ((*clip).idx == temp) {
167
169
                    candidate = clip;
168
169
                }
169
169
            }
170
169
            if (candidate == end) {
171
0
                ASSIMP_LOG_ERROR("LWO2: Clip index is out of bounds");
172
0
                temp = 0;
173
174
                // fixme: apparently some LWO files shipping with Doom3 don't
175
                // have clips at all ... check whether that's true or whether
176
                // it's a bug in the loader.
177
178
0
                s.Set("$texture.png");
179
180
                //continue;
181
169
            } else {
182
169
                if (Clip::UNSUPPORTED == (*candidate).type) {
183
0
                    ASSIMP_LOG_ERROR("LWO2: Clip type is not supported");
184
0
                    continue;
185
0
                }
186
169
                AdjustTexturePath((*candidate).path);
187
169
                s.Set((*candidate).path);
188
189
                // Additional image settings
190
169
                int flags = 0;
191
169
                if ((*candidate).negate) {
192
0
                    flags |= aiTextureFlags_Invert;
193
0
                }
194
169
                pcMat->AddProperty(&flags, 1, AI_MATKEY_TEXFLAGS(type, cur));
195
169
            }
196
169
        } else {
197
0
            std::string ss = texture.mFileName;
198
0
            if (!ss.length()) {
199
0
                ASSIMP_LOG_WARN("LWOB: Empty file name");
200
0
                continue;
201
0
            }
202
0
            AdjustTexturePath(ss);
203
0
            s.Set(ss);
204
0
        }
205
169
        pcMat->AddProperty(&s, AI_MATKEY_TEXTURE(type, cur));
206
207
        // add the blend factor
208
169
        pcMat->AddProperty<float>(&texture.mStrength, 1, AI_MATKEY_TEXBLEND(type, cur));
209
210
        // add the blend operation
211
169
        switch (texture.blendType) {
212
168
            case LWO::Texture::Normal:
213
168
            case LWO::Texture::Multiply:
214
168
                temp = (unsigned int)aiTextureOp_Multiply;
215
168
                break;
216
217
0
            case LWO::Texture::Subtractive:
218
0
            case LWO::Texture::Difference:
219
0
                temp = (unsigned int)aiTextureOp_Subtract;
220
0
                break;
221
222
0
            case LWO::Texture::Divide:
223
0
                temp = (unsigned int)aiTextureOp_Divide;
224
0
                break;
225
226
0
            case LWO::Texture::Additive:
227
0
                temp = (unsigned int)aiTextureOp_Add;
228
0
                break;
229
230
1
            default:
231
1
                temp = (unsigned int)aiTextureOp_Multiply;
232
1
                ASSIMP_LOG_WARN("LWO2: Unsupported texture blend mode: alpha or displacement");
233
169
        }
234
        // Setup texture operation
235
169
        pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_TEXOP(type, cur));
236
237
        // setup the mapping mode
238
169
        int mapping_ = static_cast<int>(mapping);
239
169
        pcMat->AddProperty<int>(&mapping_, 1, AI_MATKEY_MAPPING(type, cur));
240
241
        // add the u-wrapping
242
169
        temp = (unsigned int)GetMapMode(texture.wrapModeWidth);
243
169
        pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_MAPPINGMODE_U(type, cur));
244
245
        // add the v-wrapping
246
169
        temp = (unsigned int)GetMapMode(texture.wrapModeHeight);
247
169
        pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_MAPPINGMODE_V(type, cur));
248
249
169
        ++cur;
250
169
    }
251
3.80k
    return ret;
252
3.80k
}
253
254
// ------------------------------------------------------------------------------------------------
255
618
void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
256
    // copy the name of the surface
257
618
    aiString st;
258
618
    st.Set(surf.mName);
259
618
    pcMat->AddProperty(&st, AI_MATKEY_NAME);
260
261
618
    const int i = surf.bDoubleSided ? 1 : 0;
262
618
    pcMat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
263
264
    // add the refraction index and the bump intensity
265
618
    pcMat->AddProperty(&surf.mIOR, 1, AI_MATKEY_REFRACTI);
266
618
    pcMat->AddProperty(&surf.mBumpIntensity, 1, AI_MATKEY_BUMPSCALING);
267
268
618
    aiShadingMode m;
269
618
    if (surf.mSpecularValue && surf.mGlossiness) {
270
6
        float fGloss;
271
6
        if (mIsLWO2 || mIsLWO3) {
272
6
            fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0));
273
6
        } else {
274
0
            if (16.0 >= surf.mGlossiness)
275
0
                fGloss = 6.0;
276
0
            else if (64.0 >= surf.mGlossiness)
277
0
                fGloss = 20.0;
278
0
            else if (256.0 >= surf.mGlossiness)
279
0
                fGloss = 50.0;
280
0
            else
281
0
                fGloss = 80.0;
282
0
        }
283
284
6
        pcMat->AddProperty(&surf.mSpecularValue, 1, AI_MATKEY_SHININESS_STRENGTH);
285
6
        pcMat->AddProperty(&fGloss, 1, AI_MATKEY_SHININESS);
286
6
        m = aiShadingMode_Phong;
287
6
    } else
288
612
        m = aiShadingMode_Gouraud;
289
290
    // specular color
291
618
    aiColor3D clr = lerp(aiColor3D(1.0, 1.0, 1.0), surf.mColor, surf.mColorHighlights);
292
618
    pcMat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
293
618
    pcMat->AddProperty(&surf.mSpecularValue, 1, AI_MATKEY_SHININESS_STRENGTH);
294
295
    // emissive color
296
    // luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good.
297
618
    clr.g = clr.b = clr.r = surf.mLuminosity * ai_real(0.8);
298
618
    pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_EMISSIVE);
299
300
    // opacity ... either additive or default-blended, please
301
618
    if (0.0 != surf.mAdditiveTransparency) {
302
0
        const int add = aiBlendMode_Additive;
303
0
        pcMat->AddProperty(&surf.mAdditiveTransparency, 1, AI_MATKEY_OPACITY);
304
0
        pcMat->AddProperty(&add, 1, AI_MATKEY_BLEND_FUNC);
305
618
    } else if (10e10f != surf.mTransparency) {
306
618
        const int def = aiBlendMode_Default;
307
618
        const float f = 1.0f - surf.mTransparency;
308
618
        pcMat->AddProperty(&f, 1, AI_MATKEY_OPACITY);
309
618
        pcMat->AddProperty(&def, 1, AI_MATKEY_BLEND_FUNC);
310
618
    }
311
312
    // ADD TEXTURES to the material
313
    // TODO: find out how we can handle COLOR textures correctly...
314
618
    bool b = HandleTextures(pcMat, surf.mColorTextures, aiTextureType_DIFFUSE);
315
618
    b = (b || HandleTextures(pcMat, surf.mDiffuseTextures, aiTextureType_DIFFUSE));
316
618
    HandleTextures(pcMat, surf.mSpecularTextures, aiTextureType_SPECULAR);
317
618
    HandleTextures(pcMat, surf.mGlossinessTextures, aiTextureType_SHININESS);
318
618
    HandleTextures(pcMat, surf.mBumpTextures, aiTextureType_HEIGHT);
319
618
    HandleTextures(pcMat, surf.mOpacityTextures, aiTextureType_OPACITY);
320
618
    HandleTextures(pcMat, surf.mReflectionTextures, aiTextureType_REFLECTION);
321
322
    // Now we need to know which shader to use .. iterate through the shader list of
323
    // the surface and  search for a name which we know ...
324
618
    for (const auto &shader : surf.mShaders) {
325
0
        if (shader.functionName == "LW_SuperCelShader" || shader.functionName == "AH_CelShader") {
326
0
            ASSIMP_LOG_INFO("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon");
327
328
0
            m = aiShadingMode_Toon;
329
0
            break;
330
0
        } else if (shader.functionName == "LW_RealFresnel" || shader.functionName == "LW_FastFresnel") {
331
0
            ASSIMP_LOG_INFO("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel");
332
333
0
            m = aiShadingMode_Fresnel;
334
0
            break;
335
0
        } else {
336
0
            ASSIMP_LOG_WARN("LWO2: Unknown surface shader: ", shader.functionName);
337
0
        }
338
0
    }
339
618
    if (surf.mMaximumSmoothAngle <= 0.0)
340
601
        m = aiShadingMode_Flat;
341
618
    int m_ = static_cast<int>(m);
342
618
    pcMat->AddProperty(&m_, 1, AI_MATKEY_SHADING_MODEL);
343
344
    // (the diffuse value is just a scaling factor)
345
    // If a diffuse texture is set, we set this value to 1.0
346
618
    clr = (b ? aiColor3D(1.0, 1.0, 1.0) : surf.mColor);
347
618
    clr.r *= surf.mDiffuseValue;
348
618
    clr.g *= surf.mDiffuseValue;
349
618
    clr.b *= surf.mDiffuseValue;
350
618
    pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
351
618
}
352
353
// ------------------------------------------------------------------------------------------------
354
char LWOImporter::FindUVChannels(LWO::TextureList &list,
355
1.14k
        LWO::Layer & /*layer*/, LWO::UVChannel &uv, unsigned int next) {
356
1.14k
    char ret = 0;
357
1.14k
    for (auto &texture : list) {
358
359
        // Ignore textures with non-UV mappings for the moment.
360
142
        if (!texture.enabled || !texture.bCanUse || texture.mapMode != LWO::Texture::UV) {
361
1
            continue;
362
1
        }
363
364
141
        if (texture.mUVChannelIndex == uv.name) {
365
114
            ret = 1;
366
367
            // got it.
368
114
            if (texture.mRealUVIndex == UINT_MAX || texture.mRealUVIndex == next) {
369
114
                texture.mRealUVIndex = next;
370
114
            } else {
371
                // channel mismatch. need to duplicate the material.
372
0
                ASSIMP_LOG_WARN("LWO: Channel mismatch, would need to duplicate surface [design bug]");
373
374
                // TODO
375
0
            }
376
114
        }
377
141
    }
378
1.14k
    return ret;
379
1.14k
}
380
381
// ------------------------------------------------------------------------------------------------
382
void LWOImporter::FindUVChannels(LWO::Surface &surf,
383
        LWO::SortedRep &sorted, LWO::Layer &layer,
384
415
        unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]) {
385
415
    unsigned int next = 0, extra = 0, num_extra = 0;
386
387
    // Check whether we have an UV entry != 0 for one of the faces in 'sorted'
388
691
    for (unsigned int i = 0; i < layer.mUVChannels.size(); ++i) {
389
276
        LWO::UVChannel &uv = layer.mUVChannels[i];
390
391
1.64k
        for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
392
393
1.36k
            LWO::Face &face = layer.mFaces[*it];
394
395
6.23k
            for (unsigned int n = 0; n < face.mNumIndices; ++n) {
396
5.03k
                unsigned int idx = face.mIndices[n];
397
398
5.03k
                if (uv.abAssigned[idx] && ((aiVector2D *)&uv.rawData[0])[idx] != aiVector2D()) {
399
400
163
                    if (extra >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
401
402
0
                        ASSIMP_LOG_ERROR("LWO: Maximum number of UV channels for "
403
0
                                         "this mesh reached. Skipping channel \'" +
404
0
                                         uv.name + "\'");
405
406
163
                    } else {
407
                        // Search through all textures assigned to 'surf' and look for this UV channel
408
163
                        char had = 0;
409
163
                        had |= FindUVChannels(surf.mColorTextures, layer, uv, next);
410
163
                        had |= FindUVChannels(surf.mDiffuseTextures, layer, uv, next);
411
163
                        had |= FindUVChannels(surf.mSpecularTextures, layer, uv, next);
412
163
                        had |= FindUVChannels(surf.mGlossinessTextures, layer, uv, next);
413
163
                        had |= FindUVChannels(surf.mOpacityTextures, layer, uv, next);
414
163
                        had |= FindUVChannels(surf.mBumpTextures, layer, uv, next);
415
163
                        had |= FindUVChannels(surf.mReflectionTextures, layer, uv, next);
416
417
                        // We have a texture referencing this UV channel so we have to take special care
418
                        // and are willing to drop unreferenced channels in favour of it.
419
163
                        if (had != 0) {
420
114
                            if (num_extra) {
421
422
4
                                for (unsigned int a = next; a < std::min(extra, AI_MAX_NUMBER_OF_TEXTURECOORDS - 1u); ++a) {
423
2
                                    out[a + 1] = out[a];
424
2
                                }
425
2
                            }
426
114
                            ++extra;
427
114
                            out[next++] = i;
428
114
                        }
429
                        // Bah ... seems not to be used at all. Push to end if enough space is available.
430
49
                        else {
431
49
                            out[extra++] = i;
432
49
                            ++num_extra;
433
49
                        }
434
163
                    }
435
163
                    it = sorted.end() - 1;
436
163
                    break;
437
163
                }
438
5.03k
            }
439
1.36k
        }
440
276
    }
441
415
    if (extra < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
442
415
        out[extra] = UINT_MAX;
443
415
    }
444
415
}
445
446
// ------------------------------------------------------------------------------------------------
447
void LWOImporter::FindVCChannels(const LWO::Surface &surf, LWO::SortedRep &sorted, const LWO::Layer &layer,
448
415
        unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]) {
449
415
    unsigned int next = 0;
450
451
    // Check whether we have an vc entry != 0 for one of the faces in 'sorted'
452
436
    for (unsigned int i = 0; i < layer.mVColorChannels.size(); ++i) {
453
21
        const LWO::VColorChannel &vc = layer.mVColorChannels[i];
454
455
21
        if (surf.mVCMap == vc.name) {
456
            // The vertex color map is explicitly requested by the surface so we need to take special care of it
457
9
            for (unsigned int a = 0; a < std::min(next, AI_MAX_NUMBER_OF_COLOR_SETS - 1u); ++a) {
458
0
                out[a + 1] = out[a];
459
0
            }
460
9
            out[0] = i;
461
9
            ++next;
462
12
        } else {
463
464
429
            for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) {
465
417
                const LWO::Face &face = layer.mFaces[*it];
466
467
1.98k
                for (unsigned int n = 0; n < face.mNumIndices; ++n) {
468
1.57k
                    unsigned int idx = face.mIndices[n];
469
470
1.57k
                    if (vc.abAssigned[idx] && ((aiColor4D *)&vc.rawData[0])[idx] != aiColor4D(0.0, 0.0, 0.0, 1.0)) {
471
11
                        if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) {
472
473
0
                            ASSIMP_LOG_ERROR("LWO: Maximum number of vertex color channels for "
474
0
                                             "this mesh reached. Skipping channel \'" +
475
0
                                             vc.name + "\'");
476
477
11
                        } else {
478
11
                            out[next++] = i;
479
11
                        }
480
11
                        it = sorted.end() - 1;
481
11
                        break;
482
11
                    }
483
1.57k
                }
484
417
            }
485
12
        }
486
21
    }
487
415
    if (next != AI_MAX_NUMBER_OF_COLOR_SETS) {
488
415
        out[next] = UINT_MAX;
489
415
    }
490
415
}
491
492
// ------------------------------------------------------------------------------------------------
493
803
void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture &tex) {
494
803
    LE_NCONST uint8_t *const end = mFileBuffer + size;
495
8.72k
    while (true) {
496
8.72k
        if (mFileBuffer + 6 >= end) break;
497
7.93k
        LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
498
499
7.93k
        if (mFileBuffer + head.length > end)
500
8
            throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
501
502
7.92k
        uint8_t *const next = mFileBuffer + head.length;
503
7.92k
        switch (head.type) {
504
798
            case AI_LWO_PROJ:
505
798
                tex.mapMode = (Texture::MappingMode)GetU2();
506
798
                break;
507
795
            case AI_LWO_WRAP:
508
795
                tex.wrapModeWidth = (Texture::Wrap)GetU2();
509
795
                tex.wrapModeHeight = (Texture::Wrap)GetU2();
510
795
                break;
511
797
            case AI_LWO_AXIS:
512
797
                tex.majorAxis = (Texture::Axes)GetU2();
513
797
                break;
514
797
            case AI_LWO_IMAG:
515
797
                tex.mClipIdx = GetU2();
516
797
                break;
517
740
            case AI_LWO_VMAP:
518
740
                GetS0(tex.mUVChannelIndex, head.length);
519
740
                break;
520
790
            case AI_LWO_WRPH:
521
790
                tex.wrapAmountH = GetF4();
522
790
                break;
523
796
            case AI_LWO_WRPW:
524
796
                tex.wrapAmountW = GetF4();
525
796
                break;
526
7.92k
        }
527
7.92k
        mFileBuffer = next;
528
7.92k
    }
529
803
}
530
531
// ------------------------------------------------------------------------------------------------
532
0
void LWOImporter::LoadLWO2Procedural(unsigned int /*size*/, LWO::Texture &tex) {
533
    // --- not supported at the moment
534
0
    ASSIMP_LOG_ERROR("LWO2: Found procedural texture, this is not supported");
535
0
    tex.bCanUse = false;
536
0
}
537
538
// ------------------------------------------------------------------------------------------------
539
0
void LWOImporter::LoadLWO2Gradient(unsigned int /*size*/, LWO::Texture &tex) {
540
    // --- not supported at the moment
541
0
    ASSIMP_LOG_ERROR("LWO2: Found gradient texture, this is not supported");
542
0
    tex.bCanUse = false;
543
0
}
544
545
// ------------------------------------------------------------------------------------------------
546
807
void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture &tex) {
547
807
    LE_NCONST uint8_t *const end = mFileBuffer + size;
548
549
    // get the ordinal string
550
807
    GetS0(tex.ordinal, size);
551
552
    // we could crash later if this is an empty string ...
553
807
    if (!tex.ordinal.length()) {
554
3
        ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
555
3
        tex.ordinal = "\x00";
556
3
    }
557
4.01k
    while (true) {
558
4.01k
        if (mFileBuffer + 6 >= end) break;
559
3.21k
        const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
560
561
3.21k
        if (mFileBuffer + head.length > end)
562
4
            throw DeadlyImportError("LWO2: Invalid texture header chunk length");
563
564
3.20k
        uint8_t *const next = mFileBuffer + head.length;
565
3.20k
        switch (head.type) {
566
801
            case AI_LWO_CHAN:
567
801
                tex.type = GetU4();
568
801
                break;
569
799
            case AI_LWO_ENAB:
570
799
                tex.enabled = GetU2() ? true : false;
571
799
                break;
572
787
            case AI_LWO_OPAC:
573
787
                tex.blendType = (Texture::BlendType)GetU2();
574
787
                tex.mStrength = GetF4();
575
787
                break;
576
3.20k
        }
577
3.20k
        mFileBuffer = next;
578
3.20k
    }
579
807
}
580
581
// ------------------------------------------------------------------------------------------------
582
807
void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader *head, unsigned int size) {
583
807
    ai_assert(!mSurfaces->empty());
584
807
    LWO::Surface &surf = mSurfaces->back();
585
807
    LWO::Texture tex;
586
587
    // load the texture header
588
807
    LoadLWO2TextureHeader(head->length, tex);
589
807
    size -= head->length + 6;
590
591
    // now get the exact type of the texture
592
807
    switch (head->type) {
593
0
        case AI_LWO_PROC:
594
0
            LoadLWO2Procedural(size, tex);
595
0
            break;
596
0
        case AI_LWO_GRAD:
597
0
            LoadLWO2Gradient(size, tex);
598
0
            break;
599
803
        case AI_LWO_IMAP:
600
803
            LoadLWO2ImageMap(size, tex);
601
807
    }
602
603
    // get the destination channel
604
795
    TextureList *listRef = nullptr;
605
795
    switch (tex.type) {
606
775
        case AI_LWO_COLR:
607
775
            listRef = &surf.mColorTextures;
608
775
            break;
609
0
        case AI_LWO_DIFF:
610
0
            listRef = &surf.mDiffuseTextures;
611
0
            break;
612
0
        case AI_LWO_SPEC:
613
0
            listRef = &surf.mSpecularTextures;
614
0
            break;
615
0
        case AI_LWO_GLOS:
616
0
            listRef = &surf.mGlossinessTextures;
617
0
            break;
618
0
        case AI_LWO_BUMP:
619
0
            listRef = &surf.mBumpTextures;
620
0
            break;
621
0
        case AI_LWO_TRAN:
622
0
            listRef = &surf.mOpacityTextures;
623
0
            break;
624
0
        case AI_LWO_REFL:
625
0
            listRef = &surf.mReflectionTextures;
626
0
            break;
627
20
        default:
628
20
            ASSIMP_LOG_WARN("LWO2: Encountered unknown texture type");
629
20
            return;
630
795
    }
631
632
    // now attach the texture to the parent surface - sort by ordinal string
633
775
    for (TextureList::iterator it = listRef->begin(); it != listRef->end(); ++it) {
634
0
        if (::strcmp(tex.ordinal.c_str(), (*it).ordinal.c_str()) < 0) {
635
0
            listRef->insert(it, tex);
636
0
            return;
637
0
        }
638
0
    }
639
775
    listRef->push_back(tex);
640
775
}
641
642
// ------------------------------------------------------------------------------------------------
643
0
void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader * /*head*/, unsigned int size) {
644
0
    LE_NCONST uint8_t *const end = mFileBuffer + size;
645
646
0
    ai_assert(!mSurfaces->empty());
647
0
    LWO::Surface &surf = mSurfaces->back();
648
0
    LWO::Shader shader;
649
650
    // get the ordinal string
651
0
    GetS0(shader.ordinal, size);
652
653
    // we could crash later if this is an empty string ...
654
0
    if (!shader.ordinal.length()) {
655
0
        ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
656
0
        shader.ordinal = "\x00";
657
0
    }
658
659
    // read the header
660
0
    while (true) {
661
0
        if (mFileBuffer + 6 >= end) break;
662
0
        const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
663
664
0
        if (mFileBuffer + head.length > end)
665
0
            throw DeadlyImportError("LWO2: Invalid shader header chunk length");
666
667
0
        uint8_t *const next = mFileBuffer + head.length;
668
0
        switch (head.type) {
669
0
            case AI_LWO_ENAB:
670
0
                shader.enabled = GetU2() ? true : false;
671
0
                break;
672
673
0
            case AI_LWO_FUNC:
674
0
                GetS0(shader.functionName, head.length);
675
0
        }
676
0
        mFileBuffer = next;
677
0
    }
678
679
    // now attach the shader to the parent surface - sort by ordinal string
680
0
    for (ShaderList::iterator it = surf.mShaders.begin(); it != surf.mShaders.end(); ++it) {
681
0
        if (::strcmp(shader.ordinal.c_str(), (*it).ordinal.c_str()) < 0) {
682
0
            surf.mShaders.insert(it, shader);
683
0
            return;
684
0
        }
685
0
    }
686
0
    surf.mShaders.push_back(shader);
687
0
}
688
689
// ------------------------------------------------------------------------------------------------
690
0
void LWOImporter::LoadNodalBlocks(unsigned int size) {
691
0
    LE_NCONST uint8_t *const end = mFileBuffer + size;
692
693
0
    while (true) {
694
0
        if (mFileBuffer + 8 >= end)
695
0
            break;
696
697
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
698
0
        int bufOffset = 0;
699
0
        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
700
0
            mFileBuffer -= 8;
701
0
            head = IFF::LoadForm(mFileBuffer);
702
0
            bufOffset = 4;
703
0
        }
704
705
0
        if (mFileBuffer + head.length > end) {
706
0
            throw DeadlyImportError("LWO3: cannot read length; LoadNodalBlocks");
707
0
        }
708
0
        uint8_t *const next = mFileBuffer + head.length;
709
0
        mFileBuffer += bufOffset;
710
0
        switch (head.type) {
711
0
        case AI_LWO_NNDS:
712
0
            LoadNodes(head.length);
713
0
            break;
714
0
        }
715
716
0
        mFileBuffer = next;
717
0
    }
718
0
}
719
720
// ------------------------------------------------------------------------------------------------
721
0
void LWOImporter::LoadNodes(unsigned int size) {
722
0
    LE_NCONST uint8_t *const end = mFileBuffer + size;
723
724
0
    while (true) {
725
0
        if (mFileBuffer + 8 >= end)
726
0
            break;
727
728
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
729
0
        int bufOffset = 0;
730
0
        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
731
0
            mFileBuffer -= 8;
732
0
            head = IFF::LoadForm(mFileBuffer);
733
0
            bufOffset = 4;
734
0
        }
735
736
0
        if (mFileBuffer + head.length > end) {
737
0
            throw DeadlyImportError("LWO3: cannot read length; LoadNodes");
738
0
        }
739
740
0
        uint8_t *const next = mFileBuffer + head.length;
741
0
        mFileBuffer += bufOffset;
742
0
        switch (head.type) {
743
0
        case AI_LWO_NTAG:
744
0
            LoadNodeTag(head.length);
745
0
            break;
746
0
        }
747
748
0
        mFileBuffer = next;
749
0
    }
750
0
}
751
752
// ------------------------------------------------------------------------------------------------
753
0
void LWOImporter::LoadNodeTag(unsigned int size) {
754
0
    LE_NCONST uint8_t *const end = mFileBuffer + size;
755
756
0
    while (true) {
757
0
        if (mFileBuffer + 8 >= end)
758
0
            break;
759
760
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
761
0
        int bufOffset = 0;
762
0
        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
763
0
            mFileBuffer -= 8;
764
0
            head = IFF::LoadForm(mFileBuffer);
765
0
            bufOffset = 4;
766
0
        }
767
768
0
        if (mFileBuffer + head.length > end) {
769
0
            throw DeadlyImportError("LWO3: cannot read length; LoadNodeTag");
770
0
        }
771
772
0
        uint8_t *const next = mFileBuffer + head.length;
773
0
        mFileBuffer += bufOffset;
774
775
0
        switch (head.type) {
776
0
        case AI_LWO_NDTA:
777
0
            LoadNodeData(head.length);
778
0
            break;
779
0
        }
780
781
0
        mFileBuffer = next;
782
0
    }
783
0
}
784
785
// ------------------------------------------------------------------------------------------------
786
0
void LWOImporter::LoadNodeData(unsigned int size) {
787
0
    LE_NCONST uint8_t *const end = mFileBuffer + size;
788
789
0
    LWO::Surface &surf = mSurfaces->back();
790
791
0
    while (true) {
792
0
        if (mFileBuffer + 8 >= end)
793
0
            break;
794
795
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
796
0
        int bufOffset = 0;
797
0
        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
798
0
            mFileBuffer -= 8;
799
0
            head = IFF::LoadForm(mFileBuffer);
800
0
            bufOffset = 4;
801
0
        }
802
803
0
        if (mFileBuffer + head.length > end) {
804
0
            throw DeadlyImportError("LWO3: INVALID LENGTH; LoadNodeData");
805
0
        }
806
807
0
        uint8_t *const next = mFileBuffer + head.length;
808
0
        mFileBuffer += bufOffset;
809
0
        switch (head.type) {
810
0
            case AI_LWO_VERS:
811
0
            case AI_LWO_ENUM:
812
0
            case AI_LWO_IBGC:
813
0
            case AI_LWO_IOPC:
814
0
            case AI_LWO_IIMG:
815
0
            case AI_LWO_TXTR:
816
0
            case AI_LWO_IFAL:
817
0
            case AI_LWO_ISCL:
818
0
            case AI_LWO_IPOS:
819
0
            case AI_LWO_IROT:
820
0
            case AI_LWO_IBMP:
821
0
            case AI_LWO_IUTD:
822
0
            case AI_LWO_IVTD:
823
824
0
            case AI_LWO_IPIX:
825
0
            case AI_LWO_IMIP:
826
0
            case AI_LWO_IMOD:
827
0
            case AI_LWO_AMOD:
828
0
            case AI_LWO_IINV:
829
0
            case AI_LWO_INCR:
830
0
            case AI_LWO_IAXS:
831
0
            case AI_LWO_IFOT:
832
0
            case AI_LWO_ITIM:
833
0
            case AI_LWO_IWRL:
834
0
            case AI_LWO_IUTI:
835
0
            case AI_LWO_IUVI:
836
0
            case AI_LWO_IINX:
837
0
            case AI_LWO_IINY:
838
0
            case AI_LWO_IINZ:
839
0
            case AI_LWO_IREF:
840
0
            case AI_LWO_IMST:
841
0
            case AI_LWO_IMAP:
842
0
            case AI_LWO_IUTL:
843
0
            case AI_LWO_IVTL:
844
0
            case AI_LWO_VPVL:
845
0
            case AI_LWO_VPRM:
846
0
                mFileBuffer = next;
847
0
                break;
848
0
            case AI_LWO_ENTR:
849
0
                std::string attrName;
850
851
0
                while (true) {
852
0
                    if (mFileBuffer + 8 >= next)
853
0
                        break;
854
855
0
                    IFF::ChunkHeader head1 = IFF::LoadChunk(mFileBuffer);
856
0
                    int bufOffset1 = 0;
857
0
                    if (head1.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
858
0
                        mFileBuffer -= 8;
859
0
                        head1 = IFF::LoadForm(mFileBuffer);
860
0
                        bufOffset1 = 4;
861
0
                    }
862
863
0
                    if (mFileBuffer + head1.length > end) {
864
0
                        throw DeadlyImportError("LWO3: cannot read length;");
865
0
                    }
866
0
                    uint8_t *const next1 = mFileBuffer + head1.length;
867
0
                    mFileBuffer += bufOffset1;
868
869
0
                    switch (head1.type) {
870
0
                        case AI_LWO_FLAG:
871
0
                        case AI_LWO_TAG:
872
0
                            mFileBuffer = next1;
873
0
                            break;
874
0
                        case AI_LWO_NAME:
875
0
                            GetS0(attrName, head1.length);
876
0
                            break;
877
0
                        case AI_LWO_VALU:
878
0
                            mFileBuffer += 8;
879
880
0
                            std::string valueType;
881
0
                            GetS0(valueType, 8);
882
883
0
                            if (valueType == "int") {
884
0
                                static_cast<void>(GetU4());
885
0
                            } else if (valueType == "double") {
886
0
                                static_cast<void>(GetU8());
887
0
                            } else if (valueType == "vparam") {
888
0
                                mFileBuffer += 24;
889
890
0
                                float value = GetF8();
891
0
                                if (attrName == "Diffuse") {
892
0
                                    surf.mDiffuseValue = value;
893
0
                                } else if (attrName == "Specular") {
894
0
                                    surf.mSpecularValue = value;
895
0
                                } else if (attrName == "Transparency") {
896
0
                                    surf.mTransparency = value;
897
0
                                } else if (attrName == "Glossiness") {
898
0
                                    surf.mGlossiness = value;
899
0
                                } else if (attrName == "Luminosity") {
900
0
                                    surf.mLuminosity = value;
901
0
                                } else if (attrName == "Color Highlight") {
902
0
                                    surf.mColorHighlights = value;
903
0
                                } else if (attrName == "Refraction Index") {
904
0
                                    surf.mIOR = value;
905
0
                                } else if (attrName == "Bump Height") {
906
0
                                    surf.mBumpIntensity = value;
907
0
                                }
908
0
                            } else if (valueType == "vparam3") {
909
0
                                mFileBuffer += 24;
910
911
0
                                float value1, value2, value3;
912
0
                                value1 = GetF8();
913
0
                                value2 = GetF8();
914
0
                                value3 = GetF8();
915
916
0
                                if (attrName == "Color") {
917
0
                                    surf.mColor.r = value1;
918
0
                                    surf.mColor.g = value2;
919
0
                                    surf.mColor.b = value3;
920
0
                                }
921
0
                            }
922
923
0
                            mFileBuffer = next1;
924
0
                            break;
925
0
                    }
926
0
                }
927
928
0
                break;
929
0
        }
930
0
    }
931
0
}
932
933
// ------------------------------------------------------------------------------------------------
934
901
void LWOImporter::LoadLWO2Surface(unsigned int size) {
935
901
    LE_NCONST uint8_t *const end = mFileBuffer + size;
936
937
901
    mSurfaces->push_back(LWO::Surface());
938
901
    LWO::Surface &surf = mSurfaces->back();
939
940
901
    GetS0(surf.mName, size);
941
942
    // check whether this surface was derived from any other surface
943
901
    std::string derived;
944
901
    GetS0(derived, (unsigned int)(end - mFileBuffer));
945
901
    if (derived.length()) {
946
        // yes, find this surface
947
42
        for (SurfaceList::iterator it = mSurfaces->begin(), itEnd = mSurfaces->end() - 1; it != itEnd; ++it) {
948
27
            if ((*it).mName == derived) {
949
                // we have it ...
950
0
                surf = *it;
951
0
                derived.clear();
952
0
                break;
953
0
            }
954
27
        }
955
15
        if (derived.size()) {
956
15
            ASSIMP_LOG_WARN("LWO2: Unable to find source surface: ", derived);
957
15
        }
958
15
    }
959
960
4.82k
    while (true) {
961
4.82k
        if (mFileBuffer + 6 >= end)
962
861
            break;
963
3.96k
        const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
964
965
3.96k
        if (mFileBuffer + head.length > end)
966
18
            throw DeadlyImportError("LWO2: Invalid surface chunk length");
967
968
3.94k
        uint8_t *const next = mFileBuffer + head.length;
969
3.94k
        switch (head.type) {
970
                // diffuse color
971
890
            case AI_LWO_COLR: {
972
890
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, COLR, 12);
973
887
                surf.mColor.r = GetF4();
974
887
                surf.mColor.g = GetF4();
975
887
                surf.mColor.b = GetF4();
976
887
                break;
977
890
            }
978
                // diffuse strength ... hopefully
979
881
            case AI_LWO_DIFF: {
980
881
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, DIFF, 4);
981
880
                surf.mDiffuseValue = GetF4();
982
880
                break;
983
881
            }
984
                // specular strength ... hopefully
985
785
            case AI_LWO_SPEC: {
986
785
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPEC, 4);
987
784
                surf.mSpecularValue = GetF4();
988
784
                break;
989
785
            }
990
                // transparency
991
10
            case AI_LWO_TRAN: {
992
                // transparency explicitly disabled?
993
10
                if (surf.mTransparency == 10e10f)
994
0
                    break;
995
996
10
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TRAN, 4);
997
10
                surf.mTransparency = GetF4();
998
10
                break;
999
10
            }
1000
                // additive transparency
1001
6
            case AI_LWO_ADTR: {
1002
6
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ADTR, 4);
1003
5
                surf.mAdditiveTransparency = GetF4();
1004
5
                break;
1005
6
            }
1006
                // wireframe mode
1007
0
            case AI_LWO_LINE: {
1008
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, LINE, 2);
1009
0
                if (GetU2() & 0x1)
1010
0
                    surf.mWireframe = true;
1011
0
                break;
1012
0
            }
1013
                // glossiness
1014
7
            case AI_LWO_GLOS: {
1015
7
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, GLOS, 4);
1016
7
                surf.mGlossiness = GetF4();
1017
7
                break;
1018
7
            }
1019
                // bump intensity
1020
6
            case AI_LWO_BUMP: {
1021
6
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, BUMP, 4);
1022
6
                surf.mBumpIntensity = GetF4();
1023
6
                break;
1024
6
            }
1025
                // color highlights
1026
0
            case AI_LWO_CLRH: {
1027
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, CLRH, 4);
1028
0
                surf.mColorHighlights = GetF4();
1029
0
                break;
1030
0
            }
1031
                // index of refraction
1032
10
            case AI_LWO_RIND: {
1033
10
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, RIND, 4);
1034
9
                surf.mIOR = GetF4();
1035
9
                break;
1036
10
            }
1037
                // polygon sidedness
1038
21
            case AI_LWO_SIDE: {
1039
21
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SIDE, 2);
1040
20
                surf.bDoubleSided = (3 == GetU2());
1041
20
                break;
1042
21
            }
1043
                // maximum smoothing angle
1044
33
            case AI_LWO_SMAN: {
1045
33
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SMAN, 4);
1046
32
                surf.mMaximumSmoothAngle = std::fabs(GetF4());
1047
32
                break;
1048
33
            }
1049
                // vertex color channel to be applied to the surface
1050
13
            case AI_LWO_VCOL: {
1051
13
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, VCOL, 12);
1052
13
                surf.mDiffuseValue *= GetF4(); // strength
1053
13
                ReadVSizedIntLWO2(mFileBuffer); // skip envelope
1054
13
                surf.mVCMapType = GetU4(); // type of the channel
1055
1056
                // name of the channel
1057
13
                GetS0(surf.mVCMap, (unsigned int)(next - mFileBuffer));
1058
13
                break;
1059
13
            }
1060
                // surface bock entry
1061
815
            case AI_LWO_BLOK: {
1062
815
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, BLOK, 4);
1063
814
                IFF::SubChunkHeader head2 = IFF::LoadSubChunk(mFileBuffer);
1064
1065
814
                switch (head2.type) {
1066
0
                    case AI_LWO_PROC:
1067
0
                    case AI_LWO_GRAD:
1068
807
                    case AI_LWO_IMAP:
1069
807
                        LoadLWO2TextureBlock(&head2, head.length);
1070
807
                        break;
1071
0
                    case AI_LWO_SHDR:
1072
0
                        LoadLWO2ShaderBlock(&head2, head.length);
1073
0
                        break;
1074
1075
7
                    default:
1076
7
                        ASSIMP_LOG_WARN("LWO2: Found an unsupported surface BLOK");
1077
814
                };
1078
1079
802
                break;
1080
814
            }
1081
3.94k
        }
1082
3.92k
        mFileBuffer = next;
1083
3.92k
    }
1084
901
}
1085
1086
0
void LWOImporter::LoadLWO3Surface(unsigned int size) {
1087
0
    mFileBuffer += 8;
1088
0
    LE_NCONST uint8_t *const end = mFileBuffer + size - 12;
1089
1090
0
    mSurfaces->push_back(LWO::Surface());
1091
0
    LWO::Surface &surf = mSurfaces->back();
1092
1093
0
    GetS0(surf.mName, size);
1094
1095
    // check whether this surface was derived from any other surface
1096
0
    std::string derived;
1097
0
    GetS0(derived, (unsigned int)(end - mFileBuffer));
1098
0
    if (derived.length()) {
1099
        // yes, find this surface
1100
0
        for (SurfaceList::iterator it = mSurfaces->begin(), itEnd = mSurfaces->end() - 1; it != itEnd; ++it) {
1101
0
            if ((*it).mName == derived) {
1102
                // we have it ...
1103
0
                surf = *it;
1104
0
                derived.clear();
1105
0
                break;
1106
0
            }
1107
0
        }
1108
0
        if (derived.size()) {
1109
0
            ASSIMP_LOG_WARN("LWO3: Unable to find source surface: ", derived);
1110
0
        }
1111
0
    }
1112
0
    while (true) {
1113
0
        if (mFileBuffer + 8 >= end)
1114
0
            break;
1115
1116
0
        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
1117
0
        int bufOffset = 0;
1118
0
        if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
1119
0
            mFileBuffer -= 8;
1120
0
            head = IFF::LoadForm(mFileBuffer);
1121
0
            bufOffset = 4;
1122
0
        }
1123
1124
0
        if (mFileBuffer + head.length > end) {
1125
0
            throw DeadlyImportError("LWO3: cannot read length; LoadLWO3Surface");
1126
0
        }
1127
1128
0
        uint8_t *const next = mFileBuffer + head.length;
1129
0
        mFileBuffer += bufOffset;
1130
0
        switch (head.type) {
1131
0
            case AI_LWO_NODS:
1132
0
                LoadNodalBlocks(head.length);
1133
0
                break;
1134
                // polygon sidedness
1135
0
            case AI_LWO_SIDE: {
1136
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SIDE, 2);
1137
0
                surf.bDoubleSided = (3 == GetU2());
1138
0
                break;
1139
0
            }
1140
                // maximum smoothing angle
1141
0
            case AI_LWO_SMAN: {
1142
0
                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SMAN, 4);
1143
0
                surf.mMaximumSmoothAngle = std::fabs(GetF4());
1144
0
                break;
1145
0
            }
1146
0
        }
1147
0
        mFileBuffer = next;
1148
0
    }
1149
0
}
1150
1151
#endif // ASSIMP_BUILD_NO_LWO_IMPORTER