/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 |