[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am    Post subject: : unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!
_________________
http://www.quake.de



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm    Post subject: : two open source viewers can be found here:

http://tfc.duke.free.fr/
_________________
http://www.quake.de



pannan@Posted: Sat May 20, 2006 6:45 pm    Post subject: :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm    Post subject: : Hi, this is my first post on this Board. Smile

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.


_________________
XreaL - http://xreal.sourceforge.net



ViPr@Posted: Mon Jun 05, 2006 2:09 pm    Post subject: : your specular looks kinda off.


Ging@Posted: Sun Jun 11, 2006 11:57 am    Post subject: : I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



bozo@Posted: Wed Aug 04, 2004 2:37 pm    Post subject: Final MD5 File Formats: let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:

MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:

MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:

float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it Smile


Last edited by bozo on Mon Aug 23, 2004 1:17 pm; edited 2 times in total



der_ton@Posted: Wed Aug 04, 2004 4:22 pm    Post subject: : Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )

_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



sic1@Posted: Wed Aug 04, 2004 11:27 pm    Post subject: : Excellent info. I guess now is as better time than any for me to start learning quaternions Smile


BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am    Post subject: : Oh wait, anybody understand quaternions?. Just use them! Wink

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am    Post subject: : Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress Smile


BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am    Post subject: : ¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:



That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! Wink



sic1@Posted: Sat Aug 07, 2004 5:51 am    Post subject: : Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)



I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned Smile



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am    Post subject: : Hey, thanks for posting that image. Just what I was looking for!. Smile


bozo@Posted: Sat Aug 07, 2004 11:39 am    Post subject: : @BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm    Post subject: :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm    Post subject: : I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



bozo@Posted: Sun Aug 08, 2004 11:13 am    Post subject: : @der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:

float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am    Post subject: : So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am    Post subject: :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am    Post subject: : Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am    Post subject: :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am    Post subject: : Awesome, that makes things much easier for me Very Happy

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm    Post subject: : It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Last edited by der_ton on Mon Aug 23, 2004 11:05 am; edited 1 time in total



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am    Post subject: : With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am    Post subject: : Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm    Post subject: Final MD5 File Formats: let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:

MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:

MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:

float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it Smile


Last edited by bozo on Mon Aug 23, 2004 1:17 pm; edited 2 times in total



der_ton@Posted: Wed Aug 04, 2004 4:22 pm    Post subject: : Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )

_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



sic1@Posted: Wed Aug 04, 2004 11:27 pm    Post subject: : Excellent info. I guess now is as better time than any for me to start learning quaternions Smile


BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am    Post subject: : Oh wait, anybody understand quaternions?. Just use them! Wink

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am    Post subject: : Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress Smile


BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am    Post subject: : ¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:



That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! Wink



sic1@Posted: Sat Aug 07, 2004 5:51 am    Post subject: : Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)



I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned Smile



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am    Post subject: : Hey, thanks for posting that image. Just what I was looking for!. Smile


bozo@Posted: Sat Aug 07, 2004 11:39 am    Post subject: : @BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm    Post subject: :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm    Post subject: : I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



bozo@Posted: Sun Aug 08, 2004 11:13 am    Post subject: : @der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:

float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am    Post subject: : So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am    Post subject: :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am    Post subject: : Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am    Post subject: :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am    Post subject: : Awesome, that makes things much easier for me Very Happy

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm    Post subject: : It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Last edited by der_ton on Mon Aug 23, 2004 11:05 am; edited 1 time in total



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am    Post subject: : With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am    Post subject: : Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am    Post subject: : unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!
_________________
http://www.quake.de



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm    Post subject: : two open source viewers can be found here:

http://tfc.duke.free.fr/
_________________
http://www.quake.de



pannan@Posted: Sat May 20, 2006 6:45 pm    Post subject: :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm    Post subject: : Hi, this is my first post on this Board. Smile

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.


_________________
XreaL - http://xreal.sourceforge.net



ViPr@Posted: Mon Jun 05, 2006 2:09 pm    Post subject: : your specular looks kinda off.


Ging@Posted: Sun Jun 11, 2006 11:57 am    Post subject: : I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



bozo@Posted: Fri Sep 24, 2004 2:39 pm    Post subject: : @der_flo

Quote:

what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:

BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) Smile



der_flo@Posted: Fri Sep 24, 2004 7:33 pm    Post subject: : Hi,

thanks for that, but the more i do, the worser it looks Crying or Very sad
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade Wink )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment Wink

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm    Post subject: : BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm    Post subject: : @der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:

in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:

Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm    Post subject: : @bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm    Post subject: : Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



der_flo@Posted: Fri Sep 24, 2004 10:22 pm    Post subject: : @der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) Very Happy

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm    Post subject: : Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm    Post subject: : No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



bozo@Posted: Sat Sep 25, 2004 4:21 pm    Post subject: : i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:

void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:

vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:

...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm    Post subject: : @bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:

- use the following func, the other implementation does not work with the d3 quat's
Code:

void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:

vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:

- in iCalcBones(struct s_models *model) handle parentbones
Code:

...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done Smile

bozo wrote:

i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm    Post subject: : Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm    Post subject: : - i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works Smile (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm    Post subject: : Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man Smile

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm    Post subject: : Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm    Post subject: : i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am    Post subject: : You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_blender_windows_20040810.zip

_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



der_flo@Posted: Sun Sep 26, 2004 11:24 am    Post subject: : Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm    Post subject: : I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull Smile
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am    Post subject: : Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm    Post subject: : Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am    Post subject: : Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:

for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm    Post subject: : Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm    Post subject: : First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm    Post subject: : Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them Smile

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.






der_ton@Posted: Tue Jun 21, 2005 9:32 pm    Post subject: :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm    Post subject: : How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.
_________________
Staff
Learn something today? Why not write an article about it on modwiki.net?



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am    Post subject: : http://wiki.doom3reference.com/wiki/MD5CAMERA_%28file_format%29

Done.
_________________
Staff
Learn something today? Why not write an article about it on modwiki.net?



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am    Post subject: : Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5MESH_%28file_format%29
http://wiki.doom3reference.com/wiki/MD5ANIM_%28file_format%29
http://wiki.doom3reference.com/wiki/MD5CAMERA_%28file_format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. Laughing
_________________
Staff
Learn something today? Why not write an article about it on modwiki.net?



der_ton@Posted: Thu Jun 23, 2005 10:53 am    Post subject: : I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. Wink
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



reklipz@Posted: Mon Aug 08, 2005 5:59 pm    Post subject: : Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm    Post subject: : For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.


Last edited by Dhenry on Thu Sep 29, 2005 12:33 pm; edited 1 time in total



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm    Post subject: : Ok my own MD5 viewer seems to be quite advanced:


I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.


Last edited by Dhenry on Thu Mar 02, 2006 9:48 pm; edited 1 time in total



mcamilo@Posted: Fri Nov 25, 2005 5:15 am    Post subject: questions: Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.
_________________
Márcio Camilo



der_ton@Posted: Fri Nov 25, 2005 5:38 pm    Post subject: Re: questions: Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



mcamilo@Posted: Sat Nov 26, 2005 3:39 am    Post subject: Questions 2: Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.
_________________
Márcio Camilo



der_ton@Posted: Sat Nov 26, 2005 10:32 am    Post subject: : a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.
_________________
Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5



mcamilo@Posted: Mon Nov 28, 2005 5:11 am    Post subject: Questions forever: Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.
_________________
Márcio Camilo



Groove@Posted: Fri Dec 02, 2005 1:24 pm    Post subject: Shadow volume: I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.


Anyway, I have build me own md5 files using notepad:

Code:

MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:


But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.



To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:

void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am    Post subject: doom3 animation models: Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?
_________________
Márcio Camilo



bozo@Posted: Wed Aug 04, 2004 3:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 5:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Thu Aug 05, 2004 12:27 am :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 1:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 4:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 5:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 6:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 7:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 12:39 pm :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 3:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 6:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 12:13 pm :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 5:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 5:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 6:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 7:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 7:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Sat Aug 21, 2004 12:41 am :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 1:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 2:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



[Win]Elchtest@Posted: Wed May 10, 2006 1:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 6:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 7:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 2:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 3:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 12:57 pm :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 6:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 8:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 5:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 3:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 2:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 7:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 7:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 1:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 6:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 7:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 2:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 3:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 12:57 pm :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 6:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 8:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 5:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 3:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 2:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 7:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 7:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



bozo@Posted: Wed Aug 04, 2004 3:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 5:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Thu Aug 05, 2004 12:27 am :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 1:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 4:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 5:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 6:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 7:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 12:39 pm :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 3:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 6:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 12:13 pm :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 5:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 5:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 6:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 7:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 7:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Sat Aug 21, 2004 12:41 am :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 1:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 2:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



[Win]Elchtest@Posted: Wed May 10, 2006 1:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 6:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 7:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 2:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 3:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 12:57 pm :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 6:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 8:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 5:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 3:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 2:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 7:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 7:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



bozo@Posted: Fri Sep 24, 2004 3:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 8:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 9:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 9:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 9:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 10:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 11:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 5:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 5:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 5:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 5:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 6:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 6:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 7:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 8:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 11:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 12:24 pm :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 11:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 11:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



boomcannon@Posted: Sun Dec 05, 2004 12:43 am :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 2:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 7:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 4:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 7:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 10:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 11:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 4:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 7:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 11:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 6:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 6:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 7:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 6:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 6:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 4:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 11:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 6:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 2:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 8:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



Darineth Starwolf@Posted: Mon Aug 23, 2004 2:16 am :
I had read over that particular problem and proposed solution, but since it was md5anim-related, I couldn't see a way to apply it to the problems I'm having. I checked to make sure I'm generating the w-component properly, to make sure it's a unit quaternion, and even checked to make sure I'm not getting negative values inside the square root when calculating ot, so I'm fairly sure it's not that. But, then again, I can't honestly say I understand quaternions so I could be doing something horribly wrong.



Darineth Starwolf@Posted: Mon Aug 23, 2004 8:31 am :
The problem has been corrected. It was a difference in quaternion->bind matrix formula that I had found.



Coder@Posted: Mon Aug 23, 2004 12:45 pm :
Hi everyone!!!

I have source code ( created by myself ;)) of md5mesh/md5anim viewer. If someone wanna have it - mail me : coderserg@mail.ru

But thats not all! I have a question.

The new md5anim format using quaternions for rotations.
In my project I convert all pos and quat values to matrices and using it to represent bone transformations.
But I collide with a problem then I try to use quaternions and position separately. Exactly from quaternions values, 'couse with matrices all works perfectly.
this code works correct:
Code:
vector_t pos = ... read from file...
quaternion_t q = ... read from file ...
q.w = -(float) sqrt(... and so on :)

md5bone_t *pbone = ... current bone...
pbone->m = translate(pos) * q.tomatrix();
if(pbone->pparent)
  pbone->m *= pbone->pparent->m;

Next...
What I'm doing:
All rotations values, in md5anim, describe rotations about parent bone. So to get rotation in world coordinate system we need to multiply parent quaternion on child quaternion.
Code:
...
md5bone_t   pbone   = &pbones[ibone];
md5bone_t   pparent = pbone->pparent;
quaternion_t &qbone = pbone->q;
if( pparent )
qbone = pparent->q * qbone;
...

Complete datatype definition is not important. I hope that You understand logic of this snip! So is this write??? 'Couse this give wrong result!!
In my own model viewer ( my models format ) this techniqu works correctly.
So whats wrong with quaternions in md5 ( or in my code :)).

Thank you.

PS. Sorry for my english :)



skynet@Posted: Mon Aug 23, 2004 12:49 pm :
You seem to forget that the joint hierarchy is not only a hierarchy of rotations, but also translations. What you need to to is to convert all of the quaternion/position pairs into matrices and accumulate them instead of just the quats.



Coder@Posted: Mon Aug 23, 2004 1:14 pm :
So how I can get pure bone rotation(in WCS of course)?
I mean if I want to represent bone, for example, like a box I need to know position and rotation! How I can do this? This is really important! 'Couse in 3dsmax, for example, I can create a bone, but to animate it by keyframes I need positions and rotations defined separately!!!

Thanks.



bozo@Posted: Mon Aug 23, 2004 1:20 pm :
@Coder

also instead of
Code:
q.w = -(float) sqrt(... and so on :)


use better this:
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);



bozo@Posted: Mon Aug 23, 2004 1:26 pm :
@Coder

like skynet wrote, in md5anim the pos and the rot of the bone are relative to the
parentbone

you can represent translation (pos) and rotation with matrices
when you use 4x3 dimension or 4x4 matrices than you can build one matrix that does translation and rotation

