Coverage Report

Created: 2026-02-05 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/MDL/MDLMaterialLoader.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 part of the MDL importer class */
43
44
#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
45
46
#include "MDLDefaultColorMap.h"
47
#include "MDLLoader.h"
48
49
#include <assimp/StringUtils.h>
50
#include <assimp/qnan.h>
51
#include <assimp/scene.h>
52
#include <assimp/texture.h>
53
#include <assimp/DefaultLogger.hpp>
54
#include <assimp/IOSystem.hpp>
55
56
#include <memory>
57
58
using namespace Assimp;
59
60
static aiTexel *const bad_texel = reinterpret_cast<aiTexel *>(SIZE_MAX);
61
62
// ------------------------------------------------------------------------------------------------
63
// Find a suitable palette file or take the default one
64
0
void MDLImporter::SearchPalette(const unsigned char **pszColorMap) {
65
    // now try to find the color map in the current directory
66
0
    IOStream *pcStream = mIOHandler->Open(configPalette, "rb");
67
68
0
    const unsigned char *szColorMap = (const unsigned char *)::g_aclrDefaultColorMap;
69
0
    if (pcStream) {
70
0
        if (pcStream->FileSize() >= 768) {
71
0
            size_t len = 256 * 3;
72
0
            unsigned char *colorMap = new unsigned char[len];
73
0
            szColorMap = colorMap;
74
0
            pcStream->Read(colorMap, len, 1);
75
0
            ASSIMP_LOG_INFO("Found valid colormap.lmp in directory. "
76
0
                            "It will be used to decode embedded textures in palletized formats.");
77
0
        }
78
0
        delete pcStream;
79
0
        pcStream = nullptr;
80
0
    }
81
0
    *pszColorMap = szColorMap;
82
0
}
83
84
// ------------------------------------------------------------------------------------------------
85
// Free the palette again
86
0
void MDLImporter::FreePalette(const unsigned char *szColorMap) {
87
0
    if (szColorMap != (const unsigned char *)::g_aclrDefaultColorMap) {
88
0
        delete[] szColorMap;
89
0
    }
90
0
}
91
92
// ------------------------------------------------------------------------------------------------
93
// Check whether we can replace a texture with a single color
94
1
aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture *pcTexture) {
95
1
    ai_assert(nullptr != pcTexture);
96
97
1
    aiColor4D clrOut;
98
1
    clrOut.r = get_qnan();
99
1
    if (!pcTexture->mHeight || !pcTexture->mWidth)
100
0
        return clrOut;
101
102
1
    const unsigned int iNumPixels = pcTexture->mHeight * pcTexture->mWidth;
103
1
    const aiTexel *pcTexel = pcTexture->pcData + 1;
104
1
    const aiTexel *const pcTexelEnd = &pcTexture->pcData[iNumPixels];
105
106
1
    while (pcTexel != pcTexelEnd) {
107
1
        if (*pcTexel != *(pcTexel - 1)) {
108
1
            pcTexel = nullptr;
109
1
            break;
110
1
        }
111
0
        ++pcTexel;
112
0
    }
113
1
    if (pcTexel) {
114
0
        clrOut.r = pcTexture->pcData->r / 255.0f;
115
0
        clrOut.g = pcTexture->pcData->g / 255.0f;
116
0
        clrOut.b = pcTexture->pcData->b / 255.0f;
117
0
        clrOut.a = pcTexture->pcData->a / 255.0f;
118
0
    }
119
1
    return clrOut;
120
1
}
121
122
// ------------------------------------------------------------------------------------------------
123
// Read a texture from a MDL3 file
124
0
void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char *szData) {
125
0
    const MDL::Header *pcHeader = (const MDL::Header *)mBuffer; //the endianness is already corrected in the InternReadFile_3DGS_MDL345 function
126
0
    const size_t len = pcHeader->skinwidth * pcHeader->skinheight;
127
0
    VALIDATE_FILE_SIZE(szData + len);
128
129
    // allocate a new texture object
130
0
    aiTexture *pcNew = new aiTexture();
131
0
    pcNew->mWidth = pcHeader->skinwidth;
132
0
    pcNew->mHeight = pcHeader->skinheight;
133
134
0
    if(pcNew->mWidth != 0 && pcNew->mHeight > UINT_MAX/pcNew->mWidth) {
135
0
        throw DeadlyImportError("Invalid MDL file. A texture is too big.");
136
0
    }
137
0
    pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
138
139
0
    const unsigned char *szColorMap;
140
0
    this->SearchPalette(&szColorMap);
141
142
    // copy texture data
143
0
    for (unsigned int i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
144
0
        const unsigned char val = szData[i];
145
0
        const unsigned char *sz = &szColorMap[val * 3];
146
147
0
        pcNew->pcData[i].a = 0xFF;
148
0
        pcNew->pcData[i].r = *sz++;
149
0
        pcNew->pcData[i].g = *sz++;
150
0
        pcNew->pcData[i].b = *sz;
151
0
    }
152
153
0
    FreePalette(szColorMap);
154
155
    // store the texture
156
0
    aiTexture **pc = this->pScene->mTextures;
157
0
    this->pScene->mTextures = new aiTexture *[pScene->mNumTextures + 1];
158
0
    for (unsigned int i = 0; i < pScene->mNumTextures; ++i)
159
0
        pScene->mTextures[i] = pc[i];
160
161
0
    pScene->mTextures[this->pScene->mNumTextures] = pcNew;
162
0
    pScene->mNumTextures++;
163
0
    delete[] pc;
164
0
}
165
166
// ------------------------------------------------------------------------------------------------
167
// Read a texture from a MDL4 file
168
void MDLImporter::CreateTexture_3DGS_MDL4(const unsigned char *szData,
169
        unsigned int iType,
170
2
        unsigned int *piSkip) {
171
2
    ai_assert(nullptr != piSkip);
172
173
2
    const MDL::Header *pcHeader = (const MDL::Header *)mBuffer; //the endianness is already corrected in the InternReadFile_3DGS_MDL345 function
174
175
2
    if (iType == 1 || iType > 3) {
176
2
        ASSIMP_LOG_ERROR("Unsupported texture file format");
177
2
        return;
178
2
    }
179
180
0
    const bool bNoRead = *piSkip == UINT_MAX;
181
182
    // allocate a new texture object
183
0
    aiTexture *pcNew = new aiTexture();
184
0
    pcNew->mWidth = pcHeader->skinwidth;
185
0
    pcNew->mHeight = pcHeader->skinheight;
186
187
0
    if (bNoRead) pcNew->pcData = bad_texel;
188
0
    ParseTextureColorData(szData, iType, piSkip, pcNew);
189
190
    // store the texture
191
0
    if (!bNoRead) {
192
0
        if (!this->pScene->mNumTextures) {
193
0
            pScene->mNumTextures = 1;
194
0
            pScene->mTextures = new aiTexture *[1];
195
0
            pScene->mTextures[0] = pcNew;
196
0
        } else {
197
0
            aiTexture **pc = pScene->mTextures;
198
0
            pScene->mTextures = new aiTexture *[pScene->mNumTextures + 1];
199
0
            for (unsigned int i = 0; i < this->pScene->mNumTextures; ++i)
200
0
                pScene->mTextures[i] = pc[i];
201
0
            pScene->mTextures[pScene->mNumTextures] = pcNew;
202
0
            pScene->mNumTextures++;
203
0
            delete[] pc;
204
0
        }
205
0
    } else {
206
0
        pcNew->pcData = nullptr;
207
0
        delete pcNew;
208
0
    }
209
0
    return;
210
2
}
211
212
static const uint32_t MaxTextureSize = 4096;
213
214
// ------------------------------------------------------------------------------------------------
215
// Load color data of a texture and convert it to our output format
216
void MDLImporter::ParseTextureColorData(const unsigned char *szData,
217
        unsigned int iType,
218
        unsigned int *piSkip,
219
2
        aiTexture *pcNew) {
220
2
    const bool do_read = bad_texel != pcNew->pcData;
221
222
    // allocate storage for the texture image
223
2
    if (do_read) {
224
        // check for max texture sizes
225
2
        if (pcNew->mWidth > MaxTextureSize || pcNew->mHeight > MaxTextureSize) {
226
1
            throw DeadlyImportError("Invalid MDL file. A texture is too big.");
227
1
        }
228
229
1
        if(pcNew->mWidth != 0 && pcNew->mHeight > UINT_MAX/pcNew->mWidth) {
230
0
            throw DeadlyImportError("Invalid MDL file. A texture is too big.");
231
0
        }
232
1
        pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
233
1
    }
234
235
    // R5G6B5 format (with or without MIPs)
236
    // ****************************************************************
237
1
    if (2 == iType || 10 == iType) {
238
0
        VALIDATE_FILE_SIZE(szData + pcNew->mWidth * pcNew->mHeight * 2);
239
240
        // copy texture data
241
0
        unsigned int i;
242
0
        if (do_read) {
243
0
            for (i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
244
0
                MDL::RGB565 val = ((MDL::RGB565 *)szData)[i];
245
0
                AI_SWAP2(val);
246
247
0
                pcNew->pcData[i].a = 0xFF;
248
0
                pcNew->pcData[i].r = (unsigned char)val.b << 3;
249
0
                pcNew->pcData[i].g = (unsigned char)val.g << 2;
250
0
                pcNew->pcData[i].b = (unsigned char)val.r << 3;
251
0
            }
252
0
        } else {
253
0
            i = pcNew->mWidth * pcNew->mHeight;
254
0
        }
255
0
        *piSkip = i * 2;
256
257
        // apply MIP maps
258
0
        if (10 == iType) {
259
0
            *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
260
0
            VALIDATE_FILE_SIZE(szData + *piSkip);
261
0
        }
262
0
    }
263
    // ARGB4 format (with or without MIPs)
264
    // ****************************************************************
265
1
    else if (3 == iType || 11 == iType) {
266
0
        VALIDATE_FILE_SIZE(szData + pcNew->mWidth * pcNew->mHeight * 4);
267
268
        // copy texture data
269
0
        unsigned int i;
270
0
        if (do_read) {
271
0
            for (i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
272
0
                MDL::ARGB4 val = ((MDL::ARGB4 *)szData)[i];
273
0
                AI_SWAP2(val);
274
275
0
                pcNew->pcData[i].a = (unsigned char)val.a << 4;
276
0
                pcNew->pcData[i].r = (unsigned char)val.r << 4;
277
0
                pcNew->pcData[i].g = (unsigned char)val.g << 4;
278
0
                pcNew->pcData[i].b = (unsigned char)val.b << 4;
279
0
            }
280
0
        } else
281
0
            i = pcNew->mWidth * pcNew->mHeight;
282
0
        *piSkip = i * 2;
283
284
        // apply MIP maps
285
0
        if (11 == iType) {
286
0
            *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
287
0
            VALIDATE_FILE_SIZE(szData + *piSkip);
288
0
        }
289
0
    }
290
    // RGB8 format (with or without MIPs)
291
    // ****************************************************************
292
1
    else if (4 == iType || 12 == iType) {
293
1
        VALIDATE_FILE_SIZE(szData + pcNew->mWidth * pcNew->mHeight * 3);
294
295
        // copy texture data
296
1
        unsigned int i;
297
1
        if (do_read) {
298
166
            for (i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
299
165
                const unsigned char *_szData = &szData[i * 3];
300
301
165
                pcNew->pcData[i].a = 0xFF;
302
165
                pcNew->pcData[i].b = *_szData++;
303
165
                pcNew->pcData[i].g = *_szData++;
304
165
                pcNew->pcData[i].r = *_szData;
305
165
            }
306
1
        } else
307
0
            i = pcNew->mWidth * pcNew->mHeight;
308
309
        // apply MIP maps
310
1
        *piSkip = i * 3;
311
1
        if (12 == iType) {
312
0
            *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) * 3;
313
0
            VALIDATE_FILE_SIZE(szData + *piSkip);
314
0
        }
315
1
    }
