Sign in to follow this  
para45

md3 tag orientation

Recommended Posts

para45    122
As a school project I'm working on an OpenGL MD3 model renderer. I've read up on the format at http://linux.ucla.edu/~phaethon/q3a/formats/md3format.html which has led me to be able to write most of the code. However I have an issue with aligning the parts of the body. Since the model is in three files, a head, torso, and legs, tag objects are used to specify the origin and rotation of each member so they can properly connect. I can't seem to get this to work. The tag object has a 3-element vector for the origin (x, y, z) and 3 individial 3-element vectors for the rotation. So I'm assuming you would create a 4x4 matrix with these elements, then multiply it by the GL_MODELVIEW matrix in OpenGL before drawing the head or torso. So, for example, using these structures:
struct vec3_t {
	float x, y, z;
};
struct md3_tag_t {
	struct md3_tag_t* next;			/* next tag in the list		*/

	char name[MAX_QPATH];			/* name of tag object		*/
	struct vec3_t origin;			/* coordinates				*/
	struct vec3_t axis[3];			/* orientation				*/
};
I try to draw the head and torso with:
struct md3_tag_t* tag = NULL;
	float trans_matrix[16];

	/* render head */
	glPushMatrix();

	tag = model_head->tag_ptr;
	printf("head tag: [%s]\n", tag->name);
	trans_matrix[0] = tag->axis[0].x;
	trans_matrix[1] = tag->axis[0].y;
	trans_matrix[2] = tag->axis[0].z;
	trans_matrix[3] = 0;
	trans_matrix[4] = tag->axis[1].x;
	trans_matrix[5] = tag->axis[1].y;
	trans_matrix[6] = tag->axis[1].z;
	trans_matrix[7] = 0;
	trans_matrix[8] = tag->axis[2].x;
	trans_matrix[9] = tag->axis[2].y;
	trans_matrix[10] = tag->axis[2].z;
	trans_matrix[11] = 0;
	trans_matrix[12] = tag->origin.x;
	trans_matrix[13] = tag->origin.y;
	trans_matrix[14] = tag->origin.z;
	trans_matrix[15] = 1;
	glMultMatrixf(trans_matrix);

	md3_render(model_head);
	glPopMatrix();
	
	/* render body */
	glPushMatrix();

	tag = model_body->tag_ptr;
	printf("body tag: [%s]\n", tag->name);
	trans_matrix[0] = tag->axis[0].x;
	trans_matrix[1] = tag->axis[0].y;
	trans_matrix[2] = tag->axis[0].z;
	trans_matrix[3] = 0;
	trans_matrix[4] = tag->axis[1].x;
	trans_matrix[5] = tag->axis[1].y;
	trans_matrix[6] = tag->axis[1].z;
	trans_matrix[7] = 0;
	trans_matrix[8] = tag->axis[2].x;
	trans_matrix[9] = tag->axis[2].y;
	trans_matrix[10] = tag->axis[2].z;
	trans_matrix[11] = 0;
	trans_matrix[12] = tag->origin.x;
	trans_matrix[13] = tag->origin.y;
	trans_matrix[14] = tag->origin.z;
	trans_matrix[15] = 1;
	glMultMatrixf(trans_matrix);
	
	md3_render(model_body);
	glPopMatrix();
Which I believe is correct since the 4x4 matrix must be column-major. I output the name of the two tags to make sure they are the same, both are "tag_head", so I'm guessing these two transformations should align the two properly. However, here is what I'm getting: I do scale each vertex by a factor of 64.0f, which is what the documentation says must be done to each. Otherwise I suspect the distance would be much greater between them.
glVertex3f((float)(vptr->x * (1 / 64.0f)), (float)(vptr->y * (1 / 64.0f)), (float)(vptr->z * (1 / 64.0f)));
Any ideas? Thanks.

Share this post


Link to post
Share on other sites
para45    122
I've read that the xyz vectors provided in the tag structure are actually quaternions. Since I don't know much about what they are or how to use them, I've read up on a bit of it here: http://www.cprogramming.com/tutorial/3d/quaternions.html

My understanding from reading this is that I would create a quaternion with x=0, y=0, z=0, w=1, then multiply this by the x-rotation quaternion provided it the md3. Then multiply the result by the y-rotation, then finally the z-rotation. The final quaternion could be converted to a 4x4 matrix, and the transformation could be placed in the last row.

Here is some of the code for it:
struct quat_t {
float x, y, z;
float w;
};

void quat_init(struct quat_t* q) {
struct quat_t i = { 0, 0, 0, 1 };
*q = i;
}

void quat_normalize(struct quat_t* q) {
float r = ((q->x * q->x) + (q->y * q->y) + (q->z * q->z) + (q->w * q->w));
if ((1.0f - r) <= 0.0000001)
/* already nomalized */
return;
r = sqrt(r);
q->x /= r;
q->y /= r;
q->z /= r;
q->w /= r;
}