so you build a localmatrix of the pos and rot of the bone
and multiply this localmatrix with the worldmatrix of the parentbone and the result is the worldmatrix of the bone
than you transfer the vertices with this worldmatrix

3dsmax: that is only the userinterface, intern max use a matrix for the bones



Coder@Posted: Mon Aug 23, 2004 3:54 pm :
Ok! I'll try this again ( in 100th time :))

What about 3dsmax can You explain in details?

Thanks.



bozo@Posted: Mon Aug 23, 2004 6:13 pm :
@Coder

i don't understand what you really want to know about max

in max you use the ui to work with the models, bones, ....
and max store intern the data of this objects and the state of the workprocess
the data in your example, pos and rot, to be simplified they are stored as a transformationmatrix, in real, there are more than one matrix, because of the different states a obj can be in, ie. the created obj, each modifier applied, spacewarps, ..., to the renderstate
but this is not important to know to export a obj from max or when you work on a md5 viewer


Quote:
I mean if I want to represent bone, for example, like a box I need to know position and rotation! How I can do this?


additional to my post before this one, the md5anim gives you the pos and rot of each bone of each frame of the anim, look in the description of the md5 on the top of this thread, but, like i wrote before, they are only local pos and rot infos, so you must nuild the final transformationmatrix, see before, .....



Coder@Posted: Tue Aug 24, 2004 7:44 am :
2bozo

Thanks again! But all is not so well as I want :)

Thats what I have when I use translation part of bone WM:
pic01
[img]
http://www.gamedev.ru/images/?id=2014
[/img]
And I completly understand this transformations.

But that's what happens than I use primitives to represent a bone:
pic02
[img]
http://www.gamedev.ru/images/?id=2015
[/img]

here is the code that drows this ...
Code:
for(int ibone=0; ibone<nbones; ibone++ )
{
   md5bone_t *pbone = &pbones[ibone];
   vector_t  startpos = pbone->m.translationpart();      
   if(pbone->pchild)
   {
      for(int ichild=0; ichild<pbone->nchild; ichild++ )
      {
         vector_t endpos = pbone->pchild[ichild]->m.translationpart();

         float h = 1.0;
         float w = 1.0;

         float p1 = (endpos - startpos).length();
         vertex_t vtx[] =
         {
            {0,0,0},   {w,-h,h},  {w,h,h},
            {0,0,0},   {w,h,h},   {w,h,-h},
            {0,0,0},   {w,-h,-h}, {w,-h,h},
            {0,0,0},   {w,h,-h},  {w,-h,-h},
            {w,h,h},   {w,-h,h},  {p1,0,0},
            {w,h,h},   {p1,0,0},  {w,h,-h},
            {w,-h,h},  {w,-h,-h}, {p1,0,0},
            {w,-h,-h}, {w,h,-h},  {p1,0,0}

         };
         
         glColor3f(0.0f, 1.0f, 0.0f);
         glBegin(GL_TRIANGLES);   
         for(int i=0; i< sizeof(vtx)/sizeof(vertex_t); i++)
         {
            vector_t v(vtx[i].x, vtx[i].y, vtx[i].z);
            matrix_t m = pbone->pchild[ichild]->m;// or pbone->m = no  difference (wrong result)
            v = pbone->m * v;
            glVertex3fv(v.xyz);
         }
         glEnd();

         glColor3f(1.0f, 1.0f, 1.0f);
         glBegin(GL_LINES);
         {
            glVertex3fv(startpos.xyz);
            glVertex3fv(endpos.xyz);
         }
         glEnd();
      }

   }
}


My final inference: The bone WM not in all cases represent full transformation. In some cases bone just make a move in other just rotate.
But if in some frame bone not rotated this doesn't mean that the rotation value is set to 0. This means that the rotaion value in current frame are the same as in previos frame.
Then I was working on my exporter(MAXScript) I used <bone>.rotaion ans <bone>.position values to store transformations. In application I multiply two matrices(pos->tomatrix and quat->tomatrix) to represent bone transformation. But there is no problems then I try to use quat and pos separately! I can multily vertex(vector) by quaternion to rotate it and add position vector to translate it.
I'm tring to uderstand all workflow - from model exporting to model rendering in engine.
In 3dsmax bone object have rotaion and position value independently of animation. In any time(as 3dsmax timleslider) is posible to get current pos and rot values for the bone. So why in md5 this happens? (I know that md5 was created in Maya;))

In some cases my caculations going write:
hellknight:
[img]
http://www.gamedev.ru/images/?id=2016
[/img]

I think that this happend 'couse bone matrix contains both pos and rot.

Plaese excuse me for my pestering question, but my recalcitrance don't leting me go ;)
If someone can explain the core of my mistakes, I'll be thankful very much!

Have a nice day! :)



bozo@Posted: Wed Aug 25, 2004 10:50 am :
to the bonegeom. thing:

your bonegeom. is along the x axis

your transform of the vertices of the bonegeom. with the parentbone or bone matrix only transform your x axis aligned bonegeom. in there coord.sys.

but when you want to draw this bonegeom. as a representation from the parentbone to the bone, you must use the direction od the vector (endpos - startpos)

what you must do is, build from this directionvector a rotation, ie. you could use the dir as the forward vector and create the perpendicular left and up vectors to build a rotationmatrix or build from this directionvector a quaternion that represent this rotation, and then transform the bonegeom.vertices with this matrix/quat and finally add the parentbone (the startpos of the bonegeom.) to this transformed vertices

you can think about this as when you first rotate your bonegeom. to the right direction (bone-parentbone) and then move them to the right place (parentbone pos)



a transformationmatrix represents a full transformation (translation, rotation, scale, shear, perspective, ...), all what you want to do, it only must only push the right data into the matrix

there is no ONE way to represent and work with translations and rotations
so, ie. max let you access the translation and rotation values of the obj's in different kinds, as a matrix, vector, quaternion, euler angles, ...., but they all represent the same translation and rotation
and yes you can use a quat and pos to transform the vertices like you can use a matrix to do this

the md5's have no direct releationship to a specific 3d program
in the md5anim is all data stored that you need to build the transforminfo of the bones for each frame
which way you do this is left to your choice
ie. you could build from the stored local pos and rot a local matrix, and than build the worldmatrix of the bone from his localmatrix and the parentboneworldmatrix or you can build a pos and quat the represent the worldtransformation of the bone from the local pos and quat of the bone and the world pos and quat of the parentbone



sigget@Posted: Sun Aug 29, 2004 6:43 pm :
Excellent work bozo! Mind if i link to this post from my page at:
http://sigget.rivin.se/games/doom3/index.html



sigget@Posted: Sun Aug 29, 2004 11:40 pm :
I went ahead and added that link, hope that's okay.



bozo@Posted: Mon Aug 30, 2004 10:45 am :
@sigget
for sure, it's ok
nice site you have created :)



sigget@Posted: Mon Aug 30, 2004 8:11 pm :
thanks, im hoping to turn it into a complete reference site, lots of work though



Darineth Starwolf@Posted: Wed Sep 01, 2004 8:16 am :
So I've been working on implementing V10's animation style, and I'm having trouble with the relationship between the baseframe, individual frames, and the parent. While bozo's specs at the beginning of this thread are amazing in and of themselves, it's not at all specific about how to apply the individual transformations for the animation stuff. If anyone has a quick way of describing this relationship (i.e. is the frame relative to the baseframe which is relative to the parent?), I'd greatly appreciate the assistance.



der_ton@Posted: Wed Sep 01, 2004 10:04 am :
The values in the framedata are not relative, or not incremental. You simply replace the values in the baseframe by the values you extract from the framedata, and the resulting pose will be the one at that frametime.



nohbdy@Posted: Wed Sep 01, 2004 4:14 pm :
Darineth: I'm guessing by that you mean how each joint's entry into the baseframe section is related to the parent joint... The actual animation is simple once you understand how to build the baseframe's skeleton (simple substitution of components, like der_ton said).

Here's some code from my MD5Model/Anim viewer (Which looks totally awesome and I will release here with source once I add a few more features like reading directly from the PK4 files). It uses D3DX for Vectors(Point3D is a typedef of D3DXVECTOR3) and Quats

Code:
void MD5Anim::BuildSkeletons() {
   Point3D transformedPos;
   MD5Joint* thisJoint;
   MD5Joint* parentJoint;
   int parentID = 0;

   //First, initialize our base pose skeleton
   baseSkeleton = new MD5Skeleton();
   baseSkeleton->SetNumJoints(numJoints);
   for (int i = 0; i < numJoints; i++) {
      thisJoint = new MD5Joint;
      parentID = joints[i].parentID;
      parentJoint = baseSkeleton->GetJoint(parentID);
      thisJoint->ID = i;
      if (parentID < 0) { //Has no parent, must be origin
         thisJoint->orientation = baseFrame[i].orientation;
         thisJoint->position = baseFrame[i].position;
      } else {
         //Transform our translation by our parent's rotation quaternion and add our parent translation
         TransformPoint(&transformedPos, &baseFrame[i].position, &parentJoint->orientation);
         thisJoint->position = transformedPos + parentJoint->position;

         //Rotate our baseFrame orientation by our parent joint's rotation quaternion
         thisJoint->orientation = baseFrame[i].orientation * parentJoint->orientation; //Order is important!
         D3DXQuaternionNormalize(&thisJoint->orientation,&thisJoint->orientation);
      }
      thisJoint->parent = parentID;
      baseSkeleton->AddJoint(thisJoint);
   }

   //Then, build each frame skeleton
   int bitField = 0;
   int currComponent = 0;
   Quat animatedOrientation;
   Point3D animatedPosition;
   frameSkeletons = new MD5SkeletonPtr[numFrames];
   for (int i = 0; i < numFrames; i++) {
      frameSkeletons[i] = new MD5Skeleton();
   }
   for (int i = 0; i < numFrames; i++) {
      frameSkeletons[i]->SetNumJoints(numJoints);
      currComponent = 0;

      for (int j = 0; j < numJoints; j++) {
         thisJoint = new MD5Joint;
         parentID = joints[j].parentID;
         parentJoint = frameSkeletons[i]->GetJoint(parentID);
         thisJoint->ID = j;

         animatedOrientation = baseFrame[j].orientation;
         animatedPosition = baseFrame[j].position;
         bitField = joints[j].bitField;
         if (bitField != 0) {
            if (bitField & MD5AnimJoint::Tx) animatedPosition.x = frames[i].animationComponents[currComponent++];
            if (bitField & MD5AnimJoint::Ty) animatedPosition.y = frames[i].animationComponents[currComponent++];
            if (bitField & MD5AnimJoint::Tz) animatedPosition.z = frames[i].animationComponents[currComponent++];
            if (bitField & MD5AnimJoint::Qx) animatedOrientation.x = frames[i].animationComponents[currComponent++];
            if (bitField & MD5AnimJoint::Qy) animatedOrientation.y = frames[i].animationComponents[currComponent++];
            if (bitField & MD5AnimJoint::Qz) animatedOrientation.z = frames[i].animationComponents[currComponent++];
         }
         if (parentID < 0) {
            thisJoint->orientation = animatedOrientation;
            thisJoint->position = animatedPosition;
         } else {
            TransformPoint(&transformedPos, &animatedPosition, &parentJoint->orientation);
            thisJoint->position = transformedPos + parentJoint->position;

            thisJoint->orientation = animatedOrientation * parentJoint->orientation;
            D3DXQuaternionNormalize(&thisJoint->orientation,&thisJoint->orientation);
         }
         thisJoint->parent = parentID;
         frameSkeletons[i]->AddJoint(thisJoint);
      }
   }
   return;
}


To transform the joint's orientation, you just multiply it by it's parent's transformed orientation.

Transforming the joint's position is exactly like how the vertices are transformed/skinned to the skeleton, assuming you have that part working. TransformPoint(Point3D* result,Point3D* position,Quat* rotation) just multiplies the 'position' by the 'rotation' and puts the answer into 'result'. I don't bother converting to matrix format since it's faster using quaternions (and if you want to skin a new interpolated skeleton each frame, speed can be important)

Just for the record, TransformPoint works like this (psuedocode):
Code:
void TransformPoint(Point3D* dest, Point3D* point, Quat* rotation) {
   // rotation must be a unit length quaternion
   //dest = rotation * point * Inverse(rotation)
   //   Inverse(rotation) is the same as Conjugate(rotation) since it's unit length
   //dest = (rotation.w + rotation.xyz) * point * (rotation.w - rotation.xyz)
   //  <bunch of steps that I leave as an exercise to the reader>
   s = rotation.w    //Scalar part
   v = rotation.xyz //Vector part
   P = point
   dest = (s^2 - Dot(v,v))*P + 2*s*Cross(v,P) + 2*Dot(v,P)*v
}