316
    // ARGB8 format (with ir without MIPs)
317
    // ****************************************************************
318
0
    else if (5 == iType || 13 == iType) {
319
0
        VALIDATE_FILE_SIZE(szData + pcNew->mWidth * pcNew->mHeight * 4);
320
321
        // copy texture data
322
0
        unsigned int i;
323
0
        if (do_read) {
324
0
            for (i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
325
0
                const unsigned char *_szData = &szData[i * 4];
326
327
0
                pcNew->pcData[i].b = *_szData++;
328
0
                pcNew->pcData[i].g = *_szData++;
329
0
                pcNew->pcData[i].r = *_szData++;
330
0
                pcNew->pcData[i].a = *_szData;
331
0
            }
332
0
        } else {
333
0
            i = pcNew->mWidth * pcNew->mHeight;
334
0
        }
335
336
        // apply MIP maps
337
0
        *piSkip = i << 2;
338
0
        if (13 == iType) {
339
0
            *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 2;
340
0
        }
341
0
    }
342
    // palletized 8 bit texture. As for Quake 1
343
    // ****************************************************************
344
0
    else if (0 == iType) {
345
0
        VALIDATE_FILE_SIZE(szData + pcNew->mWidth * pcNew->mHeight);
346
347
        // copy texture data
348
0
        unsigned int i;
349
0
        if (do_read) {
350
351
0
            const unsigned char *szColorMap;
352
0
            SearchPalette(&szColorMap);
353
354
0
            for (i = 0; i < pcNew->mWidth * pcNew->mHeight; ++i) {
355
0
                const unsigned char val = szData[i];
356
0
                const unsigned char *sz = &szColorMap[val * 3];
357
358
0
                pcNew->pcData[i].a = 0xFF;
359
0
                pcNew->pcData[i].r = *sz++;
360
0
                pcNew->pcData[i].g = *sz++;
361
0
                pcNew->pcData[i].b = *sz;
362
0
            }
363
0
            this->FreePalette(szColorMap);
364
365
0
        } else
366
0
            i = pcNew->mWidth * pcNew->mHeight;
367
0
        *piSkip = i;
368
369
        // FIXME: Also support for MIP maps?
370
0
    }
