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