I hope this answers your question, but if it doesn't it should still be a good contribution to the thread : )

-nohbdy



Monstrous@Posted: Sat Sep 04, 2004 9:13 pm :
Great thread.

Thanks to the information provided here I can now load MD5 models into my game engine. I wrote a little high-level tutorial about it here: http://home.planet.nl/~monstrous/tutmd5mesh.html and that includes a demo for download (models not included).

Greetings.



der_ton@Posted: Sat Sep 04, 2004 10:29 pm :
Thanks for writing up that info. BTW, that's a nice site with lots of programming info there. :)
[/code]



kentaro-k.21@Posted: Sun Sep 05, 2004 9:23 am :
hi.

bozo wrote:
Quote:
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);



nohbdy wrote:
Quote:
Code:
         if (parentID < 0) {
            thisJoint->orientation = animatedOrientation;
            thisJoint->position = animatedPosition;
         } else {
            TransformPoint(&transformedPos, &animatedPosition, &parentJoint->orientation);
            thisJoint->position = transformedPos + parentJoint->position;

            thisJoint->orientation = animatedOrientation * parentJoint->orientation;
            D3DXQuaternionNormalize(&thisJoint->orientation,&thisJoint->orientation);
         }



thanks. their codes much benefit for my md5anim renderer.

i couldn't create my one without their helpful code samples!



der_flo@Posted: Fri Sep 17, 2004 10:35 pm :
Hello,

i dont understand, for what quaternions are good and can be used.
Does anyone has a good and easy to understand documantation about
that magic voodoo (in german or english)?

Thanks a lot

Regards

Flo



der_ton@Posted: Fri Sep 17, 2004 11:43 pm :
Here's all you need:
http://www.j3d.org/matrix_faq/matrfaq_latest.html

Basically you don't have to understand quaternions, you just have to calculate the missing/redundant quat.z value with sqr(1-x*x-y*y-z*z) and then use a quaternion_to_matrix conversion if you're more comfortable with matrices.



der_flo@Posted: Sat Sep 18, 2004 10:28 am :
I think i do slowly know, why they exist in the md5 file format:
for the rotation/orientation?
So i need them to calcucate the exact position of each vertex
in relation to it´s bone?

Is that right, or am i totaly lost :?

Regards
Flo



julienr@Posted: Sat Sep 18, 2004 7:59 pm :
Is there a difference between the joints hierarchy in the md5mesh and md5anim files ?
I watched a few models and there doesn't seem to be any difference...
Is it used for things like dismember the monsters ? Anybody has an example ?
Thanks !



der_ton@Posted: Sun Sep 19, 2004 2:21 pm :
der_flo: that's right, quaternions represent rotations, like bozo's first post in this thread points out.

julienr: I haven't seen a md5anim where there was a difference in hierarchy to the md5mesh file, either. Theoretically this would be possible though, because the hierarchy info in the md5mesh is actually redundant and is not needed. Having a different per-md5anim hierarchy could technically be possible, no matter if it makes sense from an artists point of view or not... :)



der_flo@Posted: Tue Sep 21, 2004 2:30 pm :
Hello,

i just realized, that in version 10 of the md5mesh file is something missing:
the number of the mesh you are reading:
mesh {
but in version 6 it was
mesh 1 { // or any other number
Am i able to have more than just one mesh in one md5 mesh file
or should i forget about that meshes in the md5mesh file
and concentrate on those in the md5anim file?

Regards

Flo



nohbdy@Posted: Tue Sep 21, 2004 5:24 pm :
There's a "numMeshes #" at the top of the file that tells you how many meshes there are... each mesh being labeled "mesh # {" is just redundant/unnecessary since the individual mesh numbers/indices don't really serve any purpose



der_flo@Posted: Wed Sep 22, 2004 6:57 pm :
nohbdy: thats true ...

der_ton: does your blender exporter export multiple meshes, or just one, and how do you handle multiple animations, do they each get in an extra file?

regards

flo



der_ton@Posted: Wed Sep 22, 2004 8:22 pm :
The blender exporter handles multiple meshes nicely, you can have different objects that comprise the character, and different used materials within each object. But I don't take credit for that, most of that was done by Jiba, who wrote the Cal3D export script that my exporter is partially based on. There's not too much code left of the original script, but the mesh handling and splitting is almost untouched.

Regarding multiple animations, you have to specify a frame interval for the animation export. You can have multiple animations in your .blend file and do several exports with different intervals, for example. I probably should add a "don't export mesh" option to speed up the process for exporters. I could also add an option to read the animation setup data from file (like what animations have which intervals, and the filenames they should get exported to. Now that would be export process automation heaven...).



seith@Posted: Wed Sep 22, 2004 8:27 pm :
I was wondering, is there a solution for the problem der_ton mentionned last month?

Quote:
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634


Because I seem to be having the same kind of problem. The values are indeed flipping in the anim file itself, and I was wondering how to get past that annoyance....



der_ton@Posted: Wed Sep 22, 2004 9:27 pm :
I'm not so sure anymore if the toggling really caused the animations to get borked. I think it was caused by
Code:
qw = -sqrt(1.0f - qx*qx - qy*qy - qz*qz)

Because you can end up with a negative number passed to the sqrt(). You have to clamp that term to zero if it's negative, and then calculate the sqrt(). That solved it I think.



der_flo@Posted: Thu Sep 23, 2004 11:26 am :
I got it managed to interpolate the positions of each bone,
but i dont get it how to interpolate the rotation matrices or
quaternions, can anybody give a brief instruction, or example
code?

Kind regards

Flo



nohbdy@Posted: Thu Sep 23, 2004 2:27 pm :
To interpolate quaternions, use a function called slerp (works just like lerp for positions)... for matrices.. uhh.. convert to quaternions and use slerp :)

Slerp stands for spherical linear interpolation, formula for interpolating from A to B with factor t (t would be your time between frames, normalized to range [0,1]) is something like:
[ sin(theta*(1-t))*A + sin(theta*t)*B ] / sin(theta), theta being the angle of separation between the two quaternions (arccos(A dot B) I think?) but don't quote me on any of that math, better to look it up yourself.

-nohbdy



bozo@Posted: Thu Sep 23, 2004 2:35 pm :
@der_flo

Code:
void UpdateBones(int keyframe1, int keyframe2, float deltatime)
...
quaternion_t qr;

QuaternionSlerp(ba->keyframes[keyframe1].rot, ba->keyframes[keyframe2].rot, deltatime, qr);
...


Code:
void QuaternionSlerp(const quaternion_t q1, const quaternion_t q2, const vec_t t, quaternion_t qr)
{
   vec_t sq1, sq2;

   vec_t cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];

   quaternion_t q2b;

   if( cosom < 0.0f)
   {
      cosom = -cosom;
      q2b[0] = -q2[0];
      q2b[1] = -q2[1];
      q2b[2] = -q2[2];
      q2b[3] = -q2[3];
   }
   else
   {
      QuaternionCopy(q2, q2b);
   }

   if( (1.0f + cosom) > 1E-5)
   {
      if( (1.0f - cosom) > 1E-5)
      {
         vec_t om = (vec_t) acos(cosom);
         vec_t rsinom = (vec_t)(1.0f / sin(om));

         sq1 = (vec_t)sin( (1.0f - t) * om) * rsinom;
         sq2 = (vec_t)sin(t * om) * rsinom;
      }
      else
      {
         sq1 = (vec_t)(1.0f - t);
         sq2 = t;
      }

      qr[3] = sq1 * q1[3] + sq2 * q2b[3];
      qr[0] = sq1 * q1[0] + sq2 * q2b[0];
      qr[1] = sq1 * q1[1] + sq2 * q2b[1];
      qr[2] = sq1 * q1[2] + sq2 * q2b[2];
   }
   else
   {
      const vec_t PI = (vec_t)3.14159265358979323846f;

      sq1 = (vec_t)sin( (1.0f - t) * 0.5f * PI);
      sq2 = (vec_t)sin(t * 0.5f * PI);

      qr[3] = sq1 * q1[3] + sq2 * q1[2];
      qr[0] = sq1 * q1[0] + sq2 * q1[1];
      qr[1] = sq1 * q1[1] + sq2 * q1[0];
      qr[2] = sq1 * q1[2] + sq2 * q1[3];
   }
}


(maybe a simple lerp is good enough for the quality of the anim, i read that most of the time a lerp has no big differences to the slerp in the visual result, but it seems that the most use nevertheless the slerp)



der_flo@Posted: Thu Sep 23, 2004 8:01 pm :
Hi,

you are a god, but if i implement this code to interpolate
between the bones quaternions, the rendered models look
very freaky, so maybe i have fogoten something, here´s what i am doing
befor rendering the model with it animations:
1. i calculate an interpolation value (its a float) between this and the next frame
2. After that i calculate the new bone positions
3. Then i calculate the final vertex positions relative to there bones,
and draw them.

I think the failure happens in step 2, becouse if i disable this step,
then the animation "freezes" and the baseframe is displayed, and that´s
looking perfect.

Code:
    152 int iCalcBones(struct s_models *model) {
    153   if (model == NULL) {
    154     return -1;
    155   }
    156   unsigned int i, j;
    157   float par_mat[16], this_mat[16], interpol;
    158   struct s_animations *anim = model->anim;
    159   struct s_anim_frames *currframe, *nextframe;
    160   currframe = &anim->anim_frames[anim->curr_frame];
    161   nextframe = &anim->anim_frames[anim->next_frame];
    162   //vLogMe("currframe: %d\tnextframe: %d\n",anim->curr_frame,anim->next_frame);
    163   if (currframe == NULL) {
    164     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    165     return 0;
    166   }
    167   if (nextframe == NULL) {
    168     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    169     return 0;
    170   }
    171   interpol = anim->interpol;
    172   i = 0;
    173   model->bones[i].calc_pos.x = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    174   model->bones[i].calc_pos.y = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    175   model->bones[i].calc_pos.z = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    176   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    177   // here i calculate the absolute matrix for this bone
    178   for (i=1; i<anim->numJoints; i++) {
    179     model->bones[i].calc_pos.x = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    180     model->bones[i].calc_pos.x += model->bones[i-1].calc_pos.x;
    181     model->bones[i].calc_pos.y = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    182     model->bones[i].calc_pos.y += model->bones[i-1].calc_pos.y;
    183     model->bones[i].calc_pos.z = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    184     model->bones[i].calc_pos.z += model->bones[i-1].calc_pos.z;
    185     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    186     // here i calculate the absolute matrix for this bone
    187   }
    188   return 0;
    189 }

the code that calculates the new matrix:
Code:
    213     float term = 1.0f - (model->bones[i].quat.x*model->bones[i].quat.x) - (model->bones[i].quat.y*model->bones[i]        .quat.y) - (model->bones[i].quat.z*model->bones[i].quat.z);
    214     if (term < 0.0f) {
    215       model->bones[i].quat.w = 0.0f;
    216     }else{
    217       model->bones[i].quat.w = - (float) sqrt(term);
    218     }
    219     float xx = model->bones[i].quat.x * model->bones[i].quat.x;
    220     float xy = model->bones[i].quat.y * model->bones[i].quat.y;
    221     float xz = model->bones[i].quat.x * model->bones[i].quat.z;
    222     float xw = model->bones[i].quat.x * model->bones[i].quat.w;
    223     float yy = model->bones[i].quat.y * model->bones[i].quat.y;
    224     float yz = model->bones[i].quat.y * model->bones[i].quat.z;
    225     float yw = model->bones[i].quat.y * model->bones[i].quat.w;
    226     float zz = model->bones[i].quat.z * model->bones[i].quat.z;
    227     float zw = model->bones[i].quat.z * model->bones[i].quat.w;
    228     model->bones[i].calc_mat[0]  = 1 - 2 * ( yy + zz );
    229     model->bones[i].calc_mat[1]  =     2 * ( xy - zw );
    230     model->bones[i].calc_mat[2]  =     2 * ( xz + yw );
    231     model->bones[i].calc_mat[4]  =     2 * ( xy + zw );
    232     model->bones[i].calc_mat[5]  = 1 - 2 * ( xx + zz );
    233     model->bones[i].calc_mat[6]  =     2 * ( yz - xw );
    234     model->bones[i].calc_mat[8]  =     2 * ( xz - yw );
    235     model->bones[i].calc_mat[9]  =     2 * ( yz + xw );
    236     model->bones[i].calc_mat[10] = 1 - 2 * ( xx + yy );
    237     model->bones[i].calc_mat[3]  = model->bones[i].calc_mat[7] = model->bones[i].calc_mat[11] = model->bones[i].c        alc_mat[12] = model->bones[i].calc_mat[13] = model->bones[i].calc_mat[14] = 0;
    238     model->bones[i].calc_mat[15] = 1;