371
1
}
372
373
// ------------------------------------------------------------------------------------------------
374
// Get a texture from a MDL5 file
375
void MDLImporter::CreateTexture_3DGS_MDL5(const unsigned char *szData,
376
        unsigned int iType,
377
0
        unsigned int *piSkip) {
378
0
    ai_assert(nullptr != piSkip);
379
0
    bool bNoRead = *piSkip == UINT_MAX;
380
381
    // allocate a new texture object
382
0
    aiTexture *pcNew = new aiTexture();
383
384
0
    VALIDATE_FILE_SIZE(szData + 8);
385
386
    // first read the size of the texture
387
0
    pcNew->mWidth = *((uint32_t *)szData);
388
0
    AI_SWAP4(pcNew->mWidth);
389
0
    szData += sizeof(uint32_t);
390
391
0
    pcNew->mHeight = *((uint32_t *)szData);
392
0
    AI_SWAP4(pcNew->mHeight);
393
0
    szData += sizeof(uint32_t);
394
395
0
    if (bNoRead) {
396
0
        pcNew->pcData = bad_texel;
397
0
    }
398
399
    // this should not occur - at least the docs say it shouldn't.
400
    // however, one can easily try out what MED does if you have
401
    // a model with a DDS texture and export it to MDL5 ...
402
    // yeah, it embeds the DDS file.
403
0
    if (6 == iType) {
404
        // this is a compressed texture in DDS format
405
0
        *piSkip = pcNew->mWidth;
406
0
        VALIDATE_FILE_SIZE(szData + *piSkip);
407
408
0
        if (!bNoRead) {
409
            // place a hint and let the application know that this is a DDS file
410
0
            pcNew->mHeight = 0;
411
0
            pcNew->achFormatHint[0] = 'd';
412
0
            pcNew->achFormatHint[1] = 'd';
413
0
            pcNew->achFormatHint[2] = 's';
414
0
            pcNew->achFormatHint[3] = '\0';
415
416
0
            pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth];
417
0
            ::memcpy(pcNew->pcData, szData, pcNew->mWidth);
418
0
        }
419
0
    } else {
420
        // parse the color data of the texture
421
0
        ParseTextureColorData(szData, iType, piSkip, pcNew);
422
0
    }
