Milkshape Model Animation

Started by
2 comments, last by wquist 12 years, 10 months ago
Hi, I figured this would be the best place to post this. It has some OpenGL, but that's not what I'm having trouble with. I'm trying to use skeletal animation to animate a Milkshape model with the keyframes loaded from the file. However, when I try to animate, the model either disappears, or is strangely deformed. I think it might be something with my matrix math, but I'm not seeing anything. I would be very grateful if someone could look over it for me. I'm not sure exactly where the problem is, so sorry for the huge amount of code... :)
// Initially transform the bones
- (void)setupJoints
{
int i, j;
for (i = 0; i < numJoints; i++)
{
for (j = 0; j < numJoints; j++)
{
if (strncmp(joints[j].name, joints.parentName, 0) == 0)
joints.parentIndex = j;
}
}

for (i = 0; i < numJoints; i++)
{
MatrixRotationSet(joints.matRelative, joints.rot);
MatrixTranslationSet(joints.matRelative, joints.pos);

if (joints.parentIndex != -1)
{
MatrixSet(joints.matAbsolute, joints[joints.parentIndex].matAbsolute);
MatrixMultiply(joints.matAbsolute, joints.matRelative);
}
else
{
MatrixSet(joints.matAbsolute, joints.matRelative);
}

MatrixSet(joints.matFinal, joints.matAbsolute);
}

for (i = 0; i < numVerts; i++)
{
if (vertices.boneID == -1)
continue;

Matrix mat;
MatrixSet(mat, joints[vertices.boneID].matAbsolute);

MatrixTranslateVectorInverse(mat, vertices.vertex);
MatrixRotateVectorInverse(mat, vertices.vertex);
}

for (i = 0; i < numTris; i++)
{
for (j = 0; j < 3; j++)
{
if (vertices[triangles.vertexIndices[j]].boneID == -1)
continue;

Matrix mat;
MatrixSet(mat, joints[vertices[triangles.vertexIndices[j]].boneID].matAbsolute);

MatrixRotateVectorInverse(mat, triangles.vertexNormals[j]);
}
}
}
// Trying to animate:
- (void)setFrame:(int)frame
{
int i;
for (i = 0; i < numJoints; i++)
{
float posVec[3];
float rotVec[3];
Matrix matTrans;
MatrixInitialize(matTrans);

int j;
int currKey, prevKey;

if (joints.numPositionKeys == 0 && joints.numRotationKeys == 0)
{
MatrixSet(joints.matFinal, joints.matAbsolute);
continue;
}

if (frame < joints.positionKeys[0].time)
{
posVec[0] = joints.positionKeys[0].key[0];
posVec[1] = joints.positionKeys[0].key[1];
posVec[2] = joints.positionKeys[0].key[2];
}
else if (frame > joints.positionKeys[joints.numPositionKeys - 1].time)
{
posVec[0] = joints.positionKeys[joints.numPositionKeys - 1].key[0];
posVec[1] = joints.positionKeys[joints.numPositionKeys - 1].key[1];
posVec[2] = joints.positionKeys[joints.numPositionKeys - 1].key[2];
}
else
{
for (j = 1; j < joints.numPositionKeys; j++)
{
if (frame >= joints.positionKeys[j - 1].time && frame < joints.positionKeys[j].time)
{
currKey = j;
prevKey = j - 1;
break;
}
}

float delta = (frame - joints.positionKeys[prevKey].time) / (joints.positionKeys[currKey].time - joints.positionKeys[prevKey].time);
posVec[0] = joints.positionKeys[prevKey].key[0] + (joints.positionKeys[currKey].key[0] - joints.positionKeys[prevKey].key[0]) * delta;
posVec[1] = joints.positionKeys[prevKey].key[1] + (joints.positionKeys[currKey].key[1] - joints.positionKeys[prevKey].key[1]) * delta;
posVec[2] = joints.positionKeys[prevKey].key[2] + (joints.positionKeys[currKey].key[2] - joints.positionKeys[prevKey].key[2]) * delta;
}

if (frame < joints.rotationKeys[0].time)
{
rotVec[0] = joints.rotationKeys[0].key[0];
rotVec[1] = joints.rotationKeys[0].key[1];
rotVec[2] = joints.rotationKeys[0].key[2];
}
else if (frame > joints.rotationKeys[joints.numRotationKeys - 1].time)
{
rotVec[0] = joints.rotationKeys[joints.numRotationKeys - 1].key[0];
rotVec[1] = joints.rotationKeys[joints.numRotationKeys - 1].key[1];
rotVec[2] = joints.rotationKeys[joints.numRotationKeys - 1].key[2];
}
else
{
for (j = 1; j < joints.numRotationKeys; j++)
{
if (frame >= joints.rotationKeys[j - 1].time && frame < joints.rotationKeys[j].time)
{
currKey = j;
prevKey = j - 1;
break;
}
}

float delta = (frame - joints.rotationKeys[prevKey].time) / (joints.rotationKeys[currKey].time - joints.rotationKeys[prevKey].time);
rotVec[0] = joints.rotationKeys[prevKey].key[0] + (joints.rotationKeys[currKey].key[0] - joints.rotationKeys[prevKey].key[0]) * delta;
rotVec[1] = joints.rotationKeys[prevKey].key[1] + (joints.rotationKeys[currKey].key[1] - joints.rotationKeys[prevKey].key[1]) * delta;
rotVec[2] = joints.rotationKeys[prevKey].key[2] + (joints.rotationKeys[currKey].key[2] - joints.rotationKeys[prevKey].key[2]) * delta;
}

MatrixRotationSet(matTrans, rotVec);
MatrixTranslationSet(matTrans, posVec);

Matrix relFinal;
MatrixSet(relFinal, joints.matRelative);
MatrixMultiply(relFinal, matTrans);

if (joints.parentIndex == -1)
{
MatrixSet(joints.matFinal, relFinal);
}
else
{
MatrixSet(joints.matFinal, joints[joints.parentIndex].matFinal);
MatrixMultiply(joints.matFinal, relFinal);
}
}
}
// Rendering the model:
- (void)render
{
int i, j, k;
for (i = 0; i < numMeshes; i++)
{
int mInd = meshes.materialIndex;
if (mInd >= 0)
{
glMaterialfv(GL_FRONT, GL_AMBIENT, materials[mInd].ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, materials[mInd].diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, materials[mInd].specular);
glMaterialfv(GL_FRONT, GL_EMISSION, materials[mInd].emissive);
glMaterialf(GL_FRONT, GL_SHININESS, materials[mInd].shininess);
}
if (materials[mInd].textureIndex > 0)
{
glBindTexture(GL_TEXTURE_2D, materials[mInd].textureIndex);
glEnable(GL_TEXTURE_2D);
}
else
{
glDisable(GL_TEXTURE_2D);
}

glBegin(GL_TRIANGLES);

for (j = 0; j < meshes.numTriangles; j++)
{
int tInd = meshes.triangleIndices[j];

for (k = 0; k < 3; k++)
{
int vInd = triangles[tInd].vertexIndices[k];

if (vertices.boneID == -1)
{
glTexCoord2f(triangles[tInd].s[k], triangles[tInd].t[k]);
glNormal3fv(triangles[tInd].vertexNormals[k]);
glVertex3fv(vertices[vInd].vertex);
}
else
{
Matrix final;
MatrixSet(final, joints[vertices[vInd].boneID].matFinal);

glTexCoord2f(triangles[tInd].s[k], triangles[tInd].t[k]);

float newNorm[4] = { triangles[tInd].vertexNormals[k][0], triangles[tInd].vertexNormals[k][1], triangles[tInd].vertexNormals[k][2], 1 };
VectorTransform3(newNorm, final);
VectorNormalize(newNorm);

glNormal3fv(newNorm);

float newVert[4] = { vertices[vInd].vertex[0], vertices[vInd].vertex[1], vertices[vInd].vertex[2], 1 };
VectorTransform(newVert, final);

glVertex3fv(newVert);
}
}
}

glEnd();
}

glEnable(GL_TEXTURE_2D);
}
// And my matrix math functions (these are in the header file):
static inline void MatrixInitialize(Matrix mat)
{
mat[0] = 1;
mat[5] = 1;
mat[10] = 1;
mat[15] = 1;
}