The code is in a pre alpha stadium :wink:

Kind Regards

Flo



bozo@Posted: Fri Sep 24, 2004 10:28 am :
@der_flo


in your "the code that calculates the new matrix":

why do you calc model->bones[i].quat.w?
model->bones[i].quat is a full quat
this w-calc is only used when you build the anim orientations from the md5anim file, than you have full quat's and all calc with them are also full quat's



in iCalcBones:

it seems your pos calc is not right, you add the pos of the prev bone to the current

what i would do is:

-calc the current animtime localpos via interpolation between the two keyframes (like you do)
-calc the current animtime localrot via QuaternionSlerp between the two keyframes (like you do)

than their are two methodes:
you can use this values without matrixconvertion and do the needed calc's with them (but i skip the methode here)
or you build matrices and use them for the rest of the work

so build a matrix of the current animtime localpos and localrot
multiply this localmat with the parent worldmat, than this is the worldmat for the current bone

the parent of the current bone is not the prev bone in the bones array, the bone has an index to the right parent
because in md5models the parentbones are always before (but not necessarily the prev bone) the childrens, you can calc the bones in a loop and access the parentmat without trouble

than you have the mat of the bone for the use in the vertextransform



der_flo@Posted: Fri Sep 24, 2004 10:33 am :
Hi,

i think i just realized, what i have forgoten:
I have to add the rotation to the rotation of
the previus bone, right?
Do i have to multiply them or will that be the wrong way
of combining roations?

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 12:41 pm :
First of all, thank you for helping me :)

bozo wrote:
@der_flo


in your "the code that calculates the new matrix":

why do you calc model->bones[i].quat.w?
model->bones[i].quat is a full quat
this w-calc is only used when you build the anim orientations from the md5anim file, than you have full quat's and all calc with them are also full quat's


Well, as i said, this code is still alpha...

bozo wrote:

in iCalcBones:

it seems your pos calc is not right, you add the pos of the prev bone to the current


Well, thats right, but the model i am rendering at this point only has 4 bones and in this model every bone´s parent is the previous bone, but i bugfixed that for other models with more bones and where the parent bone is not just the previuos.

bozo wrote:
what i would do is:

-calc the current animtime localpos via interpolation between the two keyframes (like you do)
-calc the current animtime localrot via QuaternionSlerp between the two keyframes (like you do)

than their are two methodes:
you can use this values without matrixconvertion and do the needed calc's with them (but i skip the methode here)
or you build matrices and use them for the rest of the work

so build a matrix of the current animtime localpos and localrot
multiply this localmat with the parent worldmat, than this is the worldmat for the current bone


what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?

bozo wrote:
the parent of the current bone is not the prev bone in the bones array, the bone has an index to the right parent
because in md5models the parentbones are always before (but not necessarily the prev bone) the childrens, you can calc the bones in a loop and access the parentmat without trouble

than you have the mat of the bone for the use in the vertextransform


Thanks for all of that, i think i am on the right way now :)

BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?

Kind regards

Flo



der_ton@Posted: Fri Sep 24, 2004 12:59 pm :
If you plan to do any further calculations with the final vertex positions, you'll have to do that on the CPU anyway. For example silhouette detection or tangentspace calculations...



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



bozo@Posted: Wed Aug 04, 2004 3:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 5:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Thu Aug 05, 2004 12:27 am :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 1:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 4:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 5:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 6:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 7:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 12:39 pm :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 3:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 6:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 12:13 pm :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 5:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 5:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 6:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 7:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 7:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Sat Aug 21, 2004 12:41 am :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 1:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 2:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



[Win]Elchtest@Posted: Wed May 10, 2006 1:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 6:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 7:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 2:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 3:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 12:57 pm :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 6:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 8:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 5:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 3:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 2:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 7:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 7:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



bozo@Posted: Fri Sep 24, 2004 3:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 8:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 9:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 9:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 9:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 10:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 11:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 5:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 5:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 5:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 5:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 6:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 6:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 7:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 8:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 11:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 12:24 pm :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 11:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 11:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



boomcannon@Posted: Sun Dec 05, 2004 12:43 am :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 2:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 7:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 4:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 7:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 10:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 11:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 4:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 7:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 11:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 6:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 6:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 7:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 6:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 6:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 4:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 11:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 6:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 2:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 8:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am : Doom3world • View topic - Final MD5 File Formats

Doom3world

The world is yours! Doom 3 - Quake 4 - ET:QW - Prey - Rage
It is currently Fri Jan 04, 2008 1:48 pm

All times are UTC




Post new topic Reply to topic  [ 113 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next
Author Message
 Post subject:
PostPosted: Sat Dec 04, 2004 11:43 pm 
Offline
is connecting to Doom3world.org

Joined: Thu Dec 02, 2004 10:01 am
Posts: 2
Location: Mars
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz


Top
 Profile  
 
 Post subject:
PostPosted: Sun Dec 12, 2004 1:40 am 
Offline
is connecting to Doom3world.org

Joined: Fri Dec 03, 2004 5:39 am
Posts: 8
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 20, 2005 6:51 pm 
Offline
is connecting to Doom3world.org

Joined: Thu Feb 10, 2005 2:45 pm
Posts: 4
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 21, 2005 3:00 pm 
Offline
Plasma Spammer
User avatar

Joined: Fri Jun 27, 2003 10:24 pm
Posts: 2528
Location: Munich, Germany
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.

_________________
Image Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 21, 2005 6:56 pm 
Offline
is connecting to Doom3world.org

Joined: Thu Feb 10, 2005 2:45 pm
Posts: 4
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 21, 2005 9:32 pm 
Offline
Plasma Spammer
User avatar

Joined: Fri Jun 27, 2003 10:24 pm
Posts: 2528
Location: Munich, Germany
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!

_________________
Image Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 21, 2005 10:43 pm 
Offline
a gun & a nice word
User avatar

Joined: Sat Jan 11, 2003 8:30 pm
Posts: 7565
Location: Orlando, FL
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.

_________________
Image Staff
Learn something today? Why not write an article about it on modwiki.net?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 22, 2005 3:26 am 
Offline
a gun & a nice word
User avatar

Joined: Sat Jan 11, 2003 8:30 pm
Posts: 7565
Location: Orlando, FL
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.

_________________
Image Staff
Learn something today? Why not write an article about it on modwiki.net?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 23, 2005 6:51 am 
Offline
a gun & a nice word
User avatar

Joined: Sat Jan 11, 2003 8:30 pm
Posts: 7565
Location: Orlando, FL
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:

_________________
Image Staff
Learn something today? Why not write an article about it on modwiki.net?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 23, 2005 10:53 am 
Offline
Plasma Spammer
User avatar

Joined: Fri Jun 27, 2003 10:24 pm
Posts: 2528
Location: Munich, Germany
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:

_________________
Image Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 08, 2005 5:59 pm 
Offline
has joined the game

Joined: Tue Aug 02, 2005 9:08 pm
Posts: 31
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 5:58 pm 
Offline
is connecting to Doom3world.org

Joined: Thu Feb 10, 2005 2:45 pm
Posts: 4
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.


Last edited by Dhenry on Thu Sep 29, 2005 12:33 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 10, 2005 6:12 pm 
Offline
is connecting to Doom3world.org

Joined: Thu Feb 10, 2005 2:45 pm
Posts: 4
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.


Last edited by Dhenry on Thu Mar 02, 2006 9:48 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: questions
PostPosted: Fri Nov 25, 2005 5:15 am 
Offline
is connecting to Doom3world.org

Joined: Fri Nov 25, 2005 4:31 am
Posts: 9
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.

_________________
Márcio Camilo


Top
 Profile  
 
 Post subject: Re: questions
PostPosted: Fri Nov 25, 2005 5:38 pm 
Offline
Plasma Spammer
User avatar

Joined: Fri Jun 27, 2003 10:24 pm
Posts: 2528
Location: Munich, Germany
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.

_________________
Image Staff
Modelviewer | 3DSMax<->MD5 | Blender<->MD5


Top
 Profile  
 
 Post subject: Questions 2
PostPosted: Sat Nov 26, 2005 3:39 am 
Offline
is connecting to Doom3world.org

Joined: Fri Nov 25, 2005 4:31 am
Posts: 9



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



[Win]Elchtest@Posted: Wed May 10, 2006 12:17 am :
unfortunatly most of the download links to md5 viewers with source are dead now.

anyone wants to re-upload them? or how about posting your md5 viewer source, even if it's not polished up? people want to see and learn :)

thanx!



[Win]Elchtest@Posted: Fri May 12, 2006 5:44 pm :
two open source viewers can be found here:

http://tfc.duke.free.fr/



pannan@Posted: Sat May 20, 2006 6:45 pm :
[Win]Elchtest wrote:
two open source viewers can be found here:

http://tfc.duke.free.fr/


Good Job!
Thanks to your threads.



Tr3B@Posted: Mon Jun 05, 2006 1:18 pm :
Hi, this is my first post on this Board. :)

I implemented .md5mesh/.md5anim support into my Q3A engine version called XreaL. The source is licensed under GPL. You can get it via Subversion. Just head over the project site and look for SourceCode.

Image



ViPr@Posted: Mon Jun 05, 2006 2:09 pm :
your specular looks kinda off.



Ging@Posted: Sun Jun 11, 2006 11:57 am :
I'm doing an opengl md5 viewer for a bit of uni work and I'm suffering from some oddities when it comes to displaying animations (surprise, surprise).

this image shows the issue, the left hand image is the mesh using the md5mesh joint information, the middle is the base frame of the idle animation, the right is frame 0. (the green lines are drawing the bones out for me)

I'm applying relative transformations to the joint hierachy for each frame - I've double checked that against some of the code in this thread, so I can but assume I'm missing something else - maybe a transformation against the modelview matrix?



ephtracy@Posted: Fri May 11, 2007 5:21 pm :
It took me three days to study md5mesh and md5anim.
Though there are also lots of problems that remain to be solved, I'm glad to see the hellknight alive in my program now... :)

I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:



der_ton@Posted: Mon May 14, 2007 7:33 pm :
ephtracy wrote:
I calculate the planes of triangles and normals, tangents of vertices every frame in order to get correct shadow and bump specular effect.However, I remenber the normals and tangents can be calculated by weights and jointMats just like the vertices.How can I get it :?:

Here is some discussion on that: http://www.doom3world.org/phpbb2/viewto ... 2179#22179



ephtracy@Posted: Wed May 16, 2007 4:48 pm :
Thanks for your reply. It helps me a lot.:D
And another question, how could I blend two animations,
for example, from walking to attacking when the monster discovers the player. It makes me a little confused.



der_ton@Posted: Thu May 17, 2007 2:21 pm :
There are several methods and there's no "right" or "wrong" way, but just visually good and not so good ways.
A very easy but visually not so good way would be to interpolate the model-space bone positions and rotations after the two animation poses have been calculated for the whole skeleton.
Better would be to interpolate the animated parentspace positions and rotations. There might be other better ways but I don't have any experience with this.



ehmdjii@Posted: Fri May 18, 2007 1:29 pm :
hello,
i was once again wondering about the original purpose of the flags in the md5anim file format.

"root" -1 63 0 // ( Tx Ty Tz Qx Qy Qz )

for example the "63"-flag would mean that all 6 components should be taken from this files frame data, right?

but what is the actual purpose of that?


and is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation? if so, how? thanks!



ephtracy@Posted: Fri May 18, 2007 6:30 pm :
Hi, guy, here are some of my ideas :

1. The component flag is set to compress the data of frame anims.
Just think about an arm of a robot which can only be rotated around one axis and cannot move anyway. In local space, only one component of orientation is animated. .

Or the animation is just for part of the body, for example, a pose of hand, an expression of face, and so on.

the others are static and useless as well, so it is no need to descript them in the file.

2. I remember I have seen it in one of doom3 files that the animation just control the body of the player, while the head is controled by the sight of player in the game.

Remenber, The frame animation is descripted in local space. You can assign different anims to differect parts of the skeleton and merge them together with the hierarchy, or even blend two anims by interpolating to get a strange pose. Some of the joint anims can be loaded from file, while some can be generated in real time to interact with enviroment.



der_ton@Posted: Fri May 18, 2007 6:51 pm :
edit: ephtracy said it already, but here is what I typed earlier anyway.

I think the flags (and the fact that values that remain static are not saved in the frame) are just to save space.

Quote:
is it possible to somehow let the model play two animations (md5anims) at once where certain bones are affected by one animation and other bones by the second animation?

In Doom3 it is possible, it is called animation channels, they are just names for groups of bones, and are defined in a model def, and I think they are referenced by the animation control code in the ai scripts.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



