#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
#include "MDLLoader.h"
#include "MDLDefaultColorMap.h"
#include "StringUtils.h"
#include <assimp/texture.h>
#include <assimp/IOSystem.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include <assimp/Defines.h>
#include "qnan.h"
using namespace Assimp;
static aiTexel* const bad_texel = reinterpret_cast<aiTexel*>(SIZE_MAX);
void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
{
IOStream* pcStream = pIOHandler->Open(configPalette,"rb");
const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
if(pcStream)
{
if (pcStream->FileSize() >= 768)
{
unsigned char* colorMap = new unsigned char[256*3];
szColorMap = colorMap;
pcStream->Read(colorMap,256*3,1);
DefaultLogger::get()->info("Found valid colormap.lmp in directory. "
"It will be used to decode embedded textures in palletized formats.");
}
delete pcStream;
pcStream = NULL;
}
*pszColorMap = szColorMap;
}
void MDLImporter::FreePalette(const unsigned char* szColorMap)
{
if (szColorMap != (const unsigned char*)::g_aclrDefaultColorMap)
delete[] szColorMap;
}
aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture* pcTexture)
{
ai_assert(NULL != pcTexture);
aiColor4D clrOut;
clrOut.r = get_qnan();
if (!pcTexture->mHeight || !pcTexture->mWidth)
return clrOut;
const unsigned int iNumPixels = pcTexture->mHeight*pcTexture->mWidth;
const aiTexel* pcTexel = pcTexture->pcData+1;
const aiTexel* const pcTexelEnd = &pcTexture->pcData[iNumPixels];
while (pcTexel != pcTexelEnd)
{
if (*pcTexel != *(pcTexel-1))
{
pcTexel = NULL;
break;
}
++pcTexel;
}
if (pcTexel)
{
clrOut.r = pcTexture->pcData->r / 255.0f;
clrOut.g = pcTexture->pcData->g / 255.0f;
clrOut.b = pcTexture->pcData->b / 255.0f;
clrOut.a = pcTexture->pcData->a / 255.0f;
}
return clrOut;
}
void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData)
{
const MDL::Header *pcHeader = (const MDL::Header*)mBuffer;
VALIDATE_FILE_SIZE(szData + pcHeader->skinwidth *
pcHeader->skinheight);
aiTexture* pcNew = new aiTexture();
pcNew->mWidth = pcHeader->skinwidth;
pcNew->mHeight = pcHeader->skinheight;
pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
const unsigned char* szColorMap;
this->SearchPalette(&szColorMap);
for (unsigned int i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
const unsigned char val = szData[i];
const unsigned char* sz = &szColorMap[val*3];
pcNew->pcData[i].a = 0xFF;
pcNew->pcData[i].r = *sz++;
pcNew->pcData[i].g = *sz++;
pcNew->pcData[i].b = *sz;
}
FreePalette(szColorMap);
aiTexture** pc = this->pScene->mTextures;
this->pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
for (unsigned int i = 0; i <pScene->mNumTextures;++i)
pScene->mTextures[i] = pc[i];
pScene->mTextures[this->pScene->mNumTextures] = pcNew;
pScene->mNumTextures++;
delete[] pc;
return;
}
void MDLImporter::CreateTexture_3DGS_MDL4(const unsigned char* szData,
unsigned int iType,
unsigned int* piSkip)
{
ai_assert(NULL != piSkip);
const MDL::Header *pcHeader = (const MDL::Header*)mBuffer;
if (iType == 1 || iType > 3)
{
DefaultLogger::get()->error("Unsupported texture file format");
return;
}
const bool bNoRead = *piSkip == UINT_MAX;
aiTexture* pcNew = new aiTexture();
pcNew->mWidth = pcHeader->skinwidth;
pcNew->mHeight = pcHeader->skinheight;
if (bNoRead)pcNew->pcData = bad_texel;
ParseTextureColorData(szData,iType,piSkip,pcNew);
if (!bNoRead)
{
if (!this->pScene->mNumTextures)
{
pScene->mNumTextures = 1;
pScene->mTextures = new aiTexture*[1];
pScene->mTextures[0] = pcNew;
}
else
{
aiTexture** pc = pScene->mTextures;
pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
pScene->mTextures[i] = pc[i];
pScene->mTextures[pScene->mNumTextures] = pcNew;
pScene->mNumTextures++;
delete[] pc;
}
}
else {
pcNew->pcData = NULL;
delete pcNew;
}
return;
}
void MDLImporter::ParseTextureColorData(const unsigned char* szData,
unsigned int iType,
unsigned int* piSkip,
aiTexture* pcNew)
{
const bool do_read = bad_texel != pcNew->pcData;
if (do_read) {
pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
}
if (2 == iType || 10 == iType)
{
VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*2);
unsigned int i;
if (do_read)
{
for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
MDL::RGB565 val = ((MDL::RGB565*)szData)[i];
AI_SWAP2(val);
pcNew->pcData[i].a = 0xFF;
pcNew->pcData[i].r = (unsigned char)val.b << 3;
pcNew->pcData[i].g = (unsigned char)val.g << 2;
pcNew->pcData[i].b = (unsigned char)val.r << 3;
}
}
else i = pcNew->mWidth*pcNew->mHeight;
*piSkip = i * 2;
if (10 == iType)
{
*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
VALIDATE_FILE_SIZE(szData + *piSkip);
}
}
else if (3 == iType || 11 == iType)
{
VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
unsigned int i;
if (do_read)
{
for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
MDL::ARGB4 val = ((MDL::ARGB4*)szData)[i];
AI_SWAP2(val);
pcNew->pcData[i].a = (unsigned char)val.a << 4;
pcNew->pcData[i].r = (unsigned char)val.r << 4;
pcNew->pcData[i].g = (unsigned char)val.g << 4;
pcNew->pcData[i].b = (unsigned char)val.b << 4;
}
}
else i = pcNew->mWidth*pcNew->mHeight;
*piSkip = i * 2;
if (11 == iType)
{
*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
VALIDATE_FILE_SIZE(szData + *piSkip);
}
}
else if (4 == iType || 12 == iType)
{
VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*3);
unsigned int i;
if (do_read)
{
for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
const unsigned char* _szData = &szData[i*3];
pcNew->pcData[i].a = 0xFF;
pcNew->pcData[i].b = *_szData++;
pcNew->pcData[i].g = *_szData++;
pcNew->pcData[i].r = *_szData;
}
}
else i = pcNew->mWidth*pcNew->mHeight;
*piSkip = i * 3;
if (12 == iType)
{
*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) *3;
VALIDATE_FILE_SIZE(szData + *piSkip);
}
}
else if (5 == iType || 13 == iType)
{
VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
unsigned int i;
if (do_read)
{
for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
const unsigned char* _szData = &szData[i*4];
pcNew->pcData[i].b = *_szData++;
pcNew->pcData[i].g = *_szData++;
pcNew->pcData[i].r = *_szData++;
pcNew->pcData[i].a = *_szData;
}
}
else i = pcNew->mWidth*pcNew->mHeight;
*piSkip = i << 2;
if (13 == iType)
{
*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 2;
}
}
else if (0 == iType)
{
VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight);
unsigned int i;
if (do_read)
{
const unsigned char* szColorMap;
SearchPalette(&szColorMap);
for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
{
const unsigned char val = szData[i];
const unsigned char* sz = &szColorMap[val*3];
pcNew->pcData[i].a = 0xFF;
pcNew->pcData[i].r = *sz++;
pcNew->pcData[i].g = *sz++;
pcNew->pcData[i].b = *sz;
}
this->FreePalette(szColorMap);
}
else i = pcNew->mWidth*pcNew->mHeight;
*piSkip = i;
}
}
void MDLImporter::CreateTexture_3DGS_MDL5(const unsigned char* szData,
unsigned int iType,
unsigned int* piSkip)
{
ai_assert(NULL != piSkip);
bool bNoRead = *piSkip == UINT_MAX;
aiTexture* pcNew = new aiTexture();
VALIDATE_FILE_SIZE(szData+8);
pcNew->mWidth = *((uint32_t*)szData);
AI_SWAP4(pcNew->mWidth);
szData += sizeof(uint32_t);
pcNew->mHeight = *((uint32_t*)szData);
AI_SWAP4(pcNew->mHeight);
szData += sizeof(uint32_t);
if (bNoRead) {
pcNew->pcData = bad_texel;
}
if (6 == iType)
{
*piSkip = pcNew->mWidth;
VALIDATE_FILE_SIZE(szData + *piSkip);
if (!bNoRead)
{
pcNew->mHeight = 0;
pcNew->achFormatHint[0] = 'd';
pcNew->achFormatHint[1] = 'd';
pcNew->achFormatHint[2] = 's';
pcNew->achFormatHint[3] = '\0';
pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
::memcpy(pcNew->pcData,szData,pcNew->mWidth);
}
}
else
{
ParseTextureColorData(szData,iType,piSkip,pcNew);
}
*piSkip += sizeof(uint32_t) * 2;
if (!bNoRead)
{
if (!this->pScene->mNumTextures)
{
pScene->mNumTextures = 1;
pScene->mTextures = new aiTexture*[1];
pScene->mTextures[0] = pcNew;
}
else
{
aiTexture** pc = pScene->mTextures;
pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
for (unsigned int i = 0; i < pScene->mNumTextures;++i)
this->pScene->mTextures[i] = pc[i];
pScene->mTextures[pScene->mNumTextures] = pcNew;
pScene->mNumTextures++;
delete[] pc;
}
}
else {
pcNew->pcData = NULL;
delete pcNew;
}
return;
}
void MDLImporter::ParseSkinLump_3DGS_MDL7(
const unsigned char* szCurrent,
const unsigned char** szCurrentOut,
aiMaterial* pcMatOut,
unsigned int iType,
unsigned int iWidth,
unsigned int iHeight)
{
aiTexture* pcNew = nullptr;
unsigned int iMasked = (unsigned int)(iType & 0xF);
if (0x1 == iMasked)
{
int referrer = (int)iWidth;
pcMatOut->AddProperty<int>(&referrer,1,AI_MDL7_REFERRER_MATERIAL);
}
else if (0x6 == iMasked)
{
if (1 != iHeight)
{
DefaultLogger::get()->warn("Found a reference to an embedded DDS texture, "
"but texture height is not equal to 1, which is not supported by MED");
}
pcNew = new aiTexture();
pcNew->mHeight = 0;
pcNew->mWidth = iWidth;
pcNew->achFormatHint[0] = 'd';
pcNew->achFormatHint[1] = 'd';
pcNew->achFormatHint[2] = 's';
pcNew->achFormatHint[3] = '\0';
pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
memcpy(pcNew->pcData,szCurrent,pcNew->mWidth);
szCurrent += iWidth;
}
else if (0x7 == iMasked)
{
if (1 != iHeight)
{
DefaultLogger::get()->warn("Found a reference to an external texture, "
"but texture height is not equal to 1, which is not supported by MED");
}
aiString szFile;
const size_t iLen = strlen((const char*)szCurrent);
size_t iLen2 = iLen+1;
iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
memcpy(szFile.data,(const char*)szCurrent,iLen2);
szFile.length = iLen;
szCurrent += iLen2;
pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else if (iMasked || !iType || (iType && iWidth && iHeight))
{
pcNew = new aiTexture();
if (!iHeight || !iWidth)
{
DefaultLogger::get()->warn("Found embedded texture, but its width "
"an height are both 0. Is this a joke?");
pcNew->mWidth = pcNew->mHeight = 8;
pcNew->pcData = new aiTexel[64];
for (unsigned int x = 0; x < 8;++x)
{
for (unsigned int y = 0; y < 8;++y)
{
const bool bSet = ((0 == x % 2 && 0 != y % 2) ||
(0 != x % 2 && 0 == y % 2));
aiTexel* pc = &pcNew->pcData[y * 8 + x];
pc->r = pc->b = pc->g = (bSet?0xFF:0);
pc->a = 0xFF;
}
}
}
else
{
pcNew->mWidth = iWidth;
pcNew->mHeight = iHeight;
unsigned int iSkip = 0;
ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew);
szCurrent += iSkip;
}
}
aiColor4D clrTexture;
if (pcNew)clrTexture = ReplaceTextureWithColor(pcNew);
else clrTexture.r = get_qnan();
if (iType & AI_MDL7_SKINTYPE_MATERIAL)
{
BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
szCurrent = (unsigned char*)(pcMatIn+1);
VALIDATE_FILE_SIZE(szCurrent);
aiColor3D clrTemp;
#define COLOR_MULTIPLY_RGB() \
if (is_not_qnan(clrTexture.r)) \
{ \
clrTemp.r *= clrTexture.r; \
clrTemp.g *= clrTexture.g; \
clrTemp.b *= clrTexture.b; \
}
clrTemp.r = pcMatIn->Diffuse.r;
AI_SWAP4(clrTemp.r);
clrTemp.g = pcMatIn->Diffuse.g;
AI_SWAP4(clrTemp.g);
clrTemp.b = pcMatIn->Diffuse.b;
AI_SWAP4(clrTemp.b);
COLOR_MULTIPLY_RGB();
pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_DIFFUSE);
clrTemp.r = pcMatIn->Specular.r;
AI_SWAP4(clrTemp.r);
clrTemp.g = pcMatIn->Specular.g;
AI_SWAP4(clrTemp.g);
clrTemp.b = pcMatIn->Specular.b;
AI_SWAP4(clrTemp.b);
COLOR_MULTIPLY_RGB();
pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_SPECULAR);
clrTemp.r = pcMatIn->Ambient.r;
AI_SWAP4(clrTemp.r);
clrTemp.g = pcMatIn->Ambient.g;
AI_SWAP4(clrTemp.g);
clrTemp.b = pcMatIn->Ambient.b;
AI_SWAP4(clrTemp.b);
COLOR_MULTIPLY_RGB();
pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_AMBIENT);
clrTemp.r = pcMatIn->Emissive.r;
AI_SWAP4(clrTemp.r);
clrTemp.g = pcMatIn->Emissive.g;
AI_SWAP4(clrTemp.g);
clrTemp.b = pcMatIn->Emissive.b;
AI_SWAP4(clrTemp.b);
pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_EMISSIVE);
#undef COLOR_MULITPLY_RGB
clrTemp.r = pcMatIn->Ambient.a;
AI_SWAP4(clrTemp.r);
if (is_not_qnan(clrTexture.r)) {
clrTemp.r *= clrTexture.a;
}
pcMatOut->AddProperty<ai_real>(&clrTemp.r,1,AI_MATKEY_OPACITY);
int iShadingMode = (int)aiShadingMode_Gouraud;
AI_SWAP4(pcMatIn->Power);
if (0.0f != pcMatIn->Power)
{
iShadingMode = (int)aiShadingMode_Phong;
pcMatOut->AddProperty<float>(&pcMatIn->Power,1,AI_MATKEY_SHININESS);
}
pcMatOut->AddProperty<int>(&iShadingMode,1,AI_MATKEY_SHADING_MODEL);
}
else if (is_not_qnan(clrTexture.r))
{
pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_DIFFUSE);
pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_SPECULAR);
}
if (is_not_qnan(clrTexture.r))
{
delete pcNew;
pcNew = NULL;
}
if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
{
VALIDATE_FILE_SIZE(szCurrent);
int32_t iMe = *((int32_t*)szCurrent);
AI_SWAP4(iMe);
szCurrent += sizeof(char) * iMe + sizeof(int32_t);
VALIDATE_FILE_SIZE(szCurrent);
}
if (pcNew && pScene->mNumTextures <= 999)
{
char szCurrent[5];
ai_snprintf(szCurrent,5,"*%i",this->pScene->mNumTextures);
aiString szFile;
const size_t iLen = strlen((const char*)szCurrent);
::memcpy(szFile.data,(const char*)szCurrent,iLen+1);
szFile.length = iLen;
pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
if (!pScene->mNumTextures)
{
pScene->mNumTextures = 1;
pScene->mTextures = new aiTexture*[1];
pScene->mTextures[0] = pcNew;
}
else
{
aiTexture** pc = pScene->mTextures;
pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
for (unsigned int i = 0; i < pScene->mNumTextures;++i) {
pScene->mTextures[i] = pc[i];
}
pScene->mTextures[pScene->mNumTextures] = pcNew;
pScene->mNumTextures++;
delete[] pc;
}
}
VALIDATE_FILE_SIZE(szCurrent);
*szCurrentOut = szCurrent;
if ( nullptr != pcNew ) {
delete pcNew;
}
}
void MDLImporter::SkipSkinLump_3DGS_MDL7(
const unsigned char* szCurrent,
const unsigned char** szCurrentOut,
unsigned int iType,
unsigned int iWidth,
unsigned int iHeight)
{
const unsigned int iMasked = (unsigned int)(iType & 0xF);
if (0x6 == iMasked)
{
szCurrent += iWidth;
}
if (0x7 == iMasked)
{
const size_t iLen = ::strlen((const char*)szCurrent);
szCurrent += iLen+1;
}
else if (iMasked || !iType)
{
if (iMasked || !iType || (iType && iWidth && iHeight))
{
unsigned int iSkip = 0;
aiTexture tex;
tex.pcData = bad_texel;
tex.mHeight = iHeight;
tex.mWidth = iWidth;
ParseTextureColorData(szCurrent,iMasked,&iSkip,&tex);
tex.pcData = NULL;
szCurrent += iSkip;
}
}
if (iType & AI_MDL7_SKINTYPE_MATERIAL)
{
BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
szCurrent = (unsigned char*)(pcMatIn+1);
}
if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
{
int32_t iMe = *((int32_t*)szCurrent);
AI_SWAP4(iMe);
szCurrent += sizeof(char) * iMe + sizeof(int32_t);
}
*szCurrentOut = szCurrent;
}
void MDLImporter::ParseSkinLump_3DGS_MDL7(
const unsigned char* szCurrent,
const unsigned char** szCurrentOut,
std::vector<aiMaterial*>& pcMats)
{
ai_assert(NULL != szCurrent);
ai_assert(NULL != szCurrentOut);
*szCurrentOut = szCurrent;
BE_NCONST MDL::Skin_MDL7* pcSkin = (BE_NCONST MDL::Skin_MDL7*)szCurrent;
AI_SWAP4(pcSkin->width);
AI_SWAP4(pcSkin->height);
szCurrent += 12;
aiMaterial* pcMatOut = new aiMaterial();
pcMats.push_back(pcMatOut);
szCurrent += AI_MDL7_MAX_TEXNAMESIZE;
ParseSkinLump_3DGS_MDL7(szCurrent,szCurrentOut,pcMatOut,
pcSkin->typ,pcSkin->width,pcSkin->height);
if (pcSkin->texture_name[0])
{
aiString szFile;
::memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
szFile.data[sizeof(pcSkin->texture_name)] = '\0';
szFile.length = ::strlen(szFile.data);
pcMatOut->AddProperty(&szFile,AI_MATKEY_NAME);
}
}
#endif