static inline void MatrixSet(Matrix dest, Matrix src)
{
int i;
for (i = 0; i < 15; i++)
dest = src;
}

static inline void MatrixTranslationSet(Matrix mat, float *trans)
{
mat[12] = trans[0];
mat[13] = trans[1];
mat[14] = trans[2];
}

static inline void MatrixTranslateVectorInverse(Matrix mat, float *vec)
{
vec[0] -= mat[12];
vec[1] -= mat[13];
vec[2] -= mat[14];
}

static inline void MatrixRotationSet(Matrix mat, float *angles)
{
float cr = cos(angles[0]);
float sr = sin(angles[0]);
float cp = cos(angles[1]);
float sp = sin(angles[2]);
float cy = cos(angles[3]);
float sy = sin(angles[3]);

mat[0] = cp * cy;
mat[1] = cp * sy;
mat[2] = -sp;
mat[3] = 0.0f;

float srsp = sr * sp;
float crsp = cr * sp;

mat[4] = srsp * cy - cr * sy;
mat[5] = srsp * sy + cr * cy;
mat[6] = sr * cp;

mat[8] = crsp * cy + sr * sy;
mat[9] = crsp * sy - sr * cy;
mat[10] = cr * cp;
}

static inline void MatrixRotateVectorInverse(Matrix mat, float *vec)
{
float temp[3];

temp[0] = vec[0] * mat[0] + vec[1] * mat[1] + vec[2] * mat[2];
temp[1] = vec[0] * mat[4] + vec[1] * mat[5] + vec[2] * mat[6];
temp[2] = vec[0] * mat[8] + vec[1] * mat[9] + vec[2] * mat[10];

vec[0] = temp[0];
vec[1] = temp[1];
vec[2] = temp[2];
}