bozo@Posted: Fri Sep 24, 2004 2:39 pm :
@der_flo

Quote:
what do you mean with "build a matrix of the current animtime localpos", do you mean a transformation matrix? So i should combine the rotation and the transformation matrix with a simple matrix multiplication, right?
And when drawing the vertices, they have to be multilied by this worldmat from this bone?


yes, you can build a transformationmat from the pos and a rotationmat from the quat, and combine them with a mat_multiplay, and use this final mat to transform the verts

Quote:
BTW: Is it faster to let a vertex sheder do the matrix multiplication or
or will it be faster to this in 3DNow or SSE Code?


additional to der_ton's post,
you can get problems with the limits of some versions of the vs's (ie. with vs < 2.0 you have not much constants to store the bonetransform's), and must work around this (ie. split the mesh, use directly the quat's in the vs to save space in the contants and transform, ....)

i think the best is, to do the work with the cpu,
and also first get it running (and than think on optimations like sse) :)



der_flo@Posted: Fri Sep 24, 2004 7:33 pm :
Hi,

thanks for that, but the more i do, the worser it looks :cry:
This is now the code that calculates the bones rotation and transformation
matrix for every frame:
Code:
    232 int iCalcBones(struct s_models *model) {
    233   if (model == NULL) {
    234     return -1;
    235   }
    236   unsigned int i, j;
    237   matrix4x4_t tmp_mat, trans_mat;
    238   float interpol;
    239   struct s_animations *anim = model->anim;
    240   struct s_anim_frames *currframe, *nextframe;
    241   currframe = &anim->anim_frames[anim->curr_frame];
    242   nextframe = &anim->anim_frames[anim->next_frame];
    243   if (currframe == NULL) {
    244     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no currframe\n");
    245     return 0;
    246   }
    247   if (nextframe == NULL) {
    248     vLogMe("aaaaaaaaaaaaaaaaaaaaaa no nextframe\n");
    249     return 0;
    250   }
    251   interpol = anim->interpol;
    252   i = 0;
    253   vMatrix4x4Identity(&trans_mat);
    254   QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    255   vQuaternionToMatrix(&model->bones[i].quat,&model->bones[i].worldmat);
    256   trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    257   trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    258   trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    259   vMatrix4x4Multiply(&trans_mat,&model->bones[i].worldmat,&model->bones[i].worldmat);
    260
    261   for (i=1; i<anim->numJoints; i++) {
    262     vMatrix4x4Identity(&trans_mat);
    263     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
    264     vQuaternionToMatrix(&model->bones[i].quat,&tmp_mat);
    265     trans_mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
    266     trans_mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
    267     trans_mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
    268     vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat);
    269     vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&tmp_mat,&model->bones[i].worldmat);
    270     //vLogMe("i: %d\tboneid: %d\tparent: %d\t is: %d\n",i,model->bones[i].id,model->bones[i].parent_id,model->bones[i].parent->id);
    271   }
    272   return 0;
    273 }

If you have Linux, i can give you the full source code, at the end
it will be released under the terms of the GPL, when it´s working (i hope
that´s this decade :wink: )

My Matrices are saved in a float[16] that´s row ordered (as in OpenGL):
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]

At first a i had some segmentation faults, but i got them fixed very quick,

The rendered goldfish looks like every thing else at the moment :wink:

Regards

Flo



der_flo@Posted: Fri Sep 24, 2004 8:18 pm :
BTW: Has anyone written a MD5Viewer in C (not C++) and may give me the sourcecode?

Regards

Flo



bozo@Posted: Fri Sep 24, 2004 8:26 pm :
@der_flo

i doesn't know how your matrix func's work, but


* maybe in the mat_mul func ie. vMatrix4x4Multiply(&trans_mat,&tmp_mat,&tmp_mat); you overwrite the data of the tmp_mat and use not the orignal values but the new calced one's to calc the next new values, so the calc goes wrong
ie.
Code:
in1 = trans_mat
in2 = out = tmp_mat
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
...
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +   in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +   in1[1][2] * in2[2][1];
...

i hope it is understandable what i mean


* maybe the order of the matrices for the mat_mul func is wrong, you can try to flip them
ie.
Code:
Matrix4x3_Multiply(rotmat, transmat, out);
Matrix4x3_Multiply(out, parentbone->worldmat, bone->worldmat);



unfortunately i dont have linux, but maybe the source can be compiled/ported to win?



der_flo@Posted: Fri Sep 24, 2004 8:43 pm :
@bozo

i have allready fixed that problem, i make a lokal copy of tha dest matrix,
and after the calculation is ready i copy the temp matrix to the destination.

i have tested this, but i think with matrix multiplication it doesn´t
matter which comes first, by quaternions it matters.

I copyed the source to my webserver, you can download it at:
http://www.dotbox.org/code/anim.zip something about 430 KB

It should be easy to port to windows, it is using the sdl, gl and glu lib,
i think there are no linux specific implementations at the moment.
A linux Makefile is also in the package, also the binary and the object files (which makes it so big).

Regards

Flo



der_ton@Posted: Fri Sep 24, 2004 9:49 pm :
Matrix multiplications are not commutative, so A * B is not necessarily B * A, if that's what you mean.

KPixel's viewer is available in sourcecode, but it's C++. I don't think that's a big deal though, you won't want to copy&paste it anyway, but just take a look and understand how it works?



der_flo@Posted: Fri Sep 24, 2004 10:22 pm :
@der_toni searched for the source code, but it was nowhere available, but then i tryed to start this binary with wine and it extracted the source code, so: thank you for this hint, i will study this code.
But first i will watch Beavis and Butthead (now on MTV-Germany) :D

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 3:39 pm :
Hello,

correct me if i am wrong, but could it be, that the quaternions in the
baseframe are (after a successfull calculation) the same as the on
given in the md5mesh file?

I ask this becouse i just want to make debuging easier for me,
and if this is the case, than i will write a small app, that does nothing
more than calculating this quaternions and if i get the right values,
i can reimplement that in my game eninge.

Regards

Flo



der_ton@Posted: Sat Sep 25, 2004 4:20 pm :
No, the baseframe is not the same pose as the bindpose from the md5mesh. It's the pose of the first frame of the animation.



bozo@Posted: Sat Sep 25, 2004 4:21 pm :
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files

- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);

- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);
     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this



der_flo@Posted: Sat Sep 25, 2004 4:42 pm :
@bozo

First of all thank for your work

bozo wrote:
i have take a first look on your code, after i must change it to be able to compile it in vc

- you have defined variables in the middle of the code, vc doesn't handle this, only on the blockstart in .c files

- your reading code doesn't handle commandline with other than "" in the md5mesh und md5anim files


I will clean up the loader after this is running, the model that i try to render
is a simple goldfish, you can find it in the blender 2 md5 export script from
der_ton and in this model there is no content in this two "", but thank you
for that tip.

bozo wrote:
- in models_load.c:
anim->anim_frames[i].ori = (vec3_t *)malloc(sizeof(vec3_t)*anim->numJoints);
must be anim->anim_frames[i].ori = (vec4_t *)malloc(sizeof(vec4_t)*anim->numJoints);


I have fixed that, also the some lines below that the fscanf instruction
has been fixed, to write the values to the right places.

bozo wrote:
- use the following func, the other implementation does not work with the d3 quat's
Code:
void vQuaternionToMatrix(vec4_t *quat, matrix4x4_t *mat) {
   float xx, xy, xz, xw, yy, yz, yw, zz, zw;
   float x2, y2, z2;
   x2 = quat->x + quat->x;
   y2 = quat->y + quat->y;
   z2 = quat->z + quat->z;

   xx = quat->x * x2;   xy = quat->x * y2;   xz = quat->x * z2;
   yy = quat->y * y2;   yz = quat->y * z2;   zz = quat->z * z2;
   xw = quat->w * x2;   yw = quat->w * y2;   zw = quat->w * z2;

   mat->m[0] = 1.0f - (yy + zz);
   mat->m[1] = xy + zw;
   mat->m[2] = xz - yw;

   mat->m[4] = xy - zw;
   mat->m[5] = 1.0f - (xx + zz);
   mat->m[6] = yz + xw;

   mat->m[8] = xz + yw;
   mat->m[9] = yz - xw;
   mat->m[10] = 1.0f - (xx + yy);

   mat->m[3]  = mat->m[7] = mat->m[11] = mat->m[12] = mat->m[13] = mat->m[14] = 0;
   mat->m[15] = 1;

   return;
}


and use this func also in models_load.c
Code:
vQuaternionToMatrix(&quat, &tmp->bones[i].mat);
tmp->bones[i].mat.m[12]  = tmp->bones[i].pos.x;
tmp->bones[i].mat.m[13]  = tmp->bones[i].pos.y;
tmp->bones[i].mat.m[14]  = tmp->bones[i].pos.z;



done

bozo wrote:
- in iCalcBones(struct s_models *model) handle parentbones
Code:
...
for (i=0; i<anim->numJoints; i++) {
     QuaternionSlerp(&currframe->ori[i],&nextframe->ori[i],interpol,&model->bones[i].quat);
     vQuaternionToMatrix(&model->bones[i].quat,&mat);

     mat.m[12] = currframe->pos[i].x + interpol * (nextframe->pos[i].x - currframe->pos[i].x);
     mat.m[13] = currframe->pos[i].y + interpol * (nextframe->pos[i].y - currframe->pos[i].y);
     mat.m[14] = currframe->pos[i].z + interpol * (nextframe->pos[i].z - currframe->pos[i].z);
     if (model->bones[i].parent != NULL) {
      //vMatrix4x4Multiply(&mat, &model->bones[i].parent->worldmat,&model->bones[i].worldmat);
      vMatrix4x4Multiply(&model->bones[i].parent->worldmat,&mat,&model->bones[i].worldmat);
     }
     else {
        vMatrix4x4Copy(&mat,&model->bones[i].worldmat);
     }
  }
...


- change the baseframe pos and ori to pointers (numJoints) and change the animloadercode for this in models_load.c


Done :)

bozo wrote:
i tested it with the imp model and with this the bindpose works right
but the anims are not ok currently
i will take another look on this


You mean the baseframe or the bones from the md5mesh?

Kind regards

Flo



der_flo@Posted: Sat Sep 25, 2004 4:44 pm :
Hello,

forget my last question about the bindpos, der_ton answered that allready.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 5:10 pm :
- i mean when i copy the md5mesh bones mat's over to the worldmat's

- in models_load.c:
fscanf(fp,"numAnimatedComponents %d\n",&anim->numAnimatedComponents);
the \n was missing

than the next two "fgets(foobar2,200,fp);" are too much, only the third is needed

one of the "fgets(foobar2,200,fp);" before "fscanf(fp,"baseframe {");" is too much

- i had overlooked your commented "vCalcInterpolation(mod,dummy);", so the anim->interpol goes wrong, now uncommented

- now the anim works :) (tested with the imp model) (the fish anim is really strange, no good for testing)



der_flo@Posted: Sat Sep 25, 2004 5:37 pm :
Thanks man, but unfortunately it´s not working
here, can you send me the source code and that imp
model please?

Wow, i can not belief that you get it to working, you rock man :)

Regards

Flo



der_flo@Posted: Sat Sep 25, 2004 6:14 pm :
Hello,

with the help from bozo i got it running, but could try to load
this fish model from der_ton´s blender export script with a viewer
(all viewers i have seen are for windows, and i dont have windows here)
becouse it seems like there is a bug in that md5anim file.

The md5mesh file is ok, the model looks like expected, but even
with the new viewer code (with which the imp model works) that
fish looks kind of strange.

Kind regards

Flo



bozo@Posted: Sat Sep 25, 2004 7:02 pm :
i load that fish.md5mesh and fish.md5anim with der_ton's 3dsmax importer in max and in der_ton's model viewer,
and the anim are strange, so it's the model not the code,
it's not an good sample to test with to build a viewer



der_ton@Posted: Sun Sep 26, 2004 10:24 am :
You have to use a newer CVS snapshot version of blender with that script, the standard 2.34 release has a bug that is a showstopper for the script to work properly.
I wrote about that in the exporter thread, and also a link to the blender forum thread where there's some explanation on that.

So I'm pretty sure that's why the exported animation you are getting is borked up. It works with the Blender build that I'm using.

exporter script's readme.txt wrote:
You need to have the patched version of Blender (standard 2.3.4 will NOT work
correctly because of a bug in the Python API). Get that version here:
http://www.letwory.net/cvsbuilds/bf_ble ... 040810.zip



der_flo@Posted: Sun Sep 26, 2004 11:24 am :
Thanks, i will try to compile blender from cvs and try it again.

Kind Regards

Flo