void quat_mult(struct quat_t* a, struct quat_t* b, struct quat_t* c) {
struct quat_t r;
r.x = ((a->w * b->x) + (a->x * b->w) + (a->y * b->z) - (a->z * b->y));
r.y = ((a->w * b->y) - (a->z * b->z) + (a->y * b->w) + (a->z * b->x));
r.z = ((a->w * b->z) + (a->x * b->y) - (a->y * b->x) + (a->z * b->w));
r.w = ((a->w * b->w) - (a->x * b->x) - (a->y * b->y) - (a->z * b->z));
*c = r;
}

void quat_rotate(struct quat_t* q, float ang, float x, float y, float z) {
float ha = sinf(ang / 2);
q->x = x * ha;
q->y = y * ha;
q->z = z * ha;
q->w = cosf(ang / 2);
}

void quat_matrix_4x4(struct quat_t* q, struct vec3_t* origin, float* m) {
m[0] = 1 - 2*(q->y)*(q->y) - 2*(q->z)*(q->z);
m[1] = 2*(q->x)*(q->y) + 2*(q->w)*(q->z);
m[2] = 2*(q->x)*(q->z) - 2*(q->w)*(q->y);
m[3] = 0;
m[4] = 2*(q->x)*(q->y) - 2*(q->w)*(q->z);
m[5] = 1 - 2*(q->x)*(q->x) - 2*(q->z)*(q->z);
m[6] = 2*(q->y)*(q->z) - 2*(q->w)*(q->z);
m[7] = 0;
m[8] = 2*(q->x)*(q->z) + 2*(q->w)*(q->y);
m[9] = 2*(q->y)*(q->z) - 2*(q->w)*(q->z);
m[10] = 1 - 2*(q->x)*(q->x) - 2*(q->y)*(q->y);
m[11] = 0;
m[12] = origin->x;
m[13] = origin->y;
m[14] = origin->z;
m[15] = 1;
}


And the tag structure:
struct md3_tag_t {
struct md3_tag_t* next; /* next tag in the list */

char name[MAX_QPATH]; /* name of tag object */
struct vec3_t origin; /* coordinates */
struct vec3_t axis[3]; /* orientation */
};


Here is the code for rendering the models head:
	/* render head */
glPushMatrix();
tag = model_head->tag_ptr;

struct quat_t local;
struct quat_t total;
quat_init(&total);

float angle = 90;

quat_rotate(&local, angle, tag->axis[0].x, tag->axis[0].y, tag->axis[0].z);
quat_mult(&local, &local, &total);

quat_rotate(&local, angle, tag->axis[1].x, tag->axis[1].y, tag->axis[1].z);
quat_mult(&total, &local, &total);

quat_rotate(&local, angle, tag->axis[2].x, tag->axis[2].y, tag->axis[2].z);
quat_mult(&total, &local, &total);

quat_normalize(&total);
quat_matrix_4x4(&total, &tag->origin, trans_matrix);

glMultMatrixf(trans_matrix);

md3_render(model_head);
glPopMatrix();


The same code is used for the model body.

I have a few questions about this though, since it doesn't seem to work.
The model is squished togther so I must be doing some math wrong - am I missing a step or am I just completely wrong with how I'm doing this?

Also these calculations require an angle or rotation which I don't have (I simply set it to 90 degrees for testing). Where does this angle come from? It's not in the model frame information. At this point I'm not concerned with rendering more than one frame (animation will come later), so this angle is a bit confusing.

Thanks.

Share this post


Link to post
Share on other sites
zedzeek    528
theyre not quaternions.
its been a while since ive looked at quake3 md3's
but i assume this is just the matrix
struct vec3_t origin; /* coordinates */
struct vec3_t axis[3]; /* orientation */

IIRC quake uses z as up ( not y) so perhaps u have some elements around the wrong order

Share this post


Link to post
Share on other sites
para45    122
I have figured out how to get the parts to align.
Apparently I was not using the "head" tag in the body object for the head translation, rather the "head" tag in the head object. When I swapped these around it worked fine.

Although problem arises though. :I can't figure out how to get the body parts to be properly rotated. For example if I just model the whole figure with no rotation (0 degree rotations for each quaternion) the model aligns but the legs may be rotated 90 degrees along the x-axis and the head some arbitrary degree around the z-axis.

If an initialized 0 rotation for each body part on each axis gives some arbitrary position, and if there is no default degree rotation information in the md3, how is it possible to properly construct the model?

Even though MD3 is a relatively old format it's amazing how little documentation I can find on it. (Actually I can find plenty on the format, just very little on tag alignment and rotation.)

Any ideas?

-edit-
Without rotation, the two models "sarge" and "visor" look like this:


-edit 2-
I'm also confused on how animation works. There are a list of "frame" objects, but these only include:
struct md3_frame_t {
struct vec3_t min_bounds;
struct vec3_t max_bounds;
struct vec3_t local_orgin;
float radius;
char name[16];
};

There does not appear to be any assoicated vertex or triangle information with a frame?

[Edited by - para45 on April 22, 2006 8:38:42 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this