423
0
    *piSkip += sizeof(uint32_t) * 2;
424
425
0
    if (!bNoRead) {
426
        // store the texture
427
0
        if (!this->pScene->mNumTextures) {
428
0
            pScene->mNumTextures = 1;
429
0
            pScene->mTextures = new aiTexture *[1];
430
0
            pScene->mTextures[0] = pcNew;
431
0
        } else {
432
0
            aiTexture **pc = pScene->mTextures;
433
0
            pScene->mTextures = new aiTexture *[pScene->mNumTextures + 1];
434
0
            for (unsigned int i = 0; i < pScene->mNumTextures; ++i)
435
0
                this->pScene->mTextures[i] = pc[i];
436
437
0
            pScene->mTextures[pScene->mNumTextures] = pcNew;
438
0
            pScene->mNumTextures++;
439
0
            delete[] pc;
440
0
        }
441
0
    } else {
442
0
        pcNew->pcData = nullptr;
443
0
        delete pcNew;
444
0
    }
445
0
    return;
446
0
}
447
448
// ------------------------------------------------------------------------------------------------
449
// Get a skin from a MDL7 file - more complex than all other subformats
450
void MDLImporter::ParseSkinLump_3DGS_MDL7(
451
        const unsigned char *szCurrent,
452
        const unsigned char **szCurrentOut,
453
        aiMaterial *pcMatOut,
454
        unsigned int iType,
455
        unsigned int iWidth,
456
14
        unsigned int iHeight) {
457
14
    std::unique_ptr<aiTexture> pcNew;
458
14
    if (szCurrent == nullptr) {
459
0
        return;
460
0
    }
461
462
    // get the type of the skin
463
14
    unsigned int iMasked = (unsigned int)(iType & 0xF);
464
465
14
    if (0x1 == iMasked) {
466
        // ***** REFERENCE TO ANOTHER SKIN INDEX *****
467
0
        int referrer = (int)iWidth;
468
0
        pcMatOut->AddProperty<int>(&referrer, 1, AI_MDL7_REFERRER_MATERIAL);
469
14
    } else if (0x6 == iMasked) {
470
        // ***** EMBEDDED DDS FILE *****
471
0
        if (1 != iHeight) {
472
0
            ASSIMP_LOG_WARN("Found a reference to an embedded DDS texture, "
473
0
                            "but texture height is not equal to 1, which is not supported by MED");
474
0
        }
475
0
        if (iWidth == 0) {
476
0
            ASSIMP_LOG_ERROR("Found a reference to an embedded DDS texture, but texture width is zero, aborting import.");
477
0
            return;
478
0
        }
479
480
0
        pcNew.reset(new aiTexture);
481
0
        pcNew->mHeight = 0;
482
0
        pcNew->mWidth = iWidth;
483
484
        // place a proper format hint
485
0
        pcNew->achFormatHint[0] = 'd';
486
0
        pcNew->achFormatHint[1] = 'd';
487
0
        pcNew->achFormatHint[2] = 's';
488
0
        pcNew->achFormatHint[3] = '\0';
489
490
0
        SizeCheck(szCurrent + pcNew->mWidth);
491
492
0
        pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth];
493
0
        memcpy(pcNew->pcData, szCurrent, pcNew->mWidth);
494
0
        szCurrent += iWidth;
495
14
    } else if (0x7 == iMasked) {
496
        // ***** REFERENCE TO EXTERNAL FILE *****
497
0
        if (1 != iHeight) {
498
0
            ASSIMP_LOG_WARN("Found a reference to an external texture, "
499
0
                            "but texture height is not equal to 1, which is not supported by MED");
500
0
        }
501
502
0
        aiString szFile;
503
0
        const size_t iLen = strlen((const char *)szCurrent);
504
0
        size_t iLen2 = iLen > (AI_MAXLEN - 1) ? (AI_MAXLEN - 1) : iLen;
505
0
        memcpy(szFile.data, (const char *)szCurrent, iLen2);
506
0
        szFile.data[iLen2] = '\0';
507
0
        szFile.length = static_cast<ai_uint32>(iLen2);
508
509
0
        szCurrent += iLen2 + 1;
510
511
        // place this as diffuse texture
512
0
        pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0));