julienr@Posted: Wed Nov 17, 2004 10:13 pm :
I've written a short tutorial about loading md5meshes. The only problem is that it is in french. (i'll try to translate it to english, but my skills aren't very good). But i've written a demo application and since c++ is international, it could perhaps be helpfull :)
tutorial : http://www.fhtagn.net/index.php?section=md5_tuto1.html
demo : http://www.fhtagn.net/archives/md5_tut1.tar.bz2 (works on Linux, use SDL & SDL_image & Opengl, so should also works on Windows, OSX)

There's currently no model in the demo, since i haven't found any freely redistribuable one...



boomcannon@Posted: Thu Dec 02, 2004 10:23 am :
Hey, I was just wondering if I could get some help. I have a fairly completed MD5 model/anim viewer, but my anim stuff is going all crazy. Sounds like this was everyone's source of a headache for a while. Ok, the problem is while converting the baseframe joints from relative to absolute coords I get slight errors in the resulting calculations. I know this because I am using julienr's code to check against my prog's calculations. The way I am converting to absolute coords is similar to what nohbdy did back on page 2 of this thread. Julienr is doing a pre-order hierarchy tree traversal that equates to the same thing. However, his works and mine gets major distortion in the animated mesh. I did a diff on the numbers that both our programs crunch for that part and thats where I find that after the first 2 joint calculations that my prog is off very slightly. Here's a piece of the diff:

3c4
< joint.pos = (15.9678 -0.0144658 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)
---
> joint.pos = (15.9678 -0.0144647 11.3845) + (1.09556 -0.0404358 -0.00858671) * (0.5 0.5 0.5 0.5)

Look at the y-term of the vector in each (my prog is the top).

And, after this point the error propagates larger and larger. The skinning the bindpos, however, is flawless. So, Im confused. The ordering of the joints before running through the algorithm doesnt seem to matter (cause the parent joints are always before their children in the file).

Well, Ive investigated this problem for a few days now...very frustrating. If anyone has anything to tell me, thank you * 1000. heh



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



boomcannon@Posted: Sat Dec 04, 2004 11:43 pm :
Hey...forget it...heh...I figured it out. It runs like a champ now, and it had nothing to do with the way I was building the absolute skeletons from the relative base skeleton. It was quite enfuriating though because I knew my mathematics were correct and my algorithms were sound.

A word to the next programmer who makes an MD5 loader: Youre probably gonna make it in the sequence of "handle .md5mesh file" first and then "handle .md5anim" file. And, when you do this, you might be tempted to compute the "real" value of the orientation quaternion in your BindMesh() function--because it is the first spot where it will be needed. But, DONT do that as it leads to a very difficult bug to find when your .md5mesh loader works perfectly and then your .md5anim loader (which also computes the "real" of the orientation quaternion) works in a rather peculiar way. The reason as hind-sight would show me is because...hey...youre computing the "real" component in your function that forms the absolute skeleton to be binded to...and...youre destroying the orientation quaternion by computing it again in the BindMesh() function which actually "skins" the skeleton.

Well, thats my contribution to the thread: A fix to my own problem...and warning of that pitfall. Until, of course, I release the source to my viewer (which, as it turns out, despite the slippery bug that just got eradicated, isnt a half-bad piece of software engineering)

Keep Coding...laterz



Sfpiano@Posted: Sun Dec 12, 2004 1:40 am :
Suprise, suprise, I too am having difficulties with the animation. The frames don't even load properly, except for the baseframe; I just get this distorted mesh for the rest of them.

Code:
for all joints
newPos = baseframe.pos;
newQuat = baseframe.quat;

go through the bitflag, update if necessary
build w component of quat

Normalize quat

if( no parent ) {
   joint[i].vPos = newPos;
   joint[i].QuatRot = newQuat;
   Build joint matrix from quat
}
else { // Parent
   Build joint matrix from quat
   joint.mat *= parent.mat
        joint.pos = newPos transformed by builtMatrix
        joint.pos += parent.pos;
}



Dhenry@Posted: Mon Jun 20, 2005 6:51 pm :
Hello, I'm building a MD5 loader too, but I got a problem with normals.

How do you pre-compute vertex normals with vertex weights? (or how do you generate "normal weights" as vertex weights?)



der_ton@Posted: Tue Jun 21, 2005 3:00 pm :
First you have to get the bind-pose model-space normals by calculating them from the model geometry in bind-pose. Then you calculate the weight's normal (which is in bone-space) by invert-transforming the normal by the bone-space matrix. So afterwards when animating, you'll transform the weight normal with the animated bone-space matrix and add them all up and you'll get back your animated vertex normal.



Dhenry@Posted: Tue Jun 21, 2005 6:56 pm :
Thanks !!!

I was sure the algorithm wasn't so complicated, but these multiple vertex weights confused me and I was completely lost... I tried numerous of eccentric and erroneous methods to get them :-)

Here's my own function I use for pre-computing the normals (if it can help others):

Code:
// --------------------------------------------------------------------------
// Md5Mesh::computeWeightNormals
//
// der_ton said:
//
// * First you have to get the bind-pose model-space normals by calculating
//   them from the model geometry in bind-pose.
//
// * Then you calculate the weight's normal (which is in bone-space) by
//   invert-transforming the normal by the bone-space matrix.
//
// * So afterwards when animating, you'll transform the weight normal with
//   the animated bone-space matrix and add them all up and you'll get
//   back your animated vertex normal.
// --------------------------------------------------------------------------

void Md5Mesh::computeWeightNormals( Md5Skeleton &skel )
{
  vector<Vector3f> bindposeVerts( _numVerts );
  vector<Vector3f> bindposeNorms( _numVerts );

  for( unsigned int i = 0; i < _numVerts; ++i ) {
   // Zero out final vertex position and final vertex normal
   bindposeVerts[i] = kZeroVectorf;
   bindposeNorms[i] = kZeroVectorf;

   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     const Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     // Calculate transformed vertex for this weight
     Vector3f wv = pWeight->pos;
     pJoint->orient.rotate( wv );

     bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias;
   }
  }

  // Compute triangle normals
  for( unsigned int i = 0; i < _numTris; ++i ) {
   const Md5Triangle_t *pTri = _tris[i];

   Vector3f triNorm( -ComputeNormal( bindposeVerts[ pTri->index[0] ],
      bindposeVerts[ pTri->index[1] ], bindposeVerts[ pTri->index[2] ] ) );

   for( int j = 0; j < 3; ++j ) {
     bindposeNorms[ pTri->index[j] ] += triNorm;
   }
  }

  // "Average" the surface normals, by normalizing them
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   bindposeNorms[i].normalize();
  }

  //
  // At this stage we have all vertex normals computed
  // for the model geometry in bind-pos
  //

  // Zero out all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm = kZeroVectorf;
  }

  // Compute weight normals by invert-transforming the normal
  // by the bone-space matrix
  for( unsigned int i = 0; i < _numVerts; ++i ) {
   for( int j = 0; j < _verts[i]->countWeight; ++j ) {
     Md5Weight_t *pWeight = _weights[ _verts[i]->startWeight + j ];
     const Md5Joint_t *pJoint = skel.getJoint( pWeight->joint );

     Vector3f wn = bindposeNorms[i];

     // Compute inverse quaternion rotation
     Quaternionf invRot = Inverse( pJoint->orient );
     invRot.rotate( wn );

     pWeight->norm += wn;
   }
  }

  // Normalize all weight normals
  for( unsigned int i = 0; i < _numWeights; ++i ) {
   _weights[i]->norm.normalize();
  }
}


I will post a link to download my MD5 viewer source code when it will be more or less finished.

Image

Image



der_ton@Posted: Tue Jun 21, 2005 9:32 pm :
Dhenry wrote:
I will post a link to download my MD5 viewer source code when it will be more or less finished.

Very cool, thanks!



rich_is_bored@Posted: Tue Jun 21, 2005 10:43 pm :
How likely would it be for someone to collect the information in this thread and write up some file specifications articles for the wiki?

I suppose I could write up an article for MD5Camera.



rich_is_bored@Posted: Wed Jun 22, 2005 3:26 am :
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

Done.



rich_is_bored@Posted: Thu Jun 23, 2005 6:51 am :
Okay, now I've drafted up articles for all three and I have no clue if they are accurate.

http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29
http://wiki.doom3reference.com/wiki/MD5 ... _format%29

I need you guys' help please.

There's a cookie in it for you. Mmmmmm. :lol:



der_ton@Posted: Thu Jun 23, 2005 10:53 am :
I only made some minor edits to the md5mesh article, and completed a few things in the md5anim article. Good job, rich. Keep your cookies. :wink:



reklipz@Posted: Mon Aug 08, 2005 5:59 pm :
Hey guys.

I'm currently in the middle of writing an anim loader for my viewer

Heres my questions:

I am trying to render the baseframe of the animation, just so that i know i get my calcs right

I am a bit confused as to how to calculate the position and the rotation of the joint in the bind pose, this is how I am currently trying to do it:
Code:
if thisJoint has a parent
   build the matrix from this joints parents quat values;
   thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
   thisJoint.quatvalues = thisJoint.quatvalues + thisjointsparent.quatvalues;


something seems to be Very wrong

if someone could tell me the algorithm to calculate the position and quat of the baseframes joints, it would be GREATLY appreciated



Dhenry@Posted: Wed Aug 10, 2005 5:58 pm :
For the bind-pose skeleton, from the .md5mesh files, you don't need to compute any position or rotation for the joints. The skeleton's joints are already transformed.

For the baseframe skeleton, from the .md5anim files, you have to rebuild it by transforming each joint:
Quote:
if thisJoint has a parent {
build the matrix from this joints parents quat values;
thisJoint.pos = thisJoint.pos * built matrix + thisJointsParent.pos;
thisJoint.quatvalues = thisjointsparent.quatvalues * thisJoint.quatvalues;
}

Be careful at the order of the concatenation for quaternions and matrices. Here the * operator for quaternions means “concatenation”, and it is backward the standard quaternion multiplication (Qa x Qb is the concatenation of Qb and Qa). You must concat parent joint's orientation with joint's orientation.



Dhenry@Posted: Wed Aug 10, 2005 6:12 pm :
Ok my own MD5 viewer seems to be quite advanced:
Image

I would like to release the code publicly, but I need at least a model and an animation + texture (with bump and specular) in order to show how to load them (it is not so easy :/). Currently, I use the player.md5mesh and the zfat model and its animations from the Doom3 demo, but I'm not sure I can distribute my demo with them so I will not give a direct link to it for the moment...

Anyway, you can find the code (binaries and samples) on my website by browsing the ftp ... it's in old/models/md5loader-08.tar.bz2 (~8,3 MB)
It is not completely finish, I have to build the OBB from bind-pose skeleton, improve the bump mapping with the height map and correct some little bugs under windows (drag&drop, fullscreen).

You can replace the default.loader file by zfat.loader to try with the fatty zombie (which is animated.. you can change current animation with '<-' and '->'.. I forgot it in the readme :s)

for bump mapping, it uses GLSL, so if you haven't a card that support OpenGL 2.0 (or OpenGL 1.5 with full GLSL 1.0 support), bump mapping will be disabled.



mcamilo@Posted: Fri Nov 25, 2005 5:15 am :
Hello guys,

1. Sorry by my poor english

2. Very thank you all for that thread. It helps me to begin what will be (as I hope) my project for my mastering thesis

3. I have won on make a viewer thanks of information posted on this thread.

But I have many questions, maybe you could help me:

a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

One more time I would like to say sorry by my poor english and my fool questions. Also I would like to thanks your attention.

If any of you could answer my questions or point some site or article I could read I would be very happy.



der_ton@Posted: Fri Nov 25, 2005 5:38 pm :
Welcome to the forum!

mcamilo wrote:
a. I am interested in how the animations are created. The normal path would be create a model and animations in a modeller software (as MAX) and so convert it to md5? Is it correct? There is a modeller specially created for doing md5 models?

Yes they were created in modellers (Maya) and converted to md5. Making a modeller just for md5 wouldn't make sense.


Quote:
b. I would like to know how some issues about IA of the characters are made in DOOM3. For instance how create a finite state machine of states that are controlled by events? How connect each state with a animation?

That's done with scripting. I don't know details about that but there's plenty of info on the forum, and if you are a programmer you will probably see what's going on by looking at the game files.

Quote:
c. How have been done the transitions between animations? I mean suppose two animations that will be played sequencially. If the first animation don´t have a final position (or should I say pose) that is the same of the first position of the second animation? How could I connect this two positions? Could I interpolate this two positions?

Yes I think that's what they do, interpolation.

Quote:
d. How is treated problems like collision? For instance, if a animation result in a collision before it´s finished. What is done? The animation is stopped, or it´s is shifted so as the model in each pose never enter a wall?