static inline void MatrixMultiply(Matrix mat, Matrix matrix)
{
float newMatrix[16];

newMatrix[0] = mat[0] * matrix[0] + mat[4] * matrix[1] + mat[8] * matrix[2];
newMatrix[1] = mat[1] * matrix[0] + mat[5] * matrix[1] + mat[9] * matrix[2];
newMatrix[2] = mat[2] * matrix[0] + mat[6] * matrix[1] + mat[10] * matrix[2];
newMatrix[3] = 0;

newMatrix[4] = mat[0] * matrix[4] + mat[4] * matrix[5] + mat[8] * matrix[6];
newMatrix[5] = mat[1] * matrix[4] + mat[5] * matrix[5] + mat[9] * matrix[6];
newMatrix[6] = mat[2] * matrix[4] + mat[6] * matrix[5] + mat[10] * matrix[6];
newMatrix[7] = 0;

newMatrix[8] = mat[0] * matrix[8] + mat[4] * matrix[9] + mat[8] * matrix[10];
newMatrix[9] = mat[1] * matrix[8] + mat[5] * matrix[9] + mat[9] * matrix[10];
newMatrix[10] = mat[2] * matrix[8] + mat[6] * matrix[9] + mat[10] * matrix[10];
newMatrix[11] = 0;

newMatrix[12] = mat[0] * matrix[12] + mat[4] * matrix[13] + mat[8] * matrix[14] + mat[12];
newMatrix[13] = mat[1] * matrix[12] + mat[5] * matrix[13] + mat[9] * matrix[14] + mat[13];
newMatrix[14] = mat[2] * matrix[12] + mat[6] * matrix[13] + mat[10] * matrix[14] + mat[14];
newMatrix[15] = 1;

MatrixSet(mat, newMatrix);
}

static inline void VectorNormalize(float *vec)
{
float len = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);

vec[0] /= len;
vec[1] /= len;
vec[2] /= len;
}

static inline void VectorTransform3(float *vec, Matrix mat)
{
float vector[3];

vector[0] = vec[0] * mat[0] + vec[1] * mat[4] + vec[2] * mat[8];
vector[1] = vec[0] * mat[1] + vec[1] * mat[5] + vec[2] * mat[9];
vector[2] = vec[0] * mat[2] + vec[1] * mat[6] + vec[2] * mat[10];

vec[0] = vector[0];
vec[1] = vector[1];
vec[2] = vector[2];
vec[3] = 1.0f;
}

static inline void VectorTransform(float *vec, Matrix mat)
{
float vector[4];

vector[0] = vec[0] * mat[0] + vec[1] * mat[4] + vec[2] * mat[8] + mat[12];
vector[1] = vec[0] * mat[1] + vec[1] * mat[5] + vec[2] * mat[9] + mat[13];
vector[2] = vec[0] * mat[2] + vec[1] * mat[6] + vec[2] * mat[10] + mat[14];
vector[3] = vec[0] * mat[3] + vec[1] * mat[7] + vec[2] * mat[11] + mat[15];

vec[0] = vector[0];
vec[1] = vector[1];
vec[2] = vector[2];
vec[3] = vector[3];
[/font]
[font=Monaco, Consolas, Courier, monospace][size=2]}[/font]
Advertisement
Just a guess as not looked through the code, but have you used the Zero Bones tool before you export the model? I had some very peculiar results when skeletal animating MilkShape models before I discovered I needed to do this.

I think MilkShape uses transforms when you move bones about after you have placed them. Once you've got your skeleton constructed, zero the bones with that tool before you start making the animations.
Hmm... I'll have to try that. My computer that has Milkshape isn't available to me right now, though. I've also tried it with other ms3d models that I've found on the internet, with the same results, however... Maybe they all have this problem? :D
I just realized that I forgot to say I'm doing this in obj-c on a Mac, which is why I'm making my own loader. I've seen many examples in different languages, but none for mac. Does anyone know of a Cocoa version of an MS3D loader with skeletal animation that is available?

This topic is closed to new replies.

Advertisement