#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
#include "MDLLoader.h"
#include "Macros.h"
#include "qnan.h"
#include "MDLDefaultColorMap.h"
#include "MD2FileData.h"
#include "StringUtils.h"
#include <assimp/Importer.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h>
#include <memory>
using namespace Assimp;
static const aiImporterDesc desc = {
"Quake Mesh / 3D GameStudio Mesh Importer",
"",
"",
"",
aiImporterFlags_SupportBinaryFlavour,
0,
0,
7,
0,
"mdl"
};
#define _AI_MDL7_ACCESS(_data, _index, _limit, _type) \
(*((const _type*)(((const char*)_data) + _index * _limit)))
#define _AI_MDL7_ACCESS_PTR(_data, _index, _limit, _type) \
((BE_NCONST _type*)(((const char*)_data) + _index * _limit))
#define _AI_MDL7_ACCESS_VERT(_data, _index, _limit) \
_AI_MDL7_ACCESS(_data,_index,_limit,MDL::Vertex_MDL7)
MDLImporter::MDLImporter()
: configFrameID(),
mBuffer(),
iGSFileVersion(),
pIOHandler(),
pScene(),
iFileSize()
{}
MDLImporter::~MDLImporter()
{}
bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string extension = GetExtension(pFile);
if (extension == "mdl" || !extension.length() || checkSig) {
uint32_t tokens[8];
tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,8,0);
}
return false;
}
void MDLImporter::SetupProperties(const Importer* pImp)
{
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME,-1);
if(static_cast<unsigned int>(-1) == configFrameID) {
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
}
configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
}
const aiImporterDesc* MDLImporter::GetInfo () const
{
return &desc;
}
void MDLImporter::InternReadFile( const std::string& pFile,
aiScene* _pScene, IOSystem* _pIOHandler)
{
pScene = _pScene;
pIOHandler = _pIOHandler;
std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
if( file.get() == NULL) {
throw DeadlyImportError( "Failed to open MDL file " + pFile + ".");
}
iFileSize = (unsigned int)file->FileSize();
if( iFileSize < sizeof(MDL::Header)) {
throw DeadlyImportError( "MDL File is too small.");
}
mBuffer =new unsigned char[iFileSize+1];
file->Read( (void*)mBuffer, 1, iFileSize);
mBuffer[iFileSize] = '\0';
const uint32_t iMagicWord = *((uint32_t*)mBuffer);
if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: Quake 1, magic word is IDPO");
iGSFileVersion = 0;
InternReadFile_Quake1();
}
else if (AI_MDL_MAGIC_NUMBER_BE_GS3 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS3 == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A2, magic word is MDL2");
iGSFileVersion = 2;
InternReadFile_Quake1();
}
else if (AI_MDL_MAGIC_NUMBER_BE_GS4 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS4 == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL3");
iGSFileVersion = 3;
InternReadFile_3DGS_MDL345();
}
else if (AI_MDL_MAGIC_NUMBER_BE_GS5a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5a == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL4");
iGSFileVersion = 4;
InternReadFile_3DGS_MDL345();
}
else if (AI_MDL_MAGIC_NUMBER_BE_GS5b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5b == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A5, magic word is MDL5");
iGSFileVersion = 5;
InternReadFile_3DGS_MDL345();
}
else if (AI_MDL_MAGIC_NUMBER_BE_GS7 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS7 == iMagicWord) {
DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A7, magic word is MDL7");
iGSFileVersion = 7;
InternReadFile_3DGS_MDL7();
}
else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord ||
AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord)
{
DefaultLogger::get()->debug("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
iGSFileVersion = 0;
InternReadFile_HL2();
}
else {
throw DeadlyImportError( "Unknown MDL subformat " + pFile +
". Magic word (" + std::string((char*)&iMagicWord,4) + ") is not known");
}
pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
delete [] mBuffer;
mBuffer= nullptr;
AI_DEBUG_INVALIDATE_PTR(pIOHandler);
AI_DEBUG_INVALIDATE_PTR(pScene);
}
void MDLImporter::SizeCheck(const void* szPos)
{
if (!szPos || (const unsigned char*)szPos > this->mBuffer + this->iFileSize)
{
throw DeadlyImportError("Invalid MDL file. The file is too small "
"or contains invalid data.");
}
}
void MDLImporter::SizeCheck(const void* szPos, const char* szFile, unsigned int iLine)
{
ai_assert(NULL != szFile);
if (!szPos || (const unsigned char*)szPos > mBuffer + iFileSize)
{
const char* szFilePtr = ::strrchr(szFile,'\\');
if (!szFilePtr) {
if(!(szFilePtr = ::strrchr(szFile,'/')))
szFilePtr = szFile;
}
if (szFilePtr)++szFilePtr;
char szBuffer[1024];
::sprintf(szBuffer,"Invalid MDL file. The file is too small "
"or contains invalid data (File: %s Line: %u)",szFilePtr,iLine);
throw DeadlyImportError(szBuffer);
}
}
void MDLImporter::ValidateHeader_Quake1(const MDL::Header* pcHeader)
{
if (!pcHeader->num_frames)
throw DeadlyImportError( "[Quake 1 MDL] There are no frames in the file");
if (!pcHeader->num_verts)
throw DeadlyImportError( "[Quake 1 MDL] There are no vertices in the file");
if (!pcHeader->num_tris)
throw DeadlyImportError( "[Quake 1 MDL] There are no triangles in the file");
if (!this->iGSFileVersion)
{
if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
"the expected file format version");
if(pcHeader->num_skins && (!pcHeader->skinwidth || !pcHeader->skinheight))
DefaultLogger::get()->warn("Skin width or height are 0");
}
}
#ifdef AI_BUILD_BIG_ENDIAN
void FlipQuakeHeader(BE_NCONST MDL::Header* pcHeader)
{
AI_SWAP4( pcHeader->ident);
AI_SWAP4( pcHeader->version);
AI_SWAP4( pcHeader->boundingradius);
AI_SWAP4( pcHeader->flags);
AI_SWAP4( pcHeader->num_frames);
AI_SWAP4( pcHeader->num_skins);
AI_SWAP4( pcHeader->num_tris);
AI_SWAP4( pcHeader->num_verts);
for (unsigned int i = 0; i < 3;++i)
{
AI_SWAP4( pcHeader->scale[i]);
AI_SWAP4( pcHeader->translate[i]);
}
AI_SWAP4( pcHeader->size);
AI_SWAP4( pcHeader->skinheight);
AI_SWAP4( pcHeader->skinwidth);
AI_SWAP4( pcHeader->synctype);
}
#endif
void MDLImporter::InternReadFile_Quake1( )
{
ai_assert(NULL != pScene);
BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
#ifdef AI_BUILD_BIG_ENDIAN
FlipQuakeHeader(pcHeader);
#endif
ValidateHeader_Quake1(pcHeader);
const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i)
{
union{BE_NCONST MDL::Skin* pcSkin;BE_NCONST MDL::GroupSkin* pcGroupSkin;};
if (szCurrent + sizeof(MDL::Skin) > this->mBuffer + this->iFileSize) {
throw DeadlyImportError("[Quake 1 MDL] Unexpected EOF");
}
pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
AI_SWAP4( pcSkin->group );
if (1 == pcSkin->group)
{
AI_SWAP4( pcGroupSkin->nb );
const unsigned int iNumImages = (unsigned int)pcGroupSkin->nb;
szCurrent += sizeof(uint32_t) * 2;
if (0 != iNumImages)
{
if (!i) {
this->CreateTextureARGB8_3DGS_MDL3(szCurrent + iNumImages * sizeof(float));
}
szCurrent += pcHeader->skinheight * pcHeader->skinwidth +
sizeof(float) * iNumImages;
}
}
else
{
szCurrent += sizeof(uint32_t);
unsigned int iSkip = i ? UINT_MAX : 0;
CreateTexture_3DGS_MDL4(szCurrent,pcSkin->group,&iSkip);
szCurrent += iSkip;
}
}
BE_NCONST MDL::TexCoord* pcTexCoords = (BE_NCONST MDL::TexCoord*)szCurrent;
szCurrent += sizeof(MDL::TexCoord) * pcHeader->num_verts;
BE_NCONST MDL::Triangle* pcTriangles = (BE_NCONST MDL::Triangle*)szCurrent;
szCurrent += sizeof(MDL::Triangle) * pcHeader->num_tris;
VALIDATE_FILE_SIZE(szCurrent);
BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
BE_NCONST MDL::SimpleFrame* pcFirstFrame;
if (0 == pcFrames->type)
{
pcFirstFrame = &pcFrames->frame;
}
else
{
BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
}
BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));
#ifdef AI_BUILD_BIG_ENDIAN
for (int i = 0; i<pcHeader->num_verts;++i)
{
AI_SWAP4( pcTexCoords[i].onseam );
AI_SWAP4( pcTexCoords[i].s );
AI_SWAP4( pcTexCoords[i].t );
}
for (int i = 0; i<pcHeader->num_tris;++i)
{
AI_SWAP4( pcTriangles[i].facesfront);
AI_SWAP4( pcTriangles[i].vertex[0]);
AI_SWAP4( pcTriangles[i].vertex[1]);
AI_SWAP4( pcTriangles[i].vertex[2]);
}
#endif
SetupMaterialProperties_3DGS_MDL5_Quake1();
aiMesh* pcMesh = new aiMesh();
pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
pcMesh->mNumVertices = pcHeader->num_tris * 3;
pcMesh->mNumFaces = pcHeader->num_tris;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
pScene->mMeshes[0] = pcMesh;
unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i)
{
pcMesh->mFaces[i].mIndices = new unsigned int[3];
pcMesh->mFaces[i].mNumIndices = 3;
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
{
pcMesh->mFaces[i].mIndices[c] = iCurrent;
unsigned int iIndex = pcTriangles->vertex[c];
if (iIndex >= (unsigned int)pcHeader->num_verts)
{
iIndex = pcHeader->num_verts-1;
DefaultLogger::get()->warn("Index overflow in Q1-MDL vertex list.");
}
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
vec.x += pcHeader->translate[0];
vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
vec.y += pcHeader->translate[1];
vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
vec.z += pcHeader->translate[2];
MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
float s = (float)pcTexCoords[iIndex].s;
float t = (float)pcTexCoords[iIndex].t;
if (0 == pcTriangles->facesfront && 0 != pcTexCoords[iIndex].onseam) {
s += pcHeader->skinwidth * 0.5f;
}
pcMesh->mTextureCoords[0][iCurrent].x = (s + 0.5f) / pcHeader->skinwidth;
pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-(t + 0.5f) / pcHeader->skinheight;
}
pcMesh->mFaces[i].mIndices[0] = iTemp+2;
pcMesh->mFaces[i].mIndices[1] = iTemp+1;
pcMesh->mFaces[i].mIndices[2] = iTemp+0;
pcTriangles++;
}
return;
}
void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1( )
{
const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = new aiMaterial();
pScene->mNumMaterials = 1;
const int iMode = (int)aiShadingMode_Gouraud;
aiMaterial* const pcHelper = (aiMaterial*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor4D clr;
if (0 != pcHeader->num_skins && pScene->mNumTextures) {
clr = this->ReplaceTextureWithColor(pScene->mTextures[0]);
if (is_not_qnan(clr.r)) {
delete pScene->mTextures[0];
delete[] pScene->mTextures;
pScene->mTextures = NULL;
pScene->mNumTextures = 0;
}
else {
clr.b = clr.a = clr.g = clr.r = 1.0f;
aiString szString;
::memcpy(szString.data,AI_MAKE_EMBEDDED_TEXNAME(0),3);
szString.length = 2;
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
}
pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.r *= 0.05f;clr.g *= 0.05f;
clr.b *= 0.05f;clr.a = 1.0f;
pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
}
void MDLImporter::InternReadFile_3DGS_MDL345( )
{
ai_assert(NULL != pScene);
BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
#ifdef AI_BUILD_BIG_ENDIAN
FlipQuakeHeader(pcHeader);
#endif
ValidateHeader_Quake1(pcHeader);
const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
const unsigned char* szEnd = mBuffer + iFileSize;
for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i) {
if (szCurrent >= szEnd) {
throw DeadlyImportError( "Texture data past end of file.");
}
BE_NCONST MDL::Skin* pcSkin;
pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
AI_SWAP4( pcSkin->group);
unsigned int iSkip = i ? UINT_MAX : 0;
if (5 <= iGSFileVersion)
{
CreateTexture_3DGS_MDL5((unsigned char*)pcSkin + sizeof(uint32_t),
pcSkin->group,&iSkip);
}
else {
CreateTexture_3DGS_MDL4((unsigned char*)pcSkin + sizeof(uint32_t),
pcSkin->group,&iSkip);
}
szCurrent += iSkip + sizeof(uint32_t);
}
BE_NCONST MDL::TexCoord_MDL3* pcTexCoords = (BE_NCONST MDL::TexCoord_MDL3*)szCurrent;
szCurrent += sizeof(MDL::TexCoord_MDL3) * pcHeader->synctype;
BE_NCONST MDL::Triangle_MDL3* pcTriangles = (BE_NCONST MDL::Triangle_MDL3*)szCurrent;
szCurrent += sizeof(MDL::Triangle_MDL3) * pcHeader->num_tris;
#ifdef AI_BUILD_BIG_ENDIAN
for (int i = 0; i<pcHeader->synctype;++i) {
AI_SWAP2( pcTexCoords[i].u );
AI_SWAP2( pcTexCoords[i].v );
}
for (int i = 0; i<pcHeader->num_tris;++i) {
AI_SWAP2( pcTriangles[i].index_xyz[0]);
AI_SWAP2( pcTriangles[i].index_xyz[1]);
AI_SWAP2( pcTriangles[i].index_xyz[2]);
AI_SWAP2( pcTriangles[i].index_uv[0]);
AI_SWAP2( pcTriangles[i].index_uv[1]);
AI_SWAP2( pcTriangles[i].index_uv[2]);
}
#endif
VALIDATE_FILE_SIZE(szCurrent);
SetupMaterialProperties_3DGS_MDL5_Quake1();
aiMesh* pcMesh = new aiMesh();
pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
pcMesh->mNumVertices = pcHeader->num_tris * 3;
pcMesh->mNumFaces = pcHeader->num_tris;
pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
pScene->mMeshes[0] = pcMesh;
pcMesh->mNumVertices = (unsigned int)pcHeader->num_tris*3;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
if (pcHeader->synctype) {
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
}
BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
AI_SWAP4(pcFrames->type);
if (0 == pcFrames->type || 3 >= this->iGSFileVersion) {
const MDL::SimpleFrame* pcFirstFrame = (const MDL::SimpleFrame*)(szCurrent + sizeof(uint32_t));
const MDL::Vertex* pcVertices = (const MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
pcMesh->mFaces[i].mIndices = new unsigned int[3];
pcMesh->mFaces[i].mNumIndices = 3;
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent) {
unsigned int iIndex = pcTriangles->index_xyz[c];
if (iIndex >= (unsigned int)pcHeader->num_verts) {
iIndex = pcHeader->num_verts-1;
DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
}
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
vec.x += pcHeader->translate[0];
vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
vec.y += pcHeader->translate[1];
vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
vec.z += pcHeader->translate[2];
MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
if (pcHeader->synctype) {
ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
pcTexCoords,pcTriangles->index_uv[c]);
}
}
pcMesh->mFaces[i].mIndices[0] = iTemp+2;
pcMesh->mFaces[i].mIndices[1] = iTemp+1;
pcMesh->mFaces[i].mIndices[2] = iTemp+0;
pcTriangles++;
}
}
else {
const MDL::SimpleFrame_MDLn_SP* pcFirstFrame = (const MDL::SimpleFrame_MDLn_SP*) (szCurrent + sizeof(uint32_t));
const MDL::Vertex_MDL4* pcVertices = (const MDL::Vertex_MDL4*) ((pcFirstFrame->name) +
sizeof(pcFirstFrame->name));
VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
pcMesh->mFaces[i].mIndices = new unsigned int[3];
pcMesh->mFaces[i].mNumIndices = 3;
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent) {
unsigned int iIndex = pcTriangles->index_xyz[c];
if (iIndex >= (unsigned int)pcHeader->num_verts) {
iIndex = pcHeader->num_verts-1;
DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
}
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
vec.x += pcHeader->translate[0];
vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
vec.y += pcHeader->translate[1];
vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
vec.z += pcHeader->translate[2];
MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
if (pcHeader->synctype) {
ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
pcTexCoords,pcTriangles->index_uv[c]);
}
}
pcMesh->mFaces[i].mIndices[0] = iTemp+2;
pcMesh->mFaces[i].mIndices[1] = iTemp+1;
pcMesh->mFaces[i].mIndices[2] = iTemp+0;
pcTriangles++;
}
}
if (0x5 == iGSFileVersion)
CalculateUVCoordinates_MDL5();
return;
}
void MDLImporter::ImportUVCoordinate_3DGS_MDL345(
aiVector3D& vOut,
const MDL::TexCoord_MDL3* pcSrc,
unsigned int iIndex)
{
ai_assert(NULL != pcSrc);
const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
if (iIndex >= (unsigned int) pcHeader->synctype) {
iIndex = pcHeader->synctype-1;
DefaultLogger::get()->warn("Index overflow in MDLn UV coord list");
}
float s = (float)pcSrc[iIndex].u;
float t = (float)pcSrc[iIndex].v;
if (0x5 != iGSFileVersion) {
s = (s + 0.5f) / pcHeader->skinwidth;
t = 1.0f-(t + 0.5f) / pcHeader->skinheight;
}
vOut.x = s;
vOut.y = t;
vOut.z = 0.0f;
}
void MDLImporter::CalculateUVCoordinates_MDL5()
{
const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
if (pcHeader->num_skins && this->pScene->mNumTextures) {
const aiTexture* pcTex = this->pScene->mTextures[0];
unsigned int iWidth, iHeight;
if (!pcTex->mHeight) {
const uint32_t* piPtr = (uint32_t*)pcTex->pcData;
piPtr += 3;
iHeight = (unsigned int)*piPtr++;
iWidth = (unsigned int)*piPtr;
if (!iHeight || !iWidth)
{
DefaultLogger::get()->warn("Either the width or the height of the "
"embedded DDS texture is zero. Unable to compute final texture "
"coordinates. The texture coordinates remain in their original "
"0-x/0-y (x,y = texture size) range.");
iWidth = 1;
iHeight = 1;
}
}
else {
iWidth = pcTex->mWidth;
iHeight = pcTex->mHeight;
}
if (1 != iWidth || 1 != iHeight) {
const float fWidth = (float)iWidth;
const float fHeight = (float)iHeight;
aiMesh* pcMesh = this->pScene->mMeshes[0];
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
pcMesh->mTextureCoords[0][i].x /= fWidth;
pcMesh->mTextureCoords[0][i].y /= fHeight;
pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; }
}
}
}
void MDLImporter::ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader)
{
ai_assert(NULL != pcHeader);
if (sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size) {
throw DeadlyImportError(
"[3DGS MDL7] sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size");
}
if (sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size) {
throw DeadlyImportError(
"[3DGS MDL7] sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size");
}
if (sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size) {
throw DeadlyImportError(
"sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size");
}
if(!pcHeader->groups_num) {
throw DeadlyImportError( "[3DGS MDL7] No frames found");
}
}
void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones)
{
const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
const MDL::Bone_MDL7* pcBones = (const MDL::Bone_MDL7*)(pcHeader+1);
ai_assert(NULL != apcOutBones);
uint16_t iParent = 0xffff;
uint32_t iIterations = 0;
while (iIterations++ < pcHeader->bones_num) {
for (uint32_t iBone = 0; iBone < pcHeader->bones_num;++iBone) {
BE_NCONST MDL::Bone_MDL7* pcBone = _AI_MDL7_ACCESS_PTR(pcBones,iBone,
pcHeader->bone_stc_size,MDL::Bone_MDL7);
AI_SWAP2(pcBone->parent_index);
AI_SWAP4(pcBone->x);
AI_SWAP4(pcBone->y);
AI_SWAP4(pcBone->z);
if (iParent == pcBone->parent_index) {
MDL::IntBone_MDL7* const pcOutBone = apcOutBones[iBone];
pcOutBone->iParent = pcBone->parent_index;
if (0xffff != iParent) {
const MDL::IntBone_MDL7* pcParentBone = apcOutBones[iParent];
pcOutBone->mOffsetMatrix.a4 = -pcParentBone->vPosition.x;
pcOutBone->mOffsetMatrix.b4 = -pcParentBone->vPosition.y;
pcOutBone->mOffsetMatrix.c4 = -pcParentBone->vPosition.z;
}
pcOutBone->vPosition.x = pcBone->x;
pcOutBone->vPosition.y = pcBone->y;
pcOutBone->vPosition.z = pcBone->z;
pcOutBone->mOffsetMatrix.a4 -= pcBone->x;
pcOutBone->mOffsetMatrix.b4 -= pcBone->y;
pcOutBone->mOffsetMatrix.c4 -= pcBone->z;
if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) {
pcOutBone->mName.length = ai_snprintf(pcOutBone->mName.data, MAXLEN,
"UnnamedBone_%i",iBone);
}
else {
uint32_t iMaxLen = pcHeader->bone_stc_size-16;
for (uint32_t qq = 0; qq < iMaxLen;++qq) {
if (!pcBone->name[qq]) {
iMaxLen = qq;
break;
}
}
pcOutBone->mName.length = (size_t)iMaxLen;
::memcpy(pcOutBone->mName.data,pcBone->name,pcOutBone->mName.length);
pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
}
}
}
++iParent;
}
}
MDL::IntBone_MDL7** MDLImporter::LoadBones_3DGS_MDL7()
{
const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
if (pcHeader->bones_num) {
if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS != pcHeader->bone_stc_size &&
AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS != pcHeader->bone_stc_size &&
AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE != pcHeader->bone_stc_size)
{
DefaultLogger::get()->warn("Unknown size of bone data structure");
return NULL;
}
MDL::IntBone_MDL7** apcBonesOut = new MDL::IntBone_MDL7*[pcHeader->bones_num];
for (uint32_t crank = 0; crank < pcHeader->bones_num;++crank)
apcBonesOut[crank] = new MDL::IntBone_MDL7();
CalcAbsBoneMatrices_3DGS_MDL7(apcBonesOut);
return apcBonesOut;
}
return NULL;
}
void MDLImporter::ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
MDL::IntGroupData_MDL7& groupData)
{
const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
unsigned int iOutIndex = 0;
for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
AI_SWAP2(pcGroupTris->v_index[0]);
AI_SWAP2(pcGroupTris->v_index[1]);
AI_SWAP2(pcGroupTris->v_index[2]);
for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
unsigned int iIndex = pcGroupTris->v_index[c];
if(iIndex > (unsigned int)groupInfo.pcGroup->numverts) {
pcGroupTris->v_index[c] = iIndex = groupInfo.pcGroup->numverts-1;
DefaultLogger::get()->warn("Index overflow in MDL7 vertex list");
}
groupData.pcFaces[iTriangle].mIndices[2-c] = iOutIndex;
aiVector3D& vPosition = groupData.vPositions[ iOutIndex ];
vPosition.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex, pcHeader->mainvertex_stc_size) .x;
vPosition.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .y;
vPosition.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .z;
if (!groupData.aiBones.empty()) {
groupData.aiBones[iOutIndex] = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,
iIndex,pcHeader->mainvertex_stc_size).vertindex;
}
if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[0];
AI_SWAP4(vNormal.x);
vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[1];
AI_SWAP4(vNormal.y);
vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[2];
AI_SWAP4(vNormal.z);
}
else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,
pcHeader->mainvertex_stc_size) .norm162index,vNormal);
}
if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV) {
if (groupInfo.pcGroup->num_stpts) {
AI_SWAP2(pcGroupTris->skinsets[0].st_index[0]);
AI_SWAP2(pcGroupTris->skinsets[0].st_index[1]);
AI_SWAP2(pcGroupTris->skinsets[0].st_index[2]);
iIndex = pcGroupTris->skinsets[0].st_index[c];
if(iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
iIndex = groupInfo.pcGroup->num_stpts-1;
DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#1)");
}
float u = groupInfo.pcGroupUVs[iIndex].u;
float v = 1.0f-groupInfo.pcGroupUVs[iIndex].v;
groupData.vTextureCoords1[iOutIndex].x = u;
groupData.vTextureCoords1[iOutIndex].y = v;
}
if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX){
AI_SWAP4(pcGroupTris->skinsets[0].material);
groupData.pcFaces[iTriangle].iMatIndex[0] = pcGroupTris->skinsets[0].material;
}
}
if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
if (groupInfo.pcGroup->num_stpts) {
AI_SWAP2(pcGroupTris->skinsets[1].st_index[0]);
AI_SWAP2(pcGroupTris->skinsets[1].st_index[1]);
AI_SWAP2(pcGroupTris->skinsets[1].st_index[2]);
AI_SWAP4(pcGroupTris->skinsets[1].material);
iIndex = pcGroupTris->skinsets[1].st_index[c];
if(iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
iIndex = groupInfo.pcGroup->num_stpts-1;
DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#2)");
}
float u = groupInfo.pcGroupUVs[ iIndex ].u;
float v = 1.0f-groupInfo.pcGroupUVs[ iIndex ].v;
groupData.vTextureCoords2[ iOutIndex ].x = u;
groupData.vTextureCoords2[ iOutIndex ].y = v;
if (0 != iIndex && (u != groupData.vTextureCoords1[ iOutIndex ].x ||
v != groupData.vTextureCoords1[ iOutIndex ].y ) )
groupData.bNeed2UV = true;
if (pcGroupTris->skinsets[ 1 ].material != pcGroupTris->skinsets[ 0 ].material)
groupData.bNeed2UV = true;
}
groupData.pcFaces[ iTriangle ].iMatIndex[ 1 ] = pcGroupTris->skinsets[ 1 ].material;
}
}
pcGroupTris = (MDL::Triangle_MDL7*)((const char*)pcGroupTris + pcHeader->triangle_stc_size);
}
}
bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
MDL::IntGroupData_MDL7& groupData,
MDL::IntSharedData_MDL7& shared,
const unsigned char* szCurrent,
const unsigned char** szCurrentOut)
{
ai_assert( nullptr != szCurrent );
ai_assert( nullptr != szCurrentOut);
const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)mBuffer;
for(unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes;++iFrame) {
MDL::IntFrameInfo_MDL7 frame ((BE_NCONST MDL::Frame_MDL7*)szCurrent,iFrame);
AI_SWAP4(frame.pcFrame->vertices_count);
AI_SWAP4(frame.pcFrame->transmatrix_count);
const unsigned int iAdd = pcHeader->frame_stc_size +
frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size +
frame.pcFrame->transmatrix_count * pcHeader->bonetrans_stc_size;
if (((const char*)szCurrent - (const char*)pcHeader) + iAdd > (unsigned int)pcHeader->data_size) {
DefaultLogger::get()->warn("Index overflow in frame area. "
"Ignoring all frames and all further mesh groups, too.");
*szCurrentOut = szCurrent;
return false;
}
if (configFrameID == iFrame) {
BE_NCONST MDL::Vertex_MDL7* pcFrameVertices = (BE_NCONST MDL::Vertex_MDL7*)(szCurrent+pcHeader->frame_stc_size);
for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count;++qq) {
uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices,qq,pcHeader->framevertex_stc_size,MDL::Vertex_MDL7).vertindex;
AI_SWAP2(iIndex);
if (iIndex >= groupInfo.pcGroup->numverts) {
DefaultLogger::get()->warn("Invalid vertex index in frame vertex section");
continue;
}
aiVector3D vPosition,vNormal;
vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .x;
AI_SWAP4(vPosition.x);
vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .y;
AI_SWAP4(vPosition.y);
vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .z;
AI_SWAP4(vPosition.z);
if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[0];
AI_SWAP4(vNormal.x);
vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[1];
AI_SWAP4(vNormal.y);
vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[2];
AI_SWAP4(vNormal.z);
}
else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,
pcHeader->framevertex_stc_size) .norm162index,vNormal);
}
BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
unsigned int iOutIndex = 0;
for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
const unsigned int iCurIndex = pcGroupTris->v_index[c];
if (iCurIndex == iIndex) {
groupData.vPositions[iOutIndex] = vPosition;
groupData.vNormals[iOutIndex] = vNormal;
}
}
pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)
pcGroupTris + pcHeader->triangle_stc_size);
}
}
}
if (shared.apcOutBones) {
ParseBoneTrafoKeys_3DGS_MDL7(groupInfo,frame,shared);
}
szCurrent += iAdd;
}
*szCurrentOut = szCurrent;
return true;
}
void MDLImporter::SortByMaterials_3DGS_MDL7(
const MDL::IntGroupInfo_MDL7& groupInfo,
MDL::IntGroupData_MDL7& groupData,
MDL::IntSplitGroupData_MDL7& splitGroupData)
{
const unsigned int iNumMaterials = (unsigned int)splitGroupData.shared.pcMats.size();
if (!groupData.bNeed2UV) {
groupData.vTextureCoords2.clear();
splitGroupData.aiSplit = new std::vector<unsigned int>*[iNumMaterials];
for (unsigned int m = 0; m < iNumMaterials;++m)
splitGroupData.aiSplit[m] = new std::vector<unsigned int>();
for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
if (groupData.pcFaces[iFace].iMatIndex[0] >= iNumMaterials) {
splitGroupData.aiSplit[iNumMaterials-1]->push_back(iFace);
if(0xFFFFFFFF != groupData.pcFaces[iFace].iMatIndex[0])
DefaultLogger::get()->warn("Index overflow in MDL7 material list [#0]");
}
else splitGroupData.aiSplit[groupData.pcFaces[iFace].
iMatIndex[0]]->push_back(iFace);
}
}
else
{
std::vector<MDL::IntMaterial_MDL7> avMats;
avMats.reserve(iNumMaterials*2);
std::vector<std::vector<unsigned int>* > aiTempSplit(iNumMaterials*2);
for (unsigned int m = 0; m < iNumMaterials;++m)
aiTempSplit[m] = new std::vector<unsigned int>();
for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
unsigned int iMatIndex = groupData.pcFaces[iFace].iMatIndex[0];
if (iMatIndex >= iNumMaterials) {
if(UINT_MAX != iMatIndex)
DefaultLogger::get()->warn("Index overflow in MDL7 material list [#1]");
iMatIndex = iNumMaterials-1;
}
unsigned int iMatIndex2 = groupData.pcFaces[iFace].iMatIndex[1];
unsigned int iNum = iMatIndex;
if (UINT_MAX != iMatIndex2 && iMatIndex != iMatIndex2) {
if (iMatIndex2 >= iNumMaterials) {
DefaultLogger::get()->warn("Index overflow in MDL7 material list [#2]");
iMatIndex2 = iNumMaterials-1;
}
iNum = 0;
bool bFound = false;
for (std::vector<MDL::IntMaterial_MDL7>::iterator i = avMats.begin();i != avMats.end();++i,++iNum){
if ((*i).iOldMatIndices[0] == iMatIndex && (*i).iOldMatIndices[1] == iMatIndex2) {
bFound = true;
break;
}
}
if (!bFound) {
MDL::IntMaterial_MDL7 sHelper;
sHelper.pcMat = new aiMaterial();
sHelper.iOldMatIndices[0] = iMatIndex;
sHelper.iOldMatIndices[1] = iMatIndex2;
JoinSkins_3DGS_MDL7(splitGroupData.shared.pcMats[iMatIndex],
splitGroupData.shared.pcMats[iMatIndex2],sHelper.pcMat);
avMats.push_back(sHelper);
iNum = (unsigned int)avMats.size()-1;
}
if (iNum == aiTempSplit.size()) {
aiTempSplit.push_back(new std::vector<unsigned int>());
}
}
aiTempSplit[iNum]->push_back(iFace);
}
if (0 == groupInfo.iIndex) {
splitGroupData.shared.pcMats.resize(avMats.size());
for (unsigned int o = 0; o < avMats.size();++o)
splitGroupData.shared.pcMats[o] = avMats[o].pcMat;
}
else {
splitGroupData.shared.pcMats.resize(iNumMaterials + avMats.size());
for (unsigned int o = iNumMaterials; o < avMats.size();++o)
splitGroupData.shared.pcMats[o] = avMats[o].pcMat;
}
splitGroupData.aiSplit = new std::vector<unsigned int>*[aiTempSplit.size()];
for (unsigned int m = 0; m < iNumMaterials;++m)
splitGroupData.aiSplit[m] = aiTempSplit[m];
}
}
void MDLImporter::InternReadFile_3DGS_MDL7( )
{
ai_assert(NULL != pScene);
MDL::IntSharedData_MDL7 sharedData;
BE_NCONST MDL::Header_MDL7 *pcHeader = (BE_NCONST MDL::Header_MDL7*)this->mBuffer;
const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
AI_SWAP4(pcHeader->version);
AI_SWAP4(pcHeader->bones_num);
AI_SWAP4(pcHeader->groups_num);
AI_SWAP4(pcHeader->data_size);
AI_SWAP4(pcHeader->entlump_size);
AI_SWAP4(pcHeader->medlump_size);
AI_SWAP2(pcHeader->bone_stc_size);
AI_SWAP2(pcHeader->skin_stc_size);
AI_SWAP2(pcHeader->colorvalue_stc_size);
AI_SWAP2(pcHeader->material_stc_size);
AI_SWAP2(pcHeader->skinpoint_stc_size);
AI_SWAP2(pcHeader->triangle_stc_size);
AI_SWAP2(pcHeader->mainvertex_stc_size);
AI_SWAP2(pcHeader->framevertex_stc_size);
AI_SWAP2(pcHeader->bonetrans_stc_size);
AI_SWAP2(pcHeader->frame_stc_size);
this->ValidateHeader_3DGS_MDL7(pcHeader);
szCurrent += pcHeader->bones_num * pcHeader->bone_stc_size;
sharedData.apcOutBones = this->LoadBones_3DGS_MDL7();
std::vector<aiMesh*>* avOutList;
avOutList = new std::vector<aiMesh*>[pcHeader->groups_num];
for (uint32_t i = 0; i < pcHeader->groups_num;++i)
avOutList[i].reserve(3);
const size_t buffersize( AI_MDL7_MAX_GROUPNAMESIZE*pcHeader->groups_num );
char* aszGroupNameBuffer = new char[ buffersize ];
for (unsigned int iGroup = 0; iGroup < (unsigned int)pcHeader->groups_num;++iGroup) {
MDL::IntGroupInfo_MDL7 groupInfo((BE_NCONST MDL::Group_MDL7*)szCurrent,iGroup);
szCurrent = (const unsigned char*)(groupInfo.pcGroup+1);
VALIDATE_FILE_SIZE(szCurrent);
AI_SWAP4(groupInfo.pcGroup->groupdata_size);
AI_SWAP4(groupInfo.pcGroup->numskins);
AI_SWAP4(groupInfo.pcGroup->num_stpts);
AI_SWAP4(groupInfo.pcGroup->numtris);
AI_SWAP4(groupInfo.pcGroup->numverts);
AI_SWAP4(groupInfo.pcGroup->numframes);
if (1 != groupInfo.pcGroup->typ) {
DefaultLogger::get()->warn("[3DGS MDL7] Not a triangle mesh group. Continuing happily");
}
const unsigned int ofs = iGroup*AI_MDL7_MAX_GROUPNAMESIZE;
::memcpy(&aszGroupNameBuffer[ofs],
groupInfo.pcGroup->name,AI_MDL7_MAX_GROUPNAMESIZE);
aszGroupNameBuffer[ofs+AI_MDL7_MAX_GROUPNAMESIZE-1] = '\0';
sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins);
sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() +
groupInfo.pcGroup->numskins,false);
for (unsigned int iSkin = 0; iSkin < (unsigned int)groupInfo.pcGroup->numskins;++iSkin) {
ParseSkinLump_3DGS_MDL7(szCurrent,&szCurrent,sharedData.pcMats);
}
if (sharedData.pcMats.empty()) {
const int iMode = (int)aiShadingMode_Gouraud;
sharedData.pcMats.push_back(new aiMaterial());
aiMaterial* pcHelper = (aiMaterial*)sharedData.pcMats[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
sharedData.abNeedMaterials.resize(1,false);
}
groupInfo.pcGroupUVs = (BE_NCONST MDL::TexCoord_MDL7*)szCurrent;
for(int i = 0; i < groupInfo.pcGroup->num_stpts; ++i){
AI_SWAP4(groupInfo.pcGroupUVs[i].u);
AI_SWAP4(groupInfo.pcGroupUVs[i].v);
}
szCurrent += pcHeader->skinpoint_stc_size * groupInfo.pcGroup->num_stpts;
groupInfo.pcGroupTris = (Triangle_MDL7*)szCurrent;
szCurrent += pcHeader->triangle_stc_size * groupInfo.pcGroup->numtris;
groupInfo.pcGroupVerts = (BE_NCONST MDL::Vertex_MDL7*)szCurrent;
for(int i = 0; i < groupInfo.pcGroup->numverts; ++i){
AI_SWAP4(groupInfo.pcGroupVerts[i].x);
AI_SWAP4(groupInfo.pcGroupVerts[i].y);
AI_SWAP4(groupInfo.pcGroupVerts[i].z);
AI_SWAP2(groupInfo.pcGroupVerts[i].vertindex);
}
szCurrent += pcHeader->mainvertex_stc_size * groupInfo.pcGroup->numverts;
VALIDATE_FILE_SIZE(szCurrent);
MDL::IntSplitGroupData_MDL7 splitGroupData(sharedData,avOutList[iGroup]);
MDL::IntGroupData_MDL7 groupData;
if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts)
{
const unsigned int iNumVertices = groupInfo.pcGroup->numtris*3;
groupData.vPositions.resize(iNumVertices);
groupData.vNormals.resize(iNumVertices);
if (sharedData.apcOutBones)groupData.aiBones.resize(iNumVertices,UINT_MAX);
if (groupInfo.pcGroup->num_stpts){
groupData.vTextureCoords1.resize(iNumVertices,aiVector3D());
if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
groupData.vTextureCoords2.resize(iNumVertices,aiVector3D());
groupData.bNeed2UV = true;
}
}
groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris];
ReadFaces_3DGS_MDL7(groupInfo, groupData);
SortByMaterials_3DGS_MDL7(groupInfo, groupData,
splitGroupData);
for (unsigned int qq = 0; qq < sharedData.pcMats.size();++qq) {
if (!splitGroupData.aiSplit[qq]->empty())
sharedData.abNeedMaterials[qq] = true;
}
}
else DefaultLogger::get()->warn("[3DGS MDL7] Mesh group consists of 0 "
"vertices or faces. It will be skipped.");
ProcessFrames_3DGS_MDL7(groupInfo,groupData, sharedData,szCurrent,&szCurrent);
GenerateOutputMeshes_3DGS_MDL7(groupData,splitGroupData);
}
pScene->mRootNode = new aiNode();
for (uint32_t i = 0; i < pcHeader->groups_num;++i)
pScene->mNumMeshes += (unsigned int)avOutList[i].size();
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; {
unsigned int p = 0,q = 0;
for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
for (unsigned int a = 0; a < avOutList[i].size();++a) {
pScene->mMeshes[p++] = avOutList[i][a];
}
if (!avOutList[i].empty())++pScene->mRootNode->mNumChildren;
}
if (sharedData.apcOutBones)++pScene->mRootNode->mNumChildren;
this->pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
p = 0;
for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
if (avOutList[i].empty())continue;
aiNode* const pcNode = pScene->mRootNode->mChildren[p] = new aiNode();
pcNode->mNumMeshes = (unsigned int)avOutList[i].size();
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
pcNode->mParent = this->pScene->mRootNode;
for (unsigned int a = 0; a < pcNode->mNumMeshes;++a)
pcNode->mMeshes[a] = q + a;
q += (unsigned int)avOutList[i].size();
char* const szBuffer = &aszGroupNameBuffer[i*AI_MDL7_MAX_GROUPNAMESIZE];
if ('\0' == *szBuffer) {
const size_t maxSize(buffersize - (i*AI_MDL7_MAX_GROUPNAMESIZE));
pcNode->mName.length = ai_snprintf(szBuffer, maxSize, "Group_%u", p);
} else {
pcNode->mName.length = ::strlen(szBuffer);
}
::strncpy(pcNode->mName.data,szBuffer,MAXLEN-1);
++p;
}
}
if (1 == pScene->mRootNode->mNumChildren && !sharedData.apcOutBones) {
aiNode* pcOldRoot = this->pScene->mRootNode;
pScene->mRootNode = pcOldRoot->mChildren[0];
pcOldRoot->mChildren[0] = NULL;
delete pcOldRoot;
pScene->mRootNode->mParent = NULL;
}
else pScene->mRootNode->mName.Set("<mesh_root>");
delete[] avOutList;
delete[] aszGroupNameBuffer;
AI_DEBUG_INVALIDATE_PTR(avOutList);
AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
CopyMaterials_3DGS_MDL7(sharedData);
HandleMaterialReferences_3DGS_MDL7();
if (sharedData.apcOutBones) {
aiNode* const pc = pScene->mRootNode->mChildren[pScene->mRootNode->mNumChildren-1] = new aiNode();
pc->mName.Set("<skeleton_root>");
AddBonesToNodeGraph_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
sharedData.apcOutBones,pc,0xffff);
BuildOutputAnims_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
sharedData.apcOutBones);
}
}
void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared)
{
pScene->mNumMaterials = (unsigned int)shared.pcMats.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
pScene->mMaterials[i] = shared.pcMats[i];
}
void MDLImporter::HandleMaterialReferences_3DGS_MDL7()
{
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
int iIndex = 0;
if (AI_SUCCESS == aiGetMaterialInteger(pScene->mMaterials[i],AI_MDL7_REFERRER_MATERIAL, &iIndex) ) {
for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
aiMesh* const pcMesh = pScene->mMeshes[a];
if (i == pcMesh->mMaterialIndex) {
pcMesh->mMaterialIndex = iIndex;
}
}
delete pScene->mMaterials[i];
for (unsigned int pp = i; pp < pScene->mNumMaterials-1;++pp) {
pScene->mMaterials[pp] = pScene->mMaterials[pp+1];
for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
aiMesh* const pcMesh = pScene->mMeshes[a];
if (pcMesh->mMaterialIndex > i)--pcMesh->mMaterialIndex;
}
}
--pScene->mNumMaterials;
}
}
}
void MDLImporter::ParseBoneTrafoKeys_3DGS_MDL7(
const MDL::IntGroupInfo_MDL7& groupInfo,
IntFrameInfo_MDL7& frame,
MDL::IntSharedData_MDL7& shared)
{
const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
if (frame.pcFrame->transmatrix_count) {
if (!groupInfo.iIndex) {
const MDL::BoneTransform_MDL7* pcBoneTransforms = (const MDL::BoneTransform_MDL7*)
(((const char*)frame.pcFrame) + pcHeader->frame_stc_size +
frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size);
for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count;++iTrafo) {
if(pcBoneTransforms->bone_index >= pcHeader->bones_num) {
DefaultLogger::get()->warn("Index overflow in frame area. "
"Unable to parse this bone transformation");
}
else {
AddAnimationBoneTrafoKey_3DGS_MDL7(frame.iIndex,
pcBoneTransforms,shared.apcOutBones);
}
pcBoneTransforms = (const MDL::BoneTransform_MDL7*)(
(const char*)pcBoneTransforms + pcHeader->bonetrans_stc_size);
}
}
else {
DefaultLogger::get()->warn("Ignoring animation keyframes in groups != 0");
}
}
}
void MDLImporter::AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBones,
aiNode* pcParent,uint16_t iParentIndex)
{
ai_assert(NULL != apcBones && NULL != pcParent);
const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
const MDL::IntBone_MDL7** apcBones2 = apcBones;
for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
const MDL::IntBone_MDL7* const pcBone = *apcBones2++;
if (pcBone->iParent == iParentIndex) {
++pcParent->mNumChildren;
}
}
pcParent->mChildren = new aiNode*[pcParent->mNumChildren];
unsigned int qq = 0;
for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
const MDL::IntBone_MDL7* const pcBone = *apcBones++;
if (pcBone->iParent != iParentIndex)continue;
aiNode* pcNode = pcParent->mChildren[qq++] = new aiNode();
pcNode->mName = aiString( pcBone->mName );
AddBonesToNodeGraph_3DGS_MDL7(apcBones,pcNode,(uint16_t)i);
}
}
void MDLImporter::BuildOutputAnims_3DGS_MDL7(
const MDL::IntBone_MDL7** apcBonesOut)
{
ai_assert(NULL != apcBonesOut);
const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)mBuffer;
aiAnimation* pcAnim = new aiAnimation();
for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
if (!apcBonesOut[i]->pkeyPositions.empty()) {
for (size_t qq = 0; qq < apcBonesOut[i]->pkeyPositions.size();++qq) {
pcAnim->mDuration = std::max(pcAnim->mDuration, (double)
apcBonesOut[i]->pkeyPositions[qq].mTime);
}
++pcAnim->mNumChannels;
}
}
if (pcAnim->mDuration) {
pcAnim->mChannels = new aiNodeAnim*[pcAnim->mNumChannels];
unsigned int iCnt = 0;
for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
if (!apcBonesOut[i]->pkeyPositions.empty()) {
const MDL::IntBone_MDL7* const intBone = apcBonesOut[i];
aiNodeAnim* const pcNodeAnim = pcAnim->mChannels[iCnt++] = new aiNodeAnim();
pcNodeAnim->mNodeName = aiString( intBone->mName );
pcNodeAnim->mNumPositionKeys = (unsigned int)intBone->pkeyPositions.size();
pcNodeAnim->mNumScalingKeys = (unsigned int)intBone->pkeyPositions.size();
pcNodeAnim->mNumRotationKeys = (unsigned int)intBone->pkeyPositions.size();
pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys];
for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys;++qq) {
pcNodeAnim->mPositionKeys[qq] = intBone->pkeyPositions[qq];
pcNodeAnim->mScalingKeys[qq] = intBone->pkeyScalings[qq];
pcNodeAnim->mRotationKeys[qq] = intBone->pkeyRotations[qq];
}
}
}
pScene->mNumAnimations = 1;
pScene->mAnimations = new aiAnimation*[1];
pScene->mAnimations[0] = pcAnim;
}
else delete pcAnim;
}
void MDLImporter::AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
const MDL::BoneTransform_MDL7* pcBoneTransforms,
MDL::IntBone_MDL7** apcBonesOut)
{
ai_assert(NULL != pcBoneTransforms);
ai_assert(NULL != apcBonesOut);
aiMatrix4x4 mTransform;
mTransform.a1 = pcBoneTransforms->m[0];
mTransform.b1 = pcBoneTransforms->m[1];
mTransform.c1 = pcBoneTransforms->m[2];
mTransform.d1 = pcBoneTransforms->m[3];
mTransform.a2 = pcBoneTransforms->m[4];
mTransform.b2 = pcBoneTransforms->m[5];
mTransform.c2 = pcBoneTransforms->m[6];
mTransform.d2 = pcBoneTransforms->m[7];
mTransform.a3 = pcBoneTransforms->m[8];
mTransform.b3 = pcBoneTransforms->m[9];
mTransform.c3 = pcBoneTransforms->m[10];
mTransform.d3 = pcBoneTransforms->m[11];
aiVectorKey vScaling,vPosition;
aiQuatKey qRotation;
mTransform.Decompose(vScaling.mValue,qRotation.mValue,vPosition.mValue);
vScaling.mTime = qRotation.mTime = vPosition.mTime = (double)iTrafo;
MDL::IntBone_MDL7* const pcBoneOut = apcBonesOut[pcBoneTransforms->bone_index];
pcBoneOut->pkeyPositions.push_back ( vPosition );
pcBoneOut->pkeyScalings.push_back ( vScaling );
pcBoneOut->pkeyRotations.push_back ( qRotation );
}
void MDLImporter::GenerateOutputMeshes_3DGS_MDL7(
MDL::IntGroupData_MDL7& groupData,
MDL::IntSplitGroupData_MDL7& splitGroupData)
{
const MDL::IntSharedData_MDL7& shared = splitGroupData.shared;
const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
const unsigned int iNumOutBones = pcHeader->bones_num;
for (std::vector<aiMaterial*>::size_type i = 0; i < shared.pcMats.size();++i) {
if (!splitGroupData.aiSplit[i]->empty()) {
aiMesh* pcMesh = new aiMesh();
pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
pcMesh->mMaterialIndex = (unsigned int)i;
pcMesh->mNumFaces = (unsigned int)splitGroupData.aiSplit[i]->size();
pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
pcMesh->mNumVertices = pcMesh->mNumFaces*3;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
if (!groupData.vTextureCoords1.empty()) {
pcMesh->mNumUVComponents[0] = 2;
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
if (!groupData.vTextureCoords2.empty()) {
pcMesh->mNumUVComponents[1] = 2;
pcMesh->mTextureCoords[1] = new aiVector3D[pcMesh->mNumVertices];
}
}
unsigned int iCurrent = 0;
for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
pcMesh->mFaces[iFace].mNumIndices = 3;
pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace);
const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
for (unsigned int c = 0; c < 3;++c) {
const uint32_t iIndex = oldFace.mIndices[c];
pcMesh->mVertices[iCurrent] = groupData.vPositions[iIndex];
pcMesh->mNormals[iCurrent] = groupData.vNormals[iIndex];
if (!groupData.vTextureCoords1.empty()) {
pcMesh->mTextureCoords[0][iCurrent] = groupData.vTextureCoords1[iIndex];
if (!groupData.vTextureCoords2.empty()) {
pcMesh->mTextureCoords[1][iCurrent] = groupData.vTextureCoords2[iIndex];
}
}
pcMesh->mFaces[iFace].mIndices[c] = iCurrent++;
}
}
if (!groupData.aiBones.empty()) {
std::vector<std::vector<unsigned int> > aaiVWeightList;
aaiVWeightList.resize(iNumOutBones);
int iCurrent = 0;
for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
unsigned int iSrcFace = splitGroupData.aiSplit[i]->operator[](iFace);
const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
for (unsigned int c = 0; c < 3;++c) {
unsigned int iBone = groupData.aiBones[ oldFace.mIndices[c] ];
if (UINT_MAX != iBone) {
if (iBone >= iNumOutBones) {
DefaultLogger::get()->error("Bone index overflow. "
"The bone index of a vertex exceeds the allowed range. ");
iBone = iNumOutBones-1;
}
aaiVWeightList[ iBone ].push_back ( iCurrent );
}
++iCurrent;
}
}
for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k != aaiVWeightList.end();++k) {
if (!(*k).empty()) {
++pcMesh->mNumBones;
}
}
pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
iCurrent = 0;
for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k!= aaiVWeightList.end();++k,++iCurrent)
{
if ((*k).empty())
continue;
aiBone* pcBone = pcMesh->mBones[ iCurrent ] = new aiBone();
pcBone->mName = aiString(shared.apcOutBones[ iCurrent ]->mName);
pcBone->mOffsetMatrix = shared.apcOutBones[ iCurrent ]->mOffsetMatrix;
pcBone->mNumWeights = (unsigned int)(*k).size();
pcBone->mWeights = new aiVertexWeight[pcBone->mNumWeights];
for (unsigned int weight = 0; weight < pcBone->mNumWeights;++weight) {
pcBone->mWeights[weight].mVertexId = (*k)[weight];
pcBone->mWeights[weight].mWeight = 1.0f;
}
}
}
splitGroupData.avOutList.push_back(pcMesh);
}
}
}
void MDLImporter::JoinSkins_3DGS_MDL7(
aiMaterial* pcMat1,
aiMaterial* pcMat2,
aiMaterial* pcMatOut)
{
ai_assert(NULL != pcMat1 && NULL != pcMat2 && NULL != pcMatOut);
aiMaterial::CopyPropertyList(pcMatOut,pcMat1);
int iVal = 0;
pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
aiString sString;
if(AI_SUCCESS == aiGetMaterialString ( pcMat2, AI_MATKEY_TEXTURE_DIFFUSE(0),&sString )) {
iVal = 1;
pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
pcMatOut->AddProperty(&sString,AI_MATKEY_TEXTURE_DIFFUSE(1));
}
}
void MDLImporter::InternReadFile_HL2( )
{
throw DeadlyImportError("HL2 MDLs are not implemented");
}
#endif