513
14
    } else if (iMasked || !iType || (iType && iWidth && iHeight)) {
514
2
        pcNew.reset(new aiTexture());
515
2
        if (!iHeight || !iWidth) {
516
0
            ASSIMP_LOG_WARN("Found embedded texture, but its width "
517
0
                            "an height are both 0. Is this a joke?");
518
519
            // generate an empty chess pattern
520
0
            pcNew->mWidth = pcNew->mHeight = 8;
521
0
            pcNew->pcData = new aiTexel[64];
522
0
            for (unsigned int x = 0; x < 8; ++x) {
523
0
                for (unsigned int y = 0; y < 8; ++y) {
524
0
                    const bool bSet = ((0 == x % 2 && 0 != y % 2) ||
525
0
                                       (0 != x % 2 && 0 == y % 2));
526
527
0
                    aiTexel *pc = &pcNew->pcData[y * 8 + x];
528
0
                    pc->r = pc->b = pc->g = (bSet ? 0xFF : 0);
529
0
                    pc->a = 0xFF;
530
0
                }
531
0
            }
532
2
        } else {
533
            // it is a standard color texture. Fill in width and height
534
            // and call the same function we used for loading MDL5 files
535
536
2
            pcNew->mWidth = iWidth;
537
2
            pcNew->mHeight = iHeight;
538
539
2
            unsigned int iSkip = 0;
540
2
            ParseTextureColorData(szCurrent, iMasked, &iSkip, pcNew.get());
541
542
            // skip length of texture data
543
2
            szCurrent += iSkip;
544
2
        }
545
2
    }
