#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
#include "MDCLoader.h"
#include "MD3FileData.h"
#include "MDCNormalTable.h"
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/importerdesc.h>
#include <memory>
using namespace Assimp;
using namespace Assimp::MDC;
static const aiImporterDesc desc = {
"Return To Castle Wolfenstein Mesh Importer",
"",
"",
"",
aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"mdc"
};
void MDC::BuildVertex(const Frame& frame,
const BaseVertex& bvert,
const CompressedVertex& cvert,
aiVector3D& vXYZOut,
aiVector3D& vNorOut)
{
const float xd = (cvert.xd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
const float yd = (cvert.yd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
const float zd = (cvert.zd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
vXYZOut.x = frame.localOrigin.x + AI_MDC_BASE_SCALING * (bvert.x + xd);
vXYZOut.y = frame.localOrigin.y + AI_MDC_BASE_SCALING * (bvert.y + yd);
vXYZOut.z = frame.localOrigin.z + AI_MDC_BASE_SCALING * (bvert.z + zd);
vNorOut.x = mdcNormals[cvert.nd][0];
vNorOut.y = mdcNormals[cvert.nd][1];
vNorOut.z = mdcNormals[cvert.nd][2];
}
MDCImporter::MDCImporter()
: configFrameID(),
pcHeader(),
mBuffer(),
fileSize()
{
}
MDCImporter::~MDCImporter()
{
}
bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string extension = GetExtension(pFile);
if (extension == "mdc")
return true;
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,1);
}
return false;
}
const aiImporterDesc* MDCImporter::GetInfo () const
{
return &desc;
}
void MDCImporter::ValidateHeader()
{
AI_SWAP4( this->pcHeader->ulVersion );
AI_SWAP4( this->pcHeader->ulFlags );
AI_SWAP4( this->pcHeader->ulNumFrames );
AI_SWAP4( this->pcHeader->ulNumTags );
AI_SWAP4( this->pcHeader->ulNumSurfaces );
AI_SWAP4( this->pcHeader->ulNumSkins );
AI_SWAP4( this->pcHeader->ulOffsetBorderFrames );
if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE &&
pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE)
{
char szBuffer[5];
szBuffer[0] = ((char*)&pcHeader->ulIdent)[0];
szBuffer[1] = ((char*)&pcHeader->ulIdent)[1];
szBuffer[2] = ((char*)&pcHeader->ulIdent)[2];
szBuffer[3] = ((char*)&pcHeader->ulIdent)[3];
szBuffer[4] = '\0';
throw DeadlyImportError("Invalid MDC magic word: should be IDPC, the "
"magic word found is " + std::string( szBuffer ));
}
if (pcHeader->ulVersion != AI_MDC_VERSION)
DefaultLogger::get()->warn("Unsupported MDC file version (2 (AI_MDC_VERSION) was expected)");
if (pcHeader->ulOffsetBorderFrames + pcHeader->ulNumFrames * sizeof(MDC::Frame) > this->fileSize ||
pcHeader->ulOffsetSurfaces + pcHeader->ulNumSurfaces * sizeof(MDC::Surface) > this->fileSize)
{
throw DeadlyImportError("Some of the offset values in the MDC header are invalid "
"and point to something behind the file.");
}
if (this->configFrameID >= this->pcHeader->ulNumFrames)
throw DeadlyImportError("The requested frame is not available");
}
void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf)
{
AI_SWAP4(pcSurf->ulFlags);
AI_SWAP4(pcSurf->ulNumCompFrames);
AI_SWAP4(pcSurf->ulNumBaseFrames);
AI_SWAP4(pcSurf->ulNumShaders);
AI_SWAP4(pcSurf->ulNumVertices);
AI_SWAP4(pcSurf->ulNumTriangles);
AI_SWAP4(pcSurf->ulOffsetTriangles);
AI_SWAP4(pcSurf->ulOffsetTexCoords);
AI_SWAP4(pcSurf->ulOffsetBaseVerts);
AI_SWAP4(pcSurf->ulOffsetCompVerts);
AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames);
AI_SWAP4(pcSurf->ulOffsetFrameCompFrames);
AI_SWAP4(pcSurf->ulOffsetEnd);
const unsigned int iMax = this->fileSize - (unsigned int)((int8_t*)pcSurf-(int8_t*)pcHeader);
if (pcSurf->ulOffsetBaseVerts + pcSurf->ulNumVertices * sizeof(MDC::BaseVertex) > iMax ||
(pcSurf->ulNumCompFrames && pcSurf->ulOffsetCompVerts + pcSurf->ulNumVertices * sizeof(MDC::CompressedVertex) > iMax) ||
pcSurf->ulOffsetTriangles + pcSurf->ulNumTriangles * sizeof(MDC::Triangle) > iMax ||
pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax ||
pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax ||
pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax ||
(pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax))
{
throw DeadlyImportError("Some of the offset values in the MDC surface header "
"are invalid and point somewhere behind the file.");
}
}
void MDCImporter::SetupProperties(const Importer* pImp)
{
if(static_cast<unsigned int>(-1) == (configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDC_KEYFRAME,-1))){
configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
}
}
void MDCImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
if( file.get() == NULL)
throw DeadlyImportError( "Failed to open MDC file " + pFile + ".");
fileSize = (unsigned int)file->FileSize();
if( fileSize < sizeof(MDC::Header))
throw DeadlyImportError( "MDC File is too small.");
std::vector<unsigned char> mBuffer2(fileSize);
file->Read( &mBuffer2[0], 1, fileSize);
mBuffer = &mBuffer2[0];
this->pcHeader = (BE_NCONST MDC::Header*)this->mBuffer;
this->ValidateHeader();
std::vector<std::string> aszShaders;
BE_NCONST MDC::Frame* pcFrame = (BE_NCONST MDC::Frame*)(this->mBuffer+
this->pcHeader->ulOffsetBorderFrames);
pcFrame += configFrameID;
AI_SWAP4( pcFrame->localOrigin[0] );
AI_SWAP4( pcFrame->localOrigin[1] );
AI_SWAP4( pcFrame->localOrigin[2] );
BE_NCONST MDC::Surface* pcSurface, *pcSurface2;
pcSurface = pcSurface2 = new (mBuffer + pcHeader->ulOffsetSurfaces) MDC::Surface;
unsigned int iNumShaders = 0;
for (unsigned int i = 0; i < pcHeader->ulNumSurfaces;++i)
{
this->ValidateSurfaceHeader(pcSurface2);
if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles)++pScene->mNumMeshes;
iNumShaders += pcSurface2->ulNumShaders;
pcSurface2 = new ((int8_t*)pcSurface2 + pcSurface2->ulOffsetEnd) MDC::Surface;
}
aszShaders.reserve(iNumShaders);
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i] = NULL;
unsigned int iDefaultMatIndex = UINT_MAX;
for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces;++i)
{
if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles)continue;
aiMesh* pcMesh = pScene->mMeshes[iNum++] = new aiMesh();
pcMesh->mNumFaces = pcSurface->ulNumTriangles;
pcMesh->mNumVertices = pcMesh->mNumFaces * 3;
const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0';
pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName;
if (pcSurface->ulNumShaders)
{
const MDC::Shader* pcShader = (const MDC::Shader*)((int8_t*)pcSurface + pcSurface->ulOffsetShaders);
pcMesh->mMaterialIndex = (unsigned int)aszShaders.size();
aszShaders.push_back(std::string( pcShader->ucName, std::min(
::strlen(pcShader->ucName),sizeof(pcShader->ucName)) ));
}
else if (UINT_MAX == iDefaultMatIndex)
{
pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size();
aszShaders.push_back(std::string());
}
else pcMesh->mMaterialIndex = iDefaultMatIndex;
aiVector3D* pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
aiVector3D* pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
aiVector3D* pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
aiFace* pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
BE_NCONST MDC::Triangle* pcTriangle = (BE_NCONST MDC::Triangle*)
((int8_t*)pcSurface+pcSurface->ulOffsetTriangles);
BE_NCONST MDC::TexturCoord* const pcUVs = (BE_NCONST MDC::TexturCoord*)
((int8_t*)pcSurface+pcSurface->ulOffsetTexCoords);
int16_t iOfs = *((int16_t*) ((int8_t*) pcSurface +
pcSurface->ulOffsetFrameBaseFrames) + this->configFrameID);
AI_SWAP2(iOfs);
BE_NCONST MDC::BaseVertex* const pcVerts = (BE_NCONST MDC::BaseVertex*)
((int8_t*)pcSurface+pcSurface->ulOffsetBaseVerts) +
((int)iOfs * pcSurface->ulNumVertices * 4);
#if (defined AI_BUILD_BIG_ENDIAN)
for (unsigned int i = 0; i < pcSurface->ulNumTriangles;++i)
{
AI_SWAP4( pcTriangle[i].aiIndices[0] );
AI_SWAP4( pcTriangle[i].aiIndices[1] );
AI_SWAP4( pcTriangle[i].aiIndices[2] );
}
for (unsigned int i = 0; i < pcSurface->ulNumVertices*pcSurface->ulNumBaseFrames;++i)
{
AI_SWAP2( pcVerts->normal );
AI_SWAP2( pcVerts->x );
AI_SWAP2( pcVerts->y );
AI_SWAP2( pcVerts->z );
}
for (unsigned int i = 0; i < pcSurface->ulNumVertices;++i)
{
AI_SWAP4( pcUVs->v );
AI_SWAP4( pcUVs->v );
}
#endif
const MDC::CompressedVertex* pcCVerts = NULL;
int16_t* mdcCompVert = NULL;
if( this->configFrameID && pcSurface->ulNumCompFrames > 0 )
{
mdcCompVert = (int16_t*) ((int8_t*)pcSurface+pcSurface->ulOffsetFrameCompFrames) + this->configFrameID;
AI_SWAP2P(mdcCompVert);
if( *mdcCompVert >= 0 )
{
pcCVerts = (const MDC::CompressedVertex*)((int8_t*)pcSurface +
pcSurface->ulOffsetCompVerts) + *mdcCompVert * pcSurface->ulNumVertices;
}
else mdcCompVert = NULL;
}
for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles;++iFace,
++pcTriangle,++pcFaceCur)
{
const unsigned int iOutIndex = iFace*3;
pcFaceCur->mNumIndices = 3;
pcFaceCur->mIndices = new unsigned int[3];
for (unsigned int iIndex = 0; iIndex < 3;++iIndex,
++pcVertCur,++pcUVCur,++pcNorCur)
{
uint32_t quak = pcTriangle->aiIndices[iIndex];
if (quak >= pcSurface->ulNumVertices)
{
DefaultLogger::get()->error("MDC vertex index is out of range");
quak = pcSurface->ulNumVertices-1;
}
if (mdcCompVert)
{
MDC::BuildVertex(*pcFrame,pcVerts[quak],pcCVerts[quak],
*pcVertCur,*pcNorCur);
}
else
{
pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING;
pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING;
pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING;
MD3::LatLngNormalToVec3( pcVerts[quak].normal, &pcNorCur->x );
pcUVCur->x = pcUVs[quak].u;
pcUVCur->y = ai_real( 1.0 )-pcUVs[quak].v; }
pcVertCur->x += pcFrame->localOrigin[0] ;
pcVertCur->y += pcFrame->localOrigin[1] ;
pcVertCur->z += pcFrame->localOrigin[2] ;
}
pcFaceCur->mIndices[0] = iOutIndex + 2;
pcFaceCur->mIndices[1] = iOutIndex + 1;
pcFaceCur->mIndices[2] = iOutIndex + 0;
}
pcSurface = new ((int8_t*)pcSurface + pcSurface->ulOffsetEnd) MDC::Surface;
}
if (!pScene->mNumMeshes)
throw DeadlyImportError( "Invalid MDC file: File contains no valid mesh");
else if (1 == pScene->mNumMeshes)
{
pScene->mRootNode = new aiNode();
pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3]));
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
}
else
{
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
pScene->mRootNode->mName.Set("<root>");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode();
pcNode->mParent = pScene->mRootNode;
pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3]));
pcNode->mNumMeshes = 1;
pcNode->mMeshes = new unsigned int[1];
pcNode->mMeshes[0] = i;
}
}
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i]->mTextureCoords[3] = NULL;
pScene->mNumMaterials = (unsigned int)aszShaders.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{
aiMaterial* pcMat = new aiMaterial();
pScene->mMaterials[i] = pcMat;
const std::string& name = aszShaders[i];
int iMode = (int)aiShadingMode_Gouraud;
pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.05f;
pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
if (name.length())clr.b = clr.g = clr.r = 1.0f;
else clr.b = clr.g = clr.r = 0.6f;
pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
if (name.length())
{
aiString path;
path.Set(name);
pcMat->AddProperty(&path,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
}
}
#endif