I don't know exactly but I've never seen a model's animation being stopped because of collision. My guess is that the engine doesn't care, but maybe it shifts the origin position so that the collision is avoided.

Quote:
e. About the bounds. They serve as a AABB (Axis Aligned Bounding Box)? They are oriented for the baseframe? How could they be rotate or translate so as to always encluse the model in each position?

The bounding box is specified for each frame in a md5anim.



mcamilo@Posted: Sat Nov 26, 2005 3:39 am :
Thank you Der_Ton,

I feel I am begining of get it.

But here I go again:

a. Can you say something more about scripting?

b. Maybe I have not made myself very clear. Suppose two animations where the final pose of the first is not the same of the second. When I play this two animations sequencially there´s a freak movement between this two poses. The idea was to made this transition smoothly. But the problem is with quaternions. I guess that the quaternions of this two poses cannot be interpolated (it would be wrong interpolated them?) because the quaternions of the first is oriented for the first animation and the quaternions of the second is oriented for the seconde animation. If I am correct (I don´t know if I am) there should be quaternions and tranlations to create this transition between this two poses. But If it´s was not created by designer, could it be created on the fly?

c. I agree that it should be the best aproach.

d. Yes, yes very good.

I that´s not the right place to ask that but maybe You can help me with that. Could any one do a description or point some place about HalfLife 2 (or even Half Life1), Unreal, Halo, etc; models just like Bozo have done with MD5?

One more time thank you (specially to Der_Ton) and sorry by my 'irck' english.



der_ton@Posted: Sat Nov 26, 2005 10:32 am :
a: No, like I said I don't know the details, I haven't spent time with that part of the engine. But if you look at the monster script files (in pak000.pk4/script/ai_monster_*.script) you'll quickly see what it's about.

b: Yes, the interpolation can be created on the fly, interpolation doesn't mean interpolation between the last frame of the 1st anim and the 1st frame of the 2nd anim, but it means interpolating over a time interval to shift from one anim to the other.
For example a transition from walk to run can be done by having half a second of transition where you calculate the animated poses from anims each frame, and then interpolate between the two poses with a shifting interpolation value from 0% at 0.0secs (pose is the walk pose) to 100% at 0.5 secs (pose is the run pose). I *think* that D3 does that but I can't prove it with code. I don't know if this part is in the engine or in the SDK.



mcamilo@Posted: Mon Nov 28, 2005 5:11 am :
Hello,

Thanks one more time Der_Ton.

About the scripts I will look up the information where you pointed.

Sorry I think I am still lost about the transitions between two animations. If I understood what You´ve said I should make a interpolation of frames that begins with a more weight to the 1st animation and as time pass this weight will pend to the 2nd animation. Is it correct?

But I am still confused about how doing that relation between the two poses (from the 1st and the 2nd) to each frame of the transition.

Because I can´t see how two poses from diferent animations could be interpolated (linear and spherically) since they have a diferente "format" (I mean a diferent set of orientations and translations) that can not (at least as I see) matched to build interpolant factors.

What should it really means => 20% (pose from 1st) + 80% (pose from 2nd), for instance?

How am I supposed to make it? Would it be that after each pose, from the 1st and the 2nd, was completely calculated (with joints contribution, and interpolation between poses from the same animation and all), I should interpolate linearly each point of that two poses? Is that what you propose? It really makes sense but I will have to try to give it the force of an experice.

(Sorry I know my english are killing you)

Thank for the attention.



Groove@Posted: Fri Dec 02, 2005 1:24 pm :
I am trying to render MD5 with shadow volume but I have some problem. A friend make me a MD5 cube to my test but its model wasn't closed.

He uses the Der_Ton's MD5 exporter, Is it a problem with an exporter option or from the model he done with 3dsmax?

The fellowing picture shadow that there is more one marker for each vertex... so it's a problem to find the model edges need by shadow volume.
Image

Anyway, I have build me own md5 files using notepad:

Code:
MD5Version 10
commandline ""

numJoints 1
numMeshes 1

joints {
   "Bone01"   -1 ( 0.0000000000 0.0000000000 0.0000000000 ) ( 0.0000000000 0.0000000000 1.0000000000 )      //
}

mesh {
   shader "undefined"

   numverts 8
   vert 0 ( 0.0000000000 0.0000000000 ) 0 1
   vert 1 ( 0.0000000000 0.0000000000 ) 1 1
   vert 2 ( 0.0000000000 1.0000000000 ) 2 1
   vert 3 ( 0.0000000000 1.0000000000 ) 3 1
   vert 4 ( 1.0000000000 0.0000000000 ) 4 1
   vert 5 ( 1.0000000000 0.0000000000 ) 5 1
   vert 6 ( 1.0000000000 1.0000000000 ) 6 1
   vert 7 ( 1.0000000000 1.0000000000 ) 7 1

   numtris 12
   tri 0 0 4 6
   tri 1 0 6 2
   tri 2 4 5 7
   tri 3 4 7 6
   tri 4 2 6 7
   tri 5 2 7 3
   tri 6 1 0 2
   tri 7 1 2 3
   tri 8 5 1 3
   tri 9 5 3 7
   tri 10 1 5 4
   tri 11 1 4 0

   numweights 8
   weight 0 0 1.0000000000 ( 0.0000000000 0.0000000000 0.0000000000 )
   weight 1 0 1.0000000000 ( 0.0000000000 0.0000000000 1.0000000000 )
   weight 2 0 1.0000000000 ( 0.0000000000 1.0000000000 0.0000000000 )
   weight 3 0 1.0000000000 ( 0.0000000000 1.0000000000 1.0000000000 )
   weight 4 0 1.0000000000 ( 1.0000000000 0.0000000000 0.0000000000 )
   weight 5 0 1.0000000000 ( 1.0000000000 0.0000000000 1.0000000000 )
   weight 6 0 1.0000000000 ( 1.0000000000 1.0000000000 0.0000000000 )
   weight 7 0 1.0000000000 ( 1.0000000000 1.0000000000 1.0000000000 )
}


And it work, I have generate the shadow volume from the MD5 file:
Image

But even if my cube works, the Doom3 models don't... Maybe, this means that mine is wrong or that Doom3 models aren't closed (This second case is what my program tell). But if the second case is true, how Doom3 does to find edges to render shadow volume?

When I render a MD5 model I see that there is just one marker be vertex so this should means that there is only one "true vertex per vertex" or there is a true vertex for each triangle that uses the vertex and for each of these vertrices markers are the same so I can see how many true vertex there are by vertex.

Image

To create my edges I use an technique by Eric Lengyel describ in "Mathematics for 3D Game Programming and Computer Graphics"

My algorithm is:

Code:
void md5Mesh::_buildEdges()
{
    if(m_EdgeArray != null)
        delete[] m_EdgeArray;
    m_EdgeArray = new md5Edge[m_TrianglesNumber * 3];

    m_EdgesNumber = 0;

    md5Edge* pEdge = m_EdgeArray;

    // Find edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 < i2)
        {
            md5Edge Edge;
            pEdge->VertexIndex[0] = i1;
            pEdge->VertexIndex[1] = i2;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i2 < i3)
        {
            pEdge->VertexIndex[0] = i2;
            pEdge->VertexIndex[1] = i3;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }

        if(i3 < i1)
        {
            pEdge->VertexIndex[0] = i3;
            pEdge->VertexIndex[1] = i1;
            pEdge->TriangleIndex[0] = i;
            pEdge->TriangleIndex[1] = -1;
            pEdge++;
            m_EdgesNumber++;
        }
    }

    // Match triangles to edges
    for(uint i = 0; i < m_TrianglesNumber * 3; i += 3)
    {
        int i1 = m_IndiceArray[i + 0];
        int i2 = m_IndiceArray[i + 1];
        int i3 = m_IndiceArray[i + 2];

        if(i1 > i2)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i2) &&
                   (pEdge->VertexIndex[1] == i1) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i2 > i3)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i3) &&
                   (pEdge->VertexIndex[1] == i2) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }

        if(i3 > i1)
        {

            pEdge = m_EdgeArray;
            for(uint j = 0; j < m_EdgesNumber; ++j)
            {
                if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] == -1))
                {
                    int Tri0 = pEdge->TriangleIndex[0];
                    pEdge->TriangleIndex[1] = i;
                    break;
                }
                else if((pEdge->VertexIndex[0] == i1) &&
                   (pEdge->VertexIndex[1] == i3) &&
                   (pEdge->TriangleIndex[1] != -1))
                {
                    fprintf(stderr, "Triple edge ...\n");
                }

                pEdge++;
            }
        }
    }

   int openEdge = 0;
   for(uint i = 0; i < m_EdgesNumber; i++)
   {
      if(m_EdgeArray[i].TriangleIndex[1] == -1)
         openEdge++;
   }

    fprintf(stderr, "openEdge : %d\n", openEdge);
}


And this Doom3 MD5 allmost of all edges are count in openEdge...

So my question: How to create edges to MD5 files?



mcamilo@Posted: Fri Feb 24, 2006 7:36 am :
Hi,

first I have to say sorry about my poor english.

second I have to say sorry about my newbies questions.

I am studying the md5 model to incorporate it in a framework of games. I am a mastering student and Its my mastering project.

I have succeed in load mesh and anim files and play the animations through this framework

My doubts are about how doom3 handle the transition between two animations?

How they handle the viewing and repositioning of one character. For instance if a character is in opposite of me and it feels me how could he turn to me if there is not an animation of turning movement?

How they handle the transition between two animations. For instance if a character is walking and so suddenly he dies (maybe he was shot). How could I play smoothly the transition of the two movements?

I know maybe you can not answer that questions. But could you point some place to search? How could do to see it in doom3´s code?

It has something with the "rag dolls" working? In time what are rag dolls? It´s a kind of configuration file to handle the .mesh and .anim files? Or what?

What kind of benefit I could earn if worked with rag dolls instead of .mesh and .anim files?



bozo@Posted: Wed Aug 04, 2004 2:37 pm :
let's talk about ...
the new formats of the md5mesh and md5anim files

i have take a first look on it and will post here what i have found out, it is not complete

so, start with some excerpts of two example files, i choose the imp:

imp.md5mesh
Code:
MD5Version 10
commandline "mesh models/monsters/imp/animation/cycles/imp.mb -dest models/md5/monsters/imp/imp.md5mesh -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numJoints 71
numMeshes 1

joints {
   "origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )      //
   "Body"   0 ( -0.0000002384 0 56.5783920288 ) ( -0.5 -0.5 -0.5 )      // origin
   "Hips"   1 ( 3.3494229317 -0.0225959271 62.0168151855 ) ( -0.5 -0.5 -0.5 )      // Body
   ...
}

mesh {
   // meshes: polySurface1
   shader "models/monsters/imp/imp"

   numverts 891
   vert 0 ( 0.8658549786 0.3910109997 ) 1377 2
   ...

   numtris 1346
   tri 0 2 1 0
   ...

   numweights 1401
   weight 0 47 1 ( 1.5918749571 -0.9465401769 4.3310847282 )
   ...
}


idle1.md5anim
Code:
MD5Version 10
commandline "anim models/monsters/imp/animation/cycles/idle1.ma -dest models/md5/monsters/imp/idle1.md5anim -game Doom -prefix IMP1_ -keep Lknee Rknee Lelbow Relbow camera Body -keep Rmissile Lmissile -parent Rmissile Rhand -parent Lmissile Lhand -parent Rwing Chest -parent Lwing Chest -parent Hips Body -parent Waist Body -parent camera Head -prefix IMP2_ -prefix IMP_ -align ALL"

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy {
   "origin"   -1 0 0   //
   "Body"   0 6 0   // origin ( Ty Tz )
   "Hips"   1 3 2   // Body ( Tx Ty )
   "Lupleg"   2 56 4   // Hips ( Qx Qy Qz )
   ...
}

bounds {
   ( -12.7120218277 -30.5424346924 -0.7133038044 ) ( 18.
4121990204 41.8222160339 81.8068695068 )
   ...
}

baseframe {
   ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
   ( 2.4760267735 51.5447387695 -3.0239832401 ) ( 0 0 -0.0269656405 )
   ( 1.0195504427 5.3025918007 3.3494231701 ) ( 0 0 0 )
   ...
}

