#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_MD2_LOADER_
#include "CMD2MeshFileLoader.h"
#include "CAnimatedMeshMD2.h"
#include "os.h"
namespace irr
{
namespace scene
{
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( push, packing )
# pragma pack( 1 )
# define PACK_STRUCT
#elif defined( __GNUC__ )
# define PACK_STRUCT __attribute__((packed))
#else
# error compiler not supported
#endif
const s32 MD2_MAGIC_NUMBER = 844121161;
const s32 MD2_VERSION = 8;
const s32 MD2_MAX_VERTS = 2048;
struct SMD2Header
{
s32 magic;
s32 version;
s32 skinWidth;
s32 skinHeight;
s32 frameSize;
s32 numSkins;
s32 numVertices;
s32 numTexcoords;
s32 numTriangles;
s32 numGlCommands;
s32 numFrames;
s32 offsetSkins;
s32 offsetTexcoords;
s32 offsetTriangles;
s32 offsetFrames;
s32 offsetGlCommands;
s32 offsetEnd;
} PACK_STRUCT;
struct SMD2Vertex
{
u8 vertex[3];
u8 lightNormalIndex;
} PACK_STRUCT;
struct SMD2Frame
{
f32 scale[3];
f32 translate[3];
c8 name[16];
SMD2Vertex vertices[1];
} PACK_STRUCT;
struct SMD2Triangle
{
u16 vertexIndices[3];
u16 textureIndices[3];
} PACK_STRUCT;
struct SMD2TextureCoordinate
{
s16 s;
s16 t;
} PACK_STRUCT;
struct SMD2GLCommand
{
f32 s, t;
s32 vertexIndex;
} PACK_STRUCT;
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop, packing )
#endif
#undef PACK_STRUCT
CMD2MeshFileLoader::CMD2MeshFileLoader()
{
#ifdef _DEBUG
setDebugName("CMD2MeshFileLoader");
#endif
}
bool CMD2MeshFileLoader::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "md2" );
}
IAnimatedMesh* CMD2MeshFileLoader::createMesh(io::IReadFile* file)
{
IAnimatedMesh* msh = new CAnimatedMeshMD2();
if (msh)
{
if (loadFile(file, (CAnimatedMeshMD2*)msh) )
return msh;
msh->drop();
}
return 0;
}
bool CMD2MeshFileLoader::loadFile(io::IReadFile* file, CAnimatedMeshMD2* mesh)
{
if (!file)
return false;
SMD2Header header;
file->read(&header, sizeof(SMD2Header));
#ifdef __BIG_ENDIAN__
header.magic = os::Byteswap::byteswap(header.magic);
header.version = os::Byteswap::byteswap(header.version);
header.skinWidth = os::Byteswap::byteswap(header.skinWidth);
header.skinHeight = os::Byteswap::byteswap(header.skinHeight);
header.frameSize = os::Byteswap::byteswap(header.frameSize);
header.numSkins = os::Byteswap::byteswap(header.numSkins);
header.numVertices = os::Byteswap::byteswap(header.numVertices);
header.numTexcoords = os::Byteswap::byteswap(header.numTexcoords);
header.numTriangles = os::Byteswap::byteswap(header.numTriangles);
header.numGlCommands = os::Byteswap::byteswap(header.numGlCommands);
header.numFrames = os::Byteswap::byteswap(header.numFrames);
header.offsetSkins = os::Byteswap::byteswap(header.offsetSkins);
header.offsetTexcoords = os::Byteswap::byteswap(header.offsetTexcoords);
header.offsetTriangles = os::Byteswap::byteswap(header.offsetTriangles);
header.offsetFrames = os::Byteswap::byteswap(header.offsetFrames);
header.offsetGlCommands = os::Byteswap::byteswap(header.offsetGlCommands);
header.offsetEnd = os::Byteswap::byteswap(header.offsetEnd);
#endif
if (header.magic != MD2_MAGIC_NUMBER || header.version != MD2_VERSION)
{
os::Printer::log("MD2 Loader: Wrong file header", file->getFileName(), ELL_WARNING);
return false;
}
mesh->FrameCount = header.numFrames;
mesh->FrameTransforms.set_used(header.numFrames);
if (mesh->FrameList)
delete [] mesh->FrameList;
mesh->FrameList = new core::array<CAnimatedMeshMD2::SMD2Vert>[header.numFrames];
s32 i;
for (i=0; i<header.numFrames; ++i)
mesh->FrameList[i].reallocate(header.numVertices);
mesh->InterpolationBuffer->Vertices.set_used(header.numTriangles*3);
mesh->InterpolationBuffer->Indices.reallocate(header.numTriangles*3);
const s32 count = header.numTriangles*3;
for (i=0; i<count; i+=3)
{
mesh->InterpolationBuffer->Indices.push_back(i);
mesh->InterpolationBuffer->Indices.push_back(i+1);
mesh->InterpolationBuffer->Indices.push_back(i+2);
}
file->seek(header.offsetTexcoords);
SMD2TextureCoordinate* textureCoords = new SMD2TextureCoordinate[header.numTexcoords];
if (!file->read(textureCoords, sizeof(SMD2TextureCoordinate)*header.numTexcoords))
{
delete[] textureCoords;
os::Printer::log("MD2 Loader: Error reading TextureCoords.", file->getFileName(), ELL_ERROR);
return false;
}
#ifdef __BIG_ENDIAN__
for (i=0; i<header.numTexcoords; ++i)
{
textureCoords[i].s = os::Byteswap::byteswap(textureCoords[i].s);
textureCoords[i].t = os::Byteswap::byteswap(textureCoords[i].t);
}
#endif
file->seek(header.offsetTriangles);
SMD2Triangle *triangles = new SMD2Triangle[header.numTriangles];
if (!file->read(triangles, header.numTriangles *sizeof(SMD2Triangle)))
{
delete[] triangles;
delete[] textureCoords;
os::Printer::log("MD2 Loader: Error reading triangles.", file->getFileName(), ELL_ERROR);
return false;
}
#ifdef __BIG_ENDIAN__
for (i=0; i<header.numTriangles; ++i)
{
triangles[i].vertexIndices[0] = os::Byteswap::byteswap(triangles[i].vertexIndices[0]);
triangles[i].vertexIndices[1] = os::Byteswap::byteswap(triangles[i].vertexIndices[1]);
triangles[i].vertexIndices[2] = os::Byteswap::byteswap(triangles[i].vertexIndices[2]);
triangles[i].textureIndices[0] = os::Byteswap::byteswap(triangles[i].textureIndices[0]);
triangles[i].textureIndices[1] = os::Byteswap::byteswap(triangles[i].textureIndices[1]);
triangles[i].textureIndices[2] = os::Byteswap::byteswap(triangles[i].textureIndices[2]);
}
#endif
u8 buffer[MD2_MAX_VERTS*4+128];
SMD2Frame* frame = (SMD2Frame*)buffer;
file->seek(header.offsetFrames);
for (i = 0; i<header.numFrames; ++i)
{
file->read(frame, header.frameSize);
#ifdef __BIG_ENDIAN__
frame->scale[0] = os::Byteswap::byteswap(frame->scale[0]);
frame->scale[1] = os::Byteswap::byteswap(frame->scale[1]);
frame->scale[2] = os::Byteswap::byteswap(frame->scale[2]);
frame->translate[0] = os::Byteswap::byteswap(frame->translate[0]);
frame->translate[1] = os::Byteswap::byteswap(frame->translate[1]);
frame->translate[2] = os::Byteswap::byteswap(frame->translate[2]);
#endif
CAnimatedMeshMD2::SAnimationData adata;
adata.begin = i;
adata.end = i;
adata.fps = 7;
if (frame->name[0])
{
for (s32 s = 0; s < 16 && frame->name[s]!=0 && (frame->name[s] < '0' || frame->name[s] > '9'); ++s)
{
adata.name += frame->name[s];
}
if (!mesh->AnimationData.empty() && mesh->AnimationData[mesh->AnimationData.size()-1].name == adata.name)
{
++mesh->AnimationData[mesh->AnimationData.size() - 1].end;
}
else
{
mesh->AnimationData.push_back(adata);
}
}
mesh->FrameTransforms[i].scale.X = frame->scale[0];
mesh->FrameTransforms[i].scale.Z = frame->scale[1];
mesh->FrameTransforms[i].scale.Y = frame->scale[2];
mesh->FrameTransforms[i].translate.X = frame->translate[0];
mesh->FrameTransforms[i].translate.Z = frame->translate[1];
mesh->FrameTransforms[i].translate.Y = frame->translate[2];
for (s32 j=0; j<header.numTriangles; ++j)
{
for (u32 ti=0; ti<3; ++ti)
{
CAnimatedMeshMD2::SMD2Vert v;
u32 num = triangles[j].vertexIndices[ti];
v.Pos.X = frame->vertices[num].vertex[0];
v.Pos.Z = frame->vertices[num].vertex[1];
v.Pos.Y = frame->vertices[num].vertex[2];
v.NormalIdx = frame->vertices[num].lightNormalIndex;
mesh->FrameList[i].push_back(v);
}
}
if (header.numVertices)
{
core::aabbox3d<f32> box;
core::vector3df pos;
pos.X = f32(mesh->FrameList[i] [0].Pos.X) * mesh->FrameTransforms[i].scale.X + mesh->FrameTransforms[i].translate.X;
pos.Y = f32(mesh->FrameList[i] [0].Pos.Y) * mesh->FrameTransforms[i].scale.Y + mesh->FrameTransforms[i].translate.Y;
pos.Z = f32(mesh->FrameList[i] [0].Pos.Z) * mesh->FrameTransforms[i].scale.Z + mesh->FrameTransforms[i].translate.Z;
box.reset(pos);
for (s32 j=1; j<header.numTriangles*3; ++j)
{
pos.X = f32(mesh->FrameList[i] [j].Pos.X) * mesh->FrameTransforms[i].scale.X + mesh->FrameTransforms[i].translate.X;
pos.Y = f32(mesh->FrameList[i] [j].Pos.Y) * mesh->FrameTransforms[i].scale.Y + mesh->FrameTransforms[i].translate.Y;
pos.Z = f32(mesh->FrameList[i] [j].Pos.Z) * mesh->FrameTransforms[i].scale.Z + mesh->FrameTransforms[i].translate.Z;
box.addInternalPoint(pos);
}
mesh->BoxList.push_back(box);
}
}
if (header.numFrames)
{
f32 dmaxs = 1.0f/(header.skinWidth);
f32 dmaxt = 1.0f/(header.skinHeight);
for (s32 t=0; t<header.numTriangles; ++t)
{
for (s32 n=0; n<3; ++n)
{
mesh->InterpolationBuffer->Vertices[t*3 + n].TCoords.X = (textureCoords[triangles[t].textureIndices[n]].s + 0.5f) * dmaxs;
mesh->InterpolationBuffer->Vertices[t*3 + n].TCoords.Y = (textureCoords[triangles[t].textureIndices[n]].t + 0.5f) * dmaxt;
mesh->InterpolationBuffer->Vertices[t*3 + n].Color = video::SColor(255,255,255,255);
}
}
}
delete [] triangles;
delete [] textureCoords;
mesh->getMesh(0);
return true;
}
}
}
#endif