// Copyright (C) 2002-2007 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CAnimatedMeshMD2.h" #include "os.h" #include "SColor.h" #include "IReadFile.h" #include "irrMath.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 // structs needed to load the md2-format const s32 MD2_MAGIC_NUMBER = 844121161; const s32 MD2_VERSION = 8; const s32 MD2_MAX_VERTS = 2048; // TA: private const s32 MD2_FRAME_SHIFT = 2; const f32 MD2_FRAME_SHIFT_RECIPROCAL = 1.f / ( 1 << MD2_FRAME_SHIFT ); 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; // Default alignment #if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) # pragma pack( pop, packing ) #endif #undef PACK_STRUCT const s32 Q2_VERTEX_NORMAL_TABLE_SIZE = 162; static const f32 Q2_VERTEX_NORMAL_TABLE[Q2_VERTEX_NORMAL_TABLE_SIZE][3] = { {-0.525731f, 0.000000f, 0.850651f}, {-0.442863f, 0.238856f, 0.864188f}, {-0.295242f, 0.000000f, 0.955423f}, {-0.309017f, 0.500000f, 0.809017f}, {-0.162460f, 0.262866f, 0.951056f}, {0.000000f, 0.000000f, 1.000000f}, {0.000000f, 0.850651f, 0.525731f}, {-0.147621f, 0.716567f, 0.681718f}, {0.147621f, 0.716567f, 0.681718f}, {0.000000f, 0.525731f, 0.850651f}, {0.309017f, 0.500000f, 0.809017f}, {0.525731f, 0.000000f, 0.850651f}, {0.295242f, 0.000000f, 0.955423f}, {0.442863f, 0.238856f, 0.864188f}, {0.162460f, 0.262866f, 0.951056f}, {-0.681718f, 0.147621f, 0.716567f}, {-0.809017f, 0.309017f, 0.500000f}, {-0.587785f, 0.425325f, 0.688191f}, {-0.850651f, 0.525731f, 0.000000f}, {-0.864188f, 0.442863f, 0.238856f}, {-0.716567f, 0.681718f, 0.147621f}, {-0.688191f, 0.587785f, 0.425325f}, {-0.500000f, 0.809017f, 0.309017f}, {-0.238856f, 0.864188f, 0.442863f}, {-0.425325f, 0.688191f, 0.587785f}, {-0.716567f, 0.681718f, -0.147621f}, {-0.500000f, 0.809017f, -0.309017f}, {-0.525731f, 0.850651f, 0.000000f}, {0.000000f, 0.850651f, -0.525731f}, {-0.238856f, 0.864188f, -0.442863f}, {0.000000f, 0.955423f, -0.295242f}, {-0.262866f, 0.951056f, -0.162460f}, {0.000000f, 1.000000f, 0.000000f}, {0.000000f, 0.955423f, 0.295242f}, {-0.262866f, 0.951056f, 0.162460f}, {0.238856f, 0.864188f, 0.442863f}, {0.262866f, 0.951056f, 0.162460f}, {0.500000f, 0.809017f, 0.309017f}, {0.238856f, 0.864188f, -0.442863f}, {0.262866f, 0.951056f, -0.162460f}, {0.500000f, 0.809017f, -0.309017f}, {0.850651f, 0.525731f, 0.000000f}, {0.716567f, 0.681718f, 0.147621f}, {0.716567f, 0.681718f, -0.147621f}, {0.525731f, 0.850651f, 0.000000f}, {0.425325f, 0.688191f, 0.587785f}, {0.864188f, 0.442863f, 0.238856f}, {0.688191f, 0.587785f, 0.425325f}, {0.809017f, 0.309017f, 0.500000f}, {0.681718f, 0.147621f, 0.716567f}, {0.587785f, 0.425325f, 0.688191f}, {0.955423f, 0.295242f, 0.000000f}, {1.000000f, 0.000000f, 0.000000f}, {0.951056f, 0.162460f, 0.262866f}, {0.850651f, -0.525731f, 0.000000f}, {0.955423f, -0.295242f, 0.000000f}, {0.864188f, -0.442863f, 0.238856f}, {0.951056f, -0.162460f, 0.262866f}, {0.809017f, -0.309017f, 0.500000f}, {0.681718f, -0.147621f, 0.716567f}, {0.850651f, 0.000000f, 0.525731f}, {0.864188f, 0.442863f, -0.238856f}, {0.809017f, 0.309017f, -0.500000f}, {0.951056f, 0.162460f, -0.262866f}, {0.525731f, 0.000000f, -0.850651f}, {0.681718f, 0.147621f, -0.716567f}, {0.681718f, -0.147621f, -0.716567f}, {0.850651f, 0.000000f, -0.525731f}, {0.809017f, -0.309017f, -0.500000f}, {0.864188f, -0.442863f, -0.238856f}, {0.951056f, -0.162460f, -0.262866f}, {0.147621f, 0.716567f, -0.681718f}, {0.309017f, 0.500000f, -0.809017f}, {0.425325f, 0.688191f, -0.587785f}, {0.442863f, 0.238856f, -0.864188f}, {0.587785f, 0.425325f, -0.688191f}, {0.688191f, 0.587785f, -0.425325f}, {-0.147621f, 0.716567f, -0.681718f}, {-0.309017f, 0.500000f, -0.809017f}, {0.000000f, 0.525731f, -0.850651f}, {-0.525731f, 0.000000f, -0.850651f}, {-0.442863f, 0.238856f, -0.864188f}, {-0.295242f, 0.000000f, -0.955423f}, {-0.162460f, 0.262866f, -0.951056f}, {0.000000f, 0.000000f, -1.000000f}, {0.295242f, 0.000000f, -0.955423f}, {0.162460f, 0.262866f, -0.951056f}, {-0.442863f, -0.238856f, -0.864188f}, {-0.309017f, -0.500000f, -0.809017f}, {-0.162460f, -0.262866f, -0.951056f}, {0.000000f, -0.850651f, -0.525731f}, {-0.147621f, -0.716567f, -0.681718f}, {0.147621f, -0.716567f, -0.681718f}, {0.000000f, -0.525731f, -0.850651f}, {0.309017f, -0.500000f, -0.809017f}, {0.442863f, -0.238856f, -0.864188f}, {0.162460f, -0.262866f, -0.951056f}, {0.238856f, -0.864188f, -0.442863f}, {0.500000f, -0.809017f, -0.309017f}, {0.425325f, -0.688191f, -0.587785f}, {0.716567f, -0.681718f, -0.147621f}, {0.688191f, -0.587785f, -0.425325f}, {0.587785f, -0.425325f, -0.688191f}, {0.000000f, -0.955423f, -0.295242f}, {0.000000f, -1.000000f, 0.000000f}, {0.262866f, -0.951056f, -0.162460f}, {0.000000f, -0.850651f, 0.525731f}, {0.000000f, -0.955423f, 0.295242f}, {0.238856f, -0.864188f, 0.442863f}, {0.262866f, -0.951056f, 0.162460f}, {0.500000f, -0.809017f, 0.309017f}, {0.716567f, -0.681718f, 0.147621f}, {0.525731f, -0.850651f, 0.000000f}, {-0.238856f, -0.864188f, -0.442863f}, {-0.500000f, -0.809017f, -0.309017f}, {-0.262866f, -0.951056f, -0.162460f}, {-0.850651f, -0.525731f, 0.000000f}, {-0.716567f, -0.681718f, -0.147621f}, {-0.716567f, -0.681718f, 0.147621f}, {-0.525731f, -0.850651f, 0.000000f}, {-0.500000f, -0.809017f, 0.309017f}, {-0.238856f, -0.864188f, 0.442863f}, {-0.262866f, -0.951056f, 0.162460f}, {-0.864188f, -0.442863f, 0.238856f}, {-0.809017f, -0.309017f, 0.500000f}, {-0.688191f, -0.587785f, 0.425325f}, {-0.681718f, -0.147621f, 0.716567f}, {-0.442863f, -0.238856f, 0.864188f}, {-0.587785f, -0.425325f, 0.688191f}, {-0.309017f, -0.500000f, 0.809017f}, {-0.147621f, -0.716567f, 0.681718f}, {-0.425325f, -0.688191f, 0.587785f}, {-0.162460f, -0.262866f, 0.951056f}, {0.442863f, -0.238856f, 0.864188f}, {0.162460f, -0.262866f, 0.951056f}, {0.309017f, -0.500000f, 0.809017f}, {0.147621f, -0.716567f, 0.681718f}, {0.000000f, -0.525731f, 0.850651f}, {0.425325f, -0.688191f, 0.587785f}, {0.587785f, -0.425325f, 0.688191f}, {0.688191f, -0.587785f, 0.425325f}, {-0.955423f, 0.295242f, 0.000000f}, {-0.951056f, 0.162460f, 0.262866f}, {-1.000000f, 0.000000f, 0.000000f}, {-0.850651f, 0.000000f, 0.525731f}, {-0.955423f, -0.295242f, 0.000000f}, {-0.951056f, -0.162460f, 0.262866f}, {-0.864188f, 0.442863f, -0.238856f}, {-0.951056f, 0.162460f, -0.262866f}, {-0.809017f, 0.309017f, -0.500000f}, {-0.864188f, -0.442863f, -0.238856f}, {-0.951056f, -0.162460f, -0.262866f}, {-0.809017f, -0.309017f, -0.500000f}, {-0.681718f, 0.147621f, -0.716567f}, {-0.681718f, -0.147621f, -0.716567f}, {-0.850651f, 0.000000f, -0.525731f}, {-0.688191f, 0.587785f, -0.425325f}, {-0.587785f, 0.425325f, -0.688191f}, {-0.425325f, 0.688191f, -0.587785f}, {-0.425325f, -0.688191f, -0.587785f}, {-0.587785f, -0.425325f, -0.688191f}, {-0.688191f, -0.587785f, -0.425325f}, }; struct SMD2AnimationType { s32 begin; s32 end; s32 fps; }; static const SMD2AnimationType MD2AnimationTypeList[21] = { { 0, 39, 9 }, // STAND { 40, 45, 10 }, // RUN { 46, 53, 10 }, // ATTACK { 54, 57, 7 }, // PAIN_A { 58, 61, 7 }, // PAIN_B { 62, 65, 7 }, // PAIN_C { 66, 71, 7 }, // JUMP { 72, 83, 7 }, // FLIP { 84, 94, 7 }, // SALUTE { 95, 111, 10 }, // FALLBACK { 112, 122, 7 }, // WAVE { 123, 134, 6 }, // POINT { 135, 153, 10 }, // CROUCH_STAND { 154, 159, 7 }, // CROUCH_WALK { 160, 168, 10 }, // CROUCH_ATTACK { 169, 172, 7 }, // CROUCH_PAIN { 173, 177, 5 }, // CROUCH_DEATH { 178, 183, 7 }, // DEATH_FALLBACK { 184, 189, 7 }, // DEATH_FALLFORWARD { 190, 197, 7 }, // DEATH_FALLBACKSLOW { 198, 198, 5 }, // BOOM }; //! constructor CAnimatedMeshMD2::CAnimatedMeshMD2() : FrameList(0), FrameCount(0), TriangleCount(0) { #ifdef _DEBUG IAnimatedMesh::setDebugName("CAnimatedMeshMD2 IAnimatedMesh"); IMesh::setDebugName("CAnimatedMeshMD2 IMesh"); IMeshBuffer::setDebugName("CAnimatedMeshMD2 IMeshBuffer"); #endif } //! destructor CAnimatedMeshMD2::~CAnimatedMeshMD2() { if (FrameList) delete [] FrameList; } //! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. s32 CAnimatedMeshMD2::getFrameCount() { return FrameCount< (FrameCount<>MD2_FRAME_SHIFT; secondFrame = frame>>MD2_FRAME_SHIFT; div = 1.0f; } else { // key frames u32 s = startFrameLoop >> MD2_FRAME_SHIFT; u32 e = endFrameLoop >> MD2_FRAME_SHIFT; firstFrame = frame >> MD2_FRAME_SHIFT; secondFrame = core::if_c_a_else_b ( firstFrame + 1 > e, s, firstFrame + 1 ); firstFrame = core::s32_min ( FrameCount - 1, firstFrame ); secondFrame = core::s32_min ( FrameCount - 1, secondFrame ); //div = (frame % (1<Pos = (second->Pos - first->Pos) * div + first->Pos; target->Normal = (second->Normal - first->Normal) * div + first->Normal; ++target; ++first; ++second; } //update bounding box BoundingBox = BoxList[secondFrame].getInterpolated(BoxList[firstFrame], div); } //! loads an md2 file bool CAnimatedMeshMD2::loadFile(io::IReadFile* file) { 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; } // create Memory for indices and frames TriangleCount = header.numTriangles; if (FrameList) delete [] FrameList; FrameList = new core::array[header.numFrames]; FrameCount = header.numFrames; s32 i; for (i=0; iseek(header.offsetTexcoords, false); SMD2TextureCoordinate* textureCoords = new SMD2TextureCoordinate[header.numTexcoords]; if (!file->read(textureCoords, sizeof(SMD2TextureCoordinate)*header.numTexcoords)) { os::Printer::log("MD2 Loader: Error reading TextureCoords.", file->getFileName(), ELL_ERROR); return false; } #ifdef __BIG_ENDIAN__ for (i=0; iseek(header.offsetTriangles, false); SMD2Triangle *triangles = new SMD2Triangle[header.numTriangles]; if (!file->read(triangles, header.numTriangles *sizeof(SMD2Triangle))) { os::Printer::log("MD2 Loader: Error reading triangles.", file->getFileName(), ELL_ERROR); return false; } #ifdef __BIG_ENDIAN__ for (i=0; i* vertices = new core::array< core::vector3df >[header.numFrames]; core::array< core::vector3df >* normals = new core::array< core::vector3df >[header.numFrames]; file->seek(header.offsetFrames, false); for (i = 0; iread(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 // store frame data SFrameData fdata; fdata.begin = i; fdata.end = i; fdata.fps = 7; if (frame->name[0]) { for (s32 s = 0; frame->name[s]!=0 && (frame->name[s] < '0' || frame->name[s] > '9'); ++s) fdata.name += frame->name[s]; if (!FrameData.empty() && FrameData[FrameData.size()-1].name == fdata.name) ++FrameData[FrameData.size()-1].end; else FrameData.push_back(fdata); } // add vertices vertices[i].reallocate(header.numVertices); for (s32 j=0; jvertices[j].vertex[0] * frame->scale[0] + frame->translate[0]; v.Z = frame->vertices[j].vertex[1] * frame->scale[1] + frame->translate[1]; v.Y = frame->vertices[j].vertex[2] * frame->scale[2] + frame->translate[2]; vertices[i].push_back(v); s32 normalidx = frame->vertices[j].lightNormalIndex; if (normalidx > 0 && normalidx < Q2_VERTEX_NORMAL_TABLE_SIZE) { v.X = Q2_VERTEX_NORMAL_TABLE[normalidx][0]; v.Z = Q2_VERTEX_NORMAL_TABLE[normalidx][1]; v.Y = Q2_VERTEX_NORMAL_TABLE[normalidx][2]; } normals[i].push_back(v); } // calculate bounding boxes if (header.numVertices) { core::aabbox3d box; box.reset(vertices[i][0]); for (s32 j=1; j& vert = vertices[f]; for (s32 t=0; t=FrameCount) defaultFrame = 0; for (u32 j=0; j& CAnimatedMeshMD2::getBoundingBox() const { return BoundingBox; } //! set user axis aligned bounding box void CAnimatedMeshMD2::setBoundingBox( const core::aabbox3df& box) { BoundingBox = box; } //! calculates normals void CAnimatedMeshMD2::calculateNormals() { for (u32 i=0; i plane( vtx[Indices[j]].Pos, vtx[Indices[j+1]].Pos, vtx[Indices[j+2]].Pos); vtx[Indices[j]].Normal = plane.Normal; vtx[Indices[j+1]].Normal = plane.Normal; vtx[Indices[j+2]].Normal = plane.Normal; } } } //! Returns the type of the animated mesh. E_ANIMATED_MESH_TYPE CAnimatedMeshMD2::getMeshType() const { return EAMT_MD2; } //! Returns frame loop data for a special MD2 animation type. void CAnimatedMeshMD2::getFrameLoop(EMD2_ANIMATION_TYPE l, s32& outBegin, s32& outEnd, s32& outFPS) const { if (l < 0 || l >= EMAT_COUNT) return; outBegin = MD2AnimationTypeList[l].begin << MD2_FRAME_SHIFT; outEnd = MD2AnimationTypeList[l].end << MD2_FRAME_SHIFT; // correct to anim between last->first frame outEnd += MD2_FRAME_SHIFT == 0 ? 1 : ( 1 << MD2_FRAME_SHIFT ) - 1; outFPS = MD2AnimationTypeList[l].fps << MD2_FRAME_SHIFT; } //! Returns frame loop data for a special MD2 animation type. bool CAnimatedMeshMD2::getFrameLoop(const c8* name, s32& outBegin, s32&outEnd, s32& outFPS) const { for (s32 i=0; i<(s32)FrameData.size(); ++i) if (FrameData[i].name == name) { outBegin = FrameData[i].begin << MD2_FRAME_SHIFT; outEnd = FrameData[i].end << MD2_FRAME_SHIFT; outEnd += MD2_FRAME_SHIFT == 0 ? 1 : ( 1 << MD2_FRAME_SHIFT ) - 1; outFPS = FrameData[i].fps << MD2_FRAME_SHIFT; return true; } return false; } //! Returns amount of md2 animations in this file. s32 CAnimatedMeshMD2::getAnimationCount() const { return FrameData.size(); } //! Returns name of md2 animation. const c8* CAnimatedMeshMD2::getAnimationName(s32 nr) const { if (nr < 0 || nr >= (s32)FrameData.size()) return 0; return FrameData[nr].name.c_str(); } } // end namespace scene } // end namespace irr