//-------------------------------------------------------------------------------------
//
// Licensed under the BSD license, see LICENSE in root for details.
//
// Copyright (c) 2008 Dr.Watson
//
// For latest updates, please visit http://www.cocoachina.com
//
//-------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "CCTexture.h"
#include "Wrapper.h"
#include "CCMD2Model.h"
MD2Animation::MD2Animation(int start, int end)
{
mStartFrame = start;
mEndFrame = end;
}
//-------------------------------------------------------------------------------------------------
CCMD2Model::CCMD2Model()
{
int endFrames[] = { 39, 46, 60, 66, 73, 95, 112, 122, 135, 154, 161, 169, 177, 185, 190, 198 };
int startFrame = 0;
mAnimations = (MD2Animation **)malloc(sizeof(MD2Animation)*MAX_ANIMATION);
for (int i=0;i<MAX_ANIMATION;i++)
{
mAnimations[i] = new MD2Animation(startFrame, endFrames[i]);
startFrame = endFrames[i]+1;
}
mState = -1;
mNextState = STATE_IDLE;
mModel = NULL;
mAnimationSpeed = 6.0f;
}
//-------------------------------------------------------------------------------------------------
CCMD2Model::~CCMD2Model()
{
if (mModel)
{
if (mModel->triIndex != NULL)
free(mModel->triIndex);
if (mModel->pointList != NULL)
free(mModel->pointList);
if (mModel->st != NULL)
free(mModel->st);
if (mModel->modelTex != NULL)
delete mModel->modelTex;
free(mModel);
}
if (mAnimations)
{
for (int i=0;i<MAX_ANIMATION;i++)
delete mAnimations[i];
free(mAnimations);
}
}
//-------------------------------------------------------------------------------------------------
// loads MD2 model
bool CCMD2Model::Load(char *filename, char *textureName)
{
FILE *filePtr; // file pointer
int fileLen; // length of model file
char *buffer; // file buffer
modelHeader_t *modelHeader; // model header
stIndex_t *stPtr; // texture data
frame_t *frame; // frame data
Vector3D *pointListPtr; // index variable
mesh_t *triIndex, *bufIndexPtr; // index variables
int i, j; // index variables
// open the model file
const char *path = GetPath(filename);
filePtr = fopen(path, "rb");
if (filePtr == NULL)
return false;
// find length of file
fseek(filePtr, 0, SEEK_END);
fileLen = ftell(filePtr);
fseek(filePtr, 0, SEEK_SET);
// read entire file into buffer
buffer = (char*)malloc(fileLen + 1);
fread(buffer, sizeof(char), fileLen, filePtr);
// extract model file header from buffer
modelHeader = (modelHeader_t*)buffer;
// allocate memory for model data
mModel = (modelData_t*)malloc(sizeof(modelData_t));
if (mModel == NULL)
return false;
// allocate memory for all vertices used in model, including animations
mModel->pointList = (Vector3D *)malloc(sizeof(Vector3D)*modelHeader->numXYZ * modelHeader->numFrames);
// store vital model data
mModel->numPoints = modelHeader->numXYZ;
mModel->numFrames = modelHeader->numFrames;
mModel->frameSize = modelHeader->framesize;
// loop number of frames in model file
for(j = 0; j < modelHeader->numFrames; j++)
{
// offset to the points in this frame
frame = (frame_t*)&buffer[modelHeader->offsetFrames + modelHeader->framesize * j];
// calculate the point positions based on frame details
pointListPtr = (Vector3D *)&mModel->pointList[modelHeader->numXYZ * j];
for(i = 0; i < modelHeader->numXYZ; i++)
{
pointListPtr[i].x = frame->scale[0] * frame->fp[i].v[0] + frame->translate[0];
pointListPtr[i].y = frame->scale[1] * frame->fp[i].v[1] + frame->translate[1];
pointListPtr[i].z = frame->scale[2] * frame->fp[i].v[2] + frame->translate[2];
}
}
CCTexture *tex = CCTextureCreate(textureName);
if (tex)
mModel->modelTex = tex;
else
{
free(mModel);
mModel = NULL;
free(buffer);
return false;
}
float texWidth = tex->GetTextureWidth();
float texHeight = tex->GetTextureHeight();
// allocate memory for the model texture coordinates
mModel->st = (texCoord_t*)malloc(sizeof(texCoord_t)*modelHeader->numST);
// store number of texture coordinates
mModel->numST = modelHeader->numST;
// set texture pointer to texture coordinate offset
stPtr = (stIndex_t*)&buffer[modelHeader->offsetST];
// calculate and store the texture coordinates for the model
for (i = 0; i < modelHeader->numST; i++)
{
mModel->st[i].s = (float)stPtr[i].s / texWidth;
mModel->st[i].t = (float)stPtr[i].t / texHeight;
}
// allocate an index of triangles
triIndex = (mesh_t*)malloc(sizeof(mesh_t) * modelHeader->numTris);
// set total number of triangles
mModel->numTriangles = modelHeader->numTris;
mModel->triIndex = triIndex;
// point to triangle indexes in buffer
bufIndexPtr = (mesh_t*)&buffer[modelHeader->offsetTris];
// create a mesh (triangle) list
for (j = 0; j < mModel->numFrames; j++)
{
// for all triangles in each frame
for(i = 0; i < modelHeader->numTris; i++)
{
triIndex[i].meshIndex[0] = bufIndexPtr[i].meshIndex[0];
triIndex[i].meshIndex[1] = bufIndexPtr[i].meshIndex[1];
triIndex[i].meshIndex[2] = bufIndexPtr[i].meshIndex[2];
triIndex[i].stIndex[0] = bufIndexPtr[i].stIndex[0];
triIndex[i].stIndex[1] = bufIndexPtr[i].stIndex[1];
triIndex[i].stIndex[2] = bufIndexPtr[i].stIndex[2];
}
}
// close file and free memory
fclose(filePtr);
free(buffer);
mModel->currentFrame = 0;
mModel->nextFrame = 1;
mModel->interpol = 0.0;
CheckNextState();
return true;
}
void CCMD2Model::ReloadTexture(const char *textureName)
{
CCTexture *tex = CCTextureCreate(textureName, true);
if (tex)
{
delete mModel->modelTex;
mModel->modelTex = tex;
}
}
//-------------------------------------------------------------------------------------------------
// given 3 points, calculates the normal to the points
void CCMD2Model::CalculateNormal(Vector3D *normal, float *p1, float *p2, float *p3)
{
float a[3], b[3], result[3];
float length;
a[0] = p1[0] - p2[0];
a[1] = p1[1] - p2[1];
a[2] = p1[2] - p2[2];
b[0] = p1[0] - p3[0];
b[1] = p1[1] - p3[1];
b[2] = p1[2] - p3[2];
result[0] = a[1] * b[2] - b[1] * a[2];
result[1] = b[0] * a[2] - a[0] * b[2];
result[2] = a[0] * b[1] - b[0] * a[1];
// calculate the length of the normal
length = (float)sqrt(result[0]*result[0] + result[1]*result[1] + result[2]*result[2]);
if (length == 0.0f)
length = SMALLEST_FP;
normal->x = result[0]/length;
normal->y = result[1]/length;
normal->z = result[2]/length;
}
//-------------------------------------------------------------------------------------------------
// render a single frame of a MD2 model
void CCMD2Model::Render(int frameNum)
{
Vector3D *pointList;
// create a pointer to the frame we want to show
pointList = &mModel->pointList[mModel->numPoints * frameNum];
mModel->modelTex->Bind();
float uv[mModel->numTriangles * 6];
float vertices[mModel->numTriangles * 9];
int uvIdx = 0;
int vertexIdx = 0;
for(int i = 0; i < mModel->numTriangles; i++)
{
//CalculateNormal(&vertices[n].normal,
// pointList[mModel->triIndex[i].meshIndex[0]].v,
// pointList[mModel->triIndex[i].meshIndex[2]].v,
// pointList[mModel->triIndex[i].meshIndex[1]].v);
uv[uvIdx++] = mModel->st[mModel->triIndex[i].stIndex[0]].s;
uv[uvIdx++] = mModel->st[mModel->triIndex[i].stIndex[0]].t;
vertices[vertexIdx++] = pointList[mModel->triIndex[i].meshIndex[0]].x;
vertices[vertexIdx++] = pointList[mModel->triIndex[i].meshIndex[0]].y;
vertices[vertexIdx++] = pointList[mModel->triIndex[i].meshIndex[0]].z;
//vertices[n].normal.x = vertices[n-1].normal.x;
//vertices[n].normal.y = vertices[n-1].normal.y;
//vertices[n].normal.z = vertices[n-1].normal.z;
uv[uvId