546
547
    // sometimes there are MDL7 files which have a monochrome
548
    // texture instead of material colors ... possible they have
549
    // been converted to MDL7 from other formats, such as MDL5
550
14
    aiColor4D clrTexture;
551
14
    if (pcNew)
552
1
        clrTexture = ReplaceTextureWithColor(pcNew.get());
553
13
    else
554
13
        clrTexture.r = get_qnan();
555
556
    // check whether a material definition is contained in the skin
557
14
    if (iType & AI_MDL7_SKINTYPE_MATERIAL) {
558
12
        BE_NCONST MDL::Material_MDL7 *pcMatIn = (BE_NCONST MDL::Material_MDL7 *)szCurrent;
559
12
        szCurrent = (unsigned char *)(pcMatIn + 1);
560
12
        VALIDATE_FILE_SIZE(szCurrent);
561
562
12
        aiColor3D clrTemp;
563
564
12
#define COLOR_MULTIPLY_RGB()         \
565
36
    if (is_not_qnan(clrTexture.r)) { \
566
0
        clrTemp.r *= clrTexture.r;   \
567
0
        clrTemp.g *= clrTexture.g;   \
568
0
        clrTemp.b *= clrTexture.b;   \
569
0
    }
570
571
        // read diffuse color
572
12
        clrTemp.r = pcMatIn->Diffuse.r;
573
12
        AI_SWAP4(clrTemp.r);
574
12
        clrTemp.g = pcMatIn->Diffuse.g;
575
12
        AI_SWAP4(clrTemp.g);
576
12
        clrTemp.b = pcMatIn->Diffuse.b;
577
12
        AI_SWAP4(clrTemp.b);
578
12
        COLOR_MULTIPLY_RGB();
579
12
        pcMatOut->AddProperty<aiColor3D>(&clrTemp, 1, AI_MATKEY_COLOR_DIFFUSE);
580
581
        // read specular color
582
12
        clrTemp.r = pcMatIn->Specular.r;
583
12
        AI_SWAP4(clrTemp.r);
584
12
        clrTemp.g = pcMatIn->Specular.g;
585
12
        AI_SWAP4(clrTemp.g);
586
12
        clrTemp.b = pcMatIn->Specular.b;
587
12
        AI_SWAP4(clrTemp.b);
588
12
        COLOR_MULTIPLY_RGB();
589
12
        pcMatOut->AddProperty<aiColor3D>(&clrTemp, 1, AI_MATKEY_COLOR_SPECULAR);
590
591
        // read ambient color
592
12
        clrTemp.r = pcMatIn->Ambient.r;
593
12
        AI_SWAP4(clrTemp.r);
594
12
        clrTemp.g = pcMatIn->Ambient.g;
595
12
        AI_SWAP4(clrTemp.g);
596
12
        clrTemp.b = pcMatIn->Ambient.b;
597
12
        AI_SWAP4(clrTemp.b);
598
12
        COLOR_MULTIPLY_RGB();
599
12
        pcMatOut->AddProperty<aiColor3D>(&clrTemp, 1, AI_MATKEY_COLOR_AMBIENT);
600
601
        // read emissive color
602
12
        clrTemp.r = pcMatIn->Emissive.r;
603
12
        AI_SWAP4(clrTemp.r);
604
12
        clrTemp.g = pcMatIn->Emissive.g;
605
12
        AI_SWAP4(clrTemp.g);
606
12
        clrTemp.b = pcMatIn->Emissive.b;
607
12
        AI_SWAP4(clrTemp.b);
608
12
        pcMatOut->AddProperty<aiColor3D>(&clrTemp, 1, AI_MATKEY_COLOR_EMISSIVE);
609
610
12
#undef COLOR_MULITPLY_RGB
611
612
        // FIX: Take the opacity from the ambient color.
613
        // The doc say something else, but it is fact that MED exports the
614
        // opacity like this .... oh well.
615
12
        clrTemp.r = pcMatIn->Ambient.a;
616
12
        AI_SWAP4(clrTemp.r);
617
12
        if (is_not_qnan(clrTexture.r)) {
618
0
            clrTemp.r *= clrTexture.a;
619
0
        }
620
12
        pcMatOut->AddProperty<float>(&clrTemp.r, 1, AI_MATKEY_OPACITY);
621
622
        // read phong power
623
12
        int iShadingMode = (int)aiShadingMode_Gouraud;
624
12
        AI_SWAP4(pcMatIn->Power);
625
12
        if (0.0f != pcMatIn->Power) {
626
12
            iShadingMode = (int)aiShadingMode_Phong;
627
            // pcMatIn is packed, we can't form pointers to its members
628
12
            float power = pcMatIn->Power;
629
12
            pcMatOut->AddProperty<float>(&power, 1, AI_MATKEY_SHININESS);
630
12
        }
631
12
        pcMatOut->AddProperty<int>(&iShadingMode, 1, AI_MATKEY_SHADING_MODEL);
632
12
    } else if (is_not_qnan(clrTexture.r)) {
633
0
        pcMatOut->AddProperty<aiColor4D>(&clrTexture, 1, AI_MATKEY_COLOR_DIFFUSE);
634
0
        pcMatOut->AddProperty<aiColor4D>(&clrTexture, 1, AI_MATKEY_COLOR_SPECULAR);
635
0
    }