frame 0 {
    51.5447387695 -3.0239832401
    1.0195504427 5.3025918007
    0.7355086803 -0.3889687657 0.4772257507
    0.0189839788 0.0578746125 -0.5245873928
    -0.8296036124 0.3866359591 0.0100788241
    0.488070637 -0.3712677062 0.7295016646
    -0.0142540894 -0.0333928876 -0.5200206041
    0.8724460602 -0.4404397309 0.1936372519
    1.0421460867 2.7552113533
    -0.1609682292 0.0150111886 0.067597121
    6.2155857086 5.6233057976 0.0092875436 -0.037551783 -0.0034615761
    -5.4427704811 0.0235444643
    6.0652527809
    0.0415914208 0.1587915123 -0.0024342418
    0.1301603615 0.0321234874 -0.2374467552
    0.0672957376 -0.1049735919 0.1197357625
    0.0461013205
    -6.2936215401 -0.2602182925 0.0192391276 -0.4136874974
    0.0191769451 -0.1025890633 0.0379714668
}
...





md5mesh:

Joint:

Code:
"origin"   -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )


first the name of the joint

second the index of the parentjoint, -1 == no parent

the next three numers are the position of the joint x y z

the last three numbers are part of the orientation-quaternion
the md5 stores only the x y z components
there are unit quaternions, so there length are 1.0
Code:
1.0 = x^2 + y^2 + z^2 + w^2

so to calc the w component do
Code:
float term = 1.0f - (x*x) - (y*y) - (z*z);
float qw;
if (term < 0.0f)
   qw = 0.0f;
else
   qw = - (float) sqrt(term);


the position and orientation are also absolute values, no offest to the parent joint


Mesh:

beside some ('s and )'s, it's equal to the old format

quick refresh:
verts store the uv texcoords and the startindex and count of the weights
weights store the jointindex and the weight and x y z pos



md5anim:

numFrames 80
numJoints 71
frameRate 24
numAnimatedComponents 52

hierarchy:

numJoints entries

Code:
"Lrib"   21 59 27   // Chest ( Tx Ty Qx Qy Qz )


first the name of the joint

than the parentjoint index

next the flag that tells whitch components are animated in the frames:

six possible components - translation x y z and orientation x y z

one bit for each component:

bit 0 - 1 => translation x
bit 1 - 2 => translation y
bit 2 - 4 => translation z
bit 3 - 8 => orientation x
bit 4 - 16 => orientation y
bit 5 - 32 => orientation z

ie:
56 => 8+16+32 -> alle three orientation values
6 => 2+4 -> translation y and z

the last number in the hierarchy entry line are the startindex of the animated components in each frame,
the count of animated components are indicated by the preceding flag

bounds:

numFrames entries
min x y z and max x y z
the boundingbox coordinates are relative to the root bone's position ( at the first frame of the animation ?)

baseframe:

position x y z and orientation x y z (part of quaternion, see md5mesh joint description)

i think this are the pos and orientation of the joints as offsets (relative to each parent joint), unlike the absolute values of the md5mesh joints

frame:

numAnimatedComponents entries

my assumption is the the anim.components are also relative values and are used in combination with the baseframe data, but i have not tried it currently, so some tests are necessarily


when you know some more infos or have some corrections please post it :)



der_ton@Posted: Wed Aug 04, 2004 4:22 pm :
Thanks for taking the time to write this up! I was working on the viewer and thought I'd post the info after I'm done with updating the viewer. There'll probably be an update tomorrow.

Quote:
i am not sure about the righthand or lefthand issue, so maybe w must be negate

The w component of the quaternions have to be negated, yep:
Code:
w = - sqrt( 1.0f - ( (x*x) + (y*y) + (z*z) ) )



sic1@Posted: Wed Aug 04, 2004 11:27 pm :
Excellent info. I guess now is as better time than any for me to start learning quaternions :)



BeRSeRKeR@Posted: Thu Aug 05, 2004 12:29 am :
Oh wait, anybody understand quaternions?. Just use them! ;)

Hmm I wish I could have Doom3 to try some MD5 coding!

Greets.



sic1@Posted: Sat Aug 07, 2004 3:07 am :
Hmm, I've managed to read in the joint data, however my problem seems to lie with drawing them (and general understanding). Do the X Y Z coords have to be transformed via the orientation quaternion? I'm curious as to everyone's progress :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 4:34 am :
¿Do you mean how to draw the skeleton just like the console command r_showSkel does?.

If so, I only use the positions from the md5mesh file to create the hierarchy. Here you see the code I use for drawing the skeleton in MAX:

Code:
fn buildSkeleton =
(
   -- For each MD5 bone
   for auxBone in bones do
   (
      local bonePos = auxBone.bindPos
      local parentBonePos = [0, 0, 0]

      -- If the current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Set the current MD5 bone's parent position
         parentBonePos = bones[auxBone.parent].bindPos
      )
      
      -- Create the new MAX bone
      local newBone = BoneSys.createBone parentBonePos bonePos [0, 0, 1]
      newBone.width = 1
      newBone.height = 1
      newBone.name = copy auxBone.name
      
      -- If current MD5 bone has a parent...
      if auxBone.parent > 0 do
      (
         -- Assign current MAX bone's parent
         newBone.parent = MAXBones[auxBone.parent]
      )

      -- Add new MAX bone to array of MAX bones
      append MAXBones newBone
   )

   -- Zoom extends to all objects
   max zoomext sel all
)


But I'm not sure if the result is the correct. Look this image:

Image

That is the MD5 importer I was working on long time ago (it's not finished, though). I was hoping that the "chest" bone would be where the "shoulders" bone is but it seems that's correct. Somebody can confirm that?. Anyway the relationships between bones seems to be correct.

Ah, one question. It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.

BTW, I'm also interested on how the other coders handle this.

Happy coding! ;)



sic1@Posted: Sat Aug 07, 2004 5:51 am :
Ahh, Thanks BeRSeRKeR! The skeleton looks great! I even checked out the bones in game for ya (since I can't go too long without seeing doom)

Image

I was trying to draw the joints out as boxes (like Max's Dummy objects) but I was doing something wrong. It seems there's a bug somewhere in my joint reading code. (You seem to be a good luck charm. ) This is specifically why I am rewriting it, but alas, I haven't learned :)



BeRSeRKeR@Posted: Sat Aug 07, 2004 6:28 am :
Hey, thanks for posting that image. Just what I was looking for!. :)



bozo@Posted: Sat Aug 07, 2004 11:39 am :
@BeRSeRKeR
Quote:
It's possible to draw the skeleton using only the info from the md5anim file or the md5mesh info is necessary?.


with the old fileformats you need the md5mesh file for the parent/child hierarchy infos

but for the other data like position and orientation only the md5anim file,each joint has a pos and orient relative to it's parentjoint,
so you calc the pos and orient for the wished frame for the joint, build a matrix of this values and multiply this matrix with the parent matrix (when available),
with this finalmatrix you can draw the skeleton (use the translationpart of the matrix as the origin of each joint and create the hierarchy)

i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame



BeRSeRKeR@Posted: Sat Aug 07, 2004 2:04 pm :
bozo wrote:
i dont try the new fileformat at present,
but now the hierarchy info is also in the md5anim files,
so it could be that the data, the baseframe + the animated components in the frames, in the md5anim file are sufficiently to calc the pos and orient of the joints for a wished frame

By the end of the next week I could buy Doom3 so maybe then I will give a try to the new format.

Thanks!.



der_ton@Posted: Sat Aug 07, 2004 5:27 pm :
I'm getting a headache from the md5anim format. Some quaternion values seem to toggle their sign, even though the bone doesn't really move. But in my viewer, that sign toggling causes major rotations... anyone has an idea what's going on there?

example:
baseframe: Qx, Qy, Qz: ( -0.802292943 0.5969303846 0.0000000054 )
Qx and Qy are animated components:
frame 14: -0.8022928834 0.5969305038
frame 15: 0.802292943 -0.5969305634



bozo@Posted: Sun Aug 08, 2004 11:13 am :
@der_ton

i didn't try the new md5anim format currently,
but in a other own project, i had troubles that sounds like yours,
here is what i did:

Code:
float cosom = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
if (cosom < 0.0f)
{
   q2[0] = -q2[0];
   q2[1] = -q2[1];
   q2[2] = -q2[2];
   q2[3] = -q2[3];
}



Hell Byte@Posted: Mon Aug 09, 2004 4:20 am :
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

Some questions:

1. Not sure about the orientation quaternions, do i calculate thosed based off of data i already have, or pull them out of the mesh data?

2. Vertex indexes, is there a specific order in which the verts need to be extracted?

3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

4. Im not sure what weights are.. can i get a primer real quick?

5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Im working with the MayaAPI specifically, and hoping to write an importer/exporter. All help is greatly appreciated, so thanks : )



BeRSeRKeR@Posted: Mon Aug 09, 2004 4:48 am :
syphlitix wrote:
So.. to export a mesh properly i would need the following data:

-Joints ( name of joint & joint location & index & index of parent joint & orientation quaternion )
-Mesh Data ( verts & tris & weights )

If you only want the geometry data, yes.

syphlitix wrote:
3. Are the numbers following the vertex coordinates the UV coords? If not, what are they and where do the UV's go?

Example:
Code:
vert 0 ( 0.8658549786 0.3910109997 ) 1377 2

1st param -> vertex index
2nd and 3rd params -> u and v (texture coordinates)
4th param -> start of the vertex weights into the array of weights
5th -> number of weights starting at 4th param (in this case would be weights 1377 and 1378)

syphlitix wrote:
4. Im not sure what weights are.. can i get a primer real quick?

The weights indicate the percentage one vertex is affected by a set of bones, ie. how many influence a bone has over a given vertex. The sum of the vertex weights should be 1.

syphlitix wrote:
5. Once i have all this data, i just need to write it to a file with the same format as all the md5mesh files: joints { ... } mesh { ... }, ect..?

Yeah, otherwise Doom3 won't be able to load your models.

syphlitix wrote:
6. Oh, and i just noticed this.. what is the fourth number in the triangles list?

Example:
Code:
tri 0 2 1 0

1st param -> triangle index
2nd, 3rd and 4th params -> vertex indices (a, b, c)

Greetings.



Hell Byte@Posted: Mon Aug 09, 2004 5:09 am :
Cool, thanks for your reply BeRSeRKeR, this is starting to come together a bit for me.

Just a few more questions though, if you guys dont mind:

I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

Thanks again for all the help, this forum is great!



BeRSeRKeR@Posted: Mon Aug 09, 2004 6:29 am :
syphlitix wrote:
I understand the concept of weights now, but im still not sure how to calculate them. Or should i be able to pull them out of the model/mesh data?

Yes. For example, in MAX you have a set of functions (built in the API) to retrieve weights from the model data. Ok, other modelling packages should support these functions too.

syphlitix wrote:
And how do you calculate the offset of a weight or set of weights into the array of weights for a given vertex, or should i be able to find this data in the model/mesh?

IIRC, same above.

syphlitix wrote:
And still, the queaternion bit, do they need to be calculated based on data i have collected, or should the model/mesh data contain this information alread?

All the bone trasformations at a given frame are accessed using the modelling package API (the same as above).

As you can see, whatever mesh property you want to know (vertex coordinates, texture coordinates, vertex indices, weights, animation keys, materials, etc), could be accessed through modelling package API.



Hell Byte@Posted: Mon Aug 09, 2004 6:32 am :
Awesome, that makes things much easier for me :D

Thanks!



der_ton@Posted: Fri Aug 20, 2004 11:41 pm :
It seems that the boundingbox coordinates in the md5anim are relative to the root bone's position at the first frame of the animation.



Darineth Starwolf@Posted: Mon Aug 23, 2004 12:52 am :
With the assistance of many people on this forum's knowledge that has been posted, I have written an MD5 loader class that uses OpenGL to draw. It was originally designed to load the V6 format, and worked great. I've since added support for the new V10 format using the specs that are, I think, at the top of this thread, but I've encountered some unusual issues. Basically, for a couple models I've tried to load (namely Cacodemon and Cyberdemon), I encounter very strange errors in the mesh. Since a picture speaks a thousand words, here's the cyberdemon and his strange mesh issues:

http://dragonhawk.dyndns.org/avalon/pics/md5-32.jpg

You'll notice the left leg has been squashed, the right foot is messed up, and it's hard to tell from that picture, but the rocket launcher on the right arm is out of place, and looks completely wrong if you rotate the model a bit. I was wondering if anyone else who's been playing with the new format has noticed anything like this? I can't say I honestly understand quaternions, but using some information I've found, I was able to transform the bones' quaternion orientation data into the old bind matrix style used in the V6 format. Anyway, thought I'd try to get a fresh perspective on this, as I've been banging my head on the wall trying to figure this out, given how inconsistant it is (I have no issues loading the Imp's mesh, but the Cacodemon's right half is skewed).



BeRSeRKeR@Posted: Mon Aug 23, 2004 1:34 am :
Take a look at this. I think the answer of bozo could be the solution to your problem.

Greets.