636
    // if the texture could be replaced by a single material color
637
    // we don't need the texture anymore
638
14
    if (is_not_qnan(clrTexture.r)) {
639
0
        pcNew.reset();
640
0
    }
641
642
    // If an ASCII effect description (HLSL?) is contained in the file,
643
    // we can simply ignore it ...
644
14
    if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF) {
645
0
        VALIDATE_FILE_SIZE(szCurrent);
646
0
        int32_t iMe = *((int32_t *)szCurrent);
647
0
        AI_SWAP4(iMe);
648
0
        szCurrent += sizeof(char) * iMe + sizeof(int32_t);
649
0
        VALIDATE_FILE_SIZE(szCurrent);
650
0
    }
651
652
    // If an embedded texture has been loaded setup the corresponding
653
    // data structures in the aiScene instance
654
14
    if (pcNew && pScene->mNumTextures <= 999) {
655
        // place this as diffuse texture
656
1
        char current[5];
657
1
        ai_snprintf(current, 5, "*%i", this->pScene->mNumTextures);
658
659
1
        aiString szFile;
660
1
        const size_t iLen = strlen((const char *)current);
661
1
        ::memcpy(szFile.data, (const char *)current, iLen + 1);
662
1
        szFile.length = (ai_uint32)iLen;
663
664
1
        pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0));
665
666
        // store the texture
667
1
        if (!pScene->mNumTextures) {
668
1
            pScene->mNumTextures = 1;
669
1
            pScene->mTextures = new aiTexture *[1];
670
1
            pScene->mTextures[0] = pcNew.release();
671
1
        } else {
672
0
            aiTexture **pc = pScene->mTextures;
673
0
            pScene->mTextures = new aiTexture *[pScene->mNumTextures + 1];
674
0
            for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
675
0
                pScene->mTextures[i] = pc[i];
676
0
            }
677
678
0
            pScene->mTextures[pScene->mNumTextures] = pcNew.release();
679
0
            pScene->mNumTextures++;
680
0
            delete[] pc;
681
0
        }
682
1
    }
683
14
    VALIDATE_FILE_SIZE(szCurrent);
684
14
    *szCurrentOut = szCurrent;
685
14
}
686
687
// ------------------------------------------------------------------------------------------------
688
// Skip a skin lump
689
void MDLImporter::SkipSkinLump_3DGS_MDL7(
690
        const unsigned char *szCurrent,
691
        const unsigned char **szCurrentOut,
692
        unsigned int iType,
693
        unsigned int iWidth,
694
0
        unsigned int iHeight) {
695
    // get the type of the skin
696
0
    const unsigned int iMasked = (unsigned int)(iType & 0xF);
697
698
0
    if (0x6 == iMasked) {
699
0
        szCurrent += iWidth;
700
0
    }
701
0
    if (0x7 == iMasked) {
702
0
        const size_t iLen = std::strlen((const char *)szCurrent);
703
0
        szCurrent += iLen + 1;
704
0
    } else if (iMasked || !iType) {
705
0
        if (iMasked || !iType || (iType && iWidth && iHeight)) {
706
            // ParseTextureColorData(..., aiTexture::pcData == bad_texel) will simply
707
            // return the size of the color data in bytes in iSkip
708
0
            unsigned int iSkip = 0;
709
710
0
            aiTexture tex;
711
0
            tex.pcData = bad_texel;
712
0
            tex.mHeight = iHeight;
713
0
            tex.mWidth = iWidth;
714
715
0
            try {
716
0
                ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex);
717
0
            } catch (...) {
718
                // FIX: Important, otherwise the destructor will crash
719
0
                tex.pcData = nullptr;
720
0
                throw;
721
0
            }
722
723
            // FIX: Important, otherwise the destructor will crash
724
0
            tex.pcData = nullptr;
725
726
            // skip length of texture data
727
0
            szCurrent += iSkip;
728
0
        }
729
0
    }
730
731
    // check whether a material definition is contained in the skin
732
0
    if (iType & AI_MDL7_SKINTYPE_MATERIAL) {
733
0
        BE_NCONST MDL::Material_MDL7 *pcMatIn = (BE_NCONST MDL::Material_MDL7 *)szCurrent;
734
0
        szCurrent = (unsigned char *)(pcMatIn + 1);
735
0
    }
736
737
    // if an ASCII effect description (HLSL?) is contained in the file,
738
    // we can simply ignore it ...
739
0
    if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF) {
740
0
        VALIDATE_FILE_SIZE(szCurrent + sizeof(int32_t));
741
0
        int32_t iMe = 0;
742
0
        ::memcpy(&iMe, szCurrent, sizeof(int32_t));
743
0
        AI_SWAP4(iMe);
744
0
        szCurrent += sizeof(char) * iMe + sizeof(int32_t);
745
0
        VALIDATE_FILE_SIZE(szCurrent);
746
0
    }
747
0
    *szCurrentOut = szCurrent;
748
0
}
749
750
// ------------------------------------------------------------------------------------------------
751
void MDLImporter::ParseSkinLump_3DGS_MDL7(
752
        const unsigned char *szCurrent,
753
        const unsigned char **szCurrentOut,
754
13
        std::vector<aiMaterial *> &pcMats) {
755
13
    ai_assert(nullptr != szCurrent);
756
13
    ai_assert(nullptr != szCurrentOut);
757
758
13
    *szCurrentOut = szCurrent;
759
13
    BE_NCONST MDL::Skin_MDL7 *pcSkin = (BE_NCONST MDL::Skin_MDL7 *)szCurrent;
760
13
    AI_SWAP4(pcSkin->width);
761
13
    AI_SWAP4(pcSkin->height);
762
13
    szCurrent += 12;
763
764
    // allocate an output material
765
13
    aiMaterial *pcMatOut = new aiMaterial();
766
13
    pcMats.push_back(pcMatOut);
767
768
    // skip length of file name
769
13
    szCurrent += AI_MDL7_MAX_TEXNAMESIZE;
770
771
13
    ParseSkinLump_3DGS_MDL7(szCurrent, szCurrentOut, pcMatOut,
772
13
            pcSkin->typ, pcSkin->width, pcSkin->height);
773
774
    // place the name of the skin in the material
775
13
    if (pcSkin->texture_name[0]) {
776
        // the 0 termination could be there or not - we can't know
777
12
        aiString szFile;
778
12
        ::memcpy(szFile.data, pcSkin->texture_name, sizeof(pcSkin->texture_name));
779
12
        szFile.data[sizeof(pcSkin->texture_name)] = '\0';
780
12
        szFile.length = (ai_uint32)::strlen(szFile.data);
781
782
        pcMatOut->AddProperty(&szFile, AI_MATKEY_NAME);
783
12
    }
784
13
}
785
786
#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER