Hello everyone, I've been silently working on animations for a few days now and I think I've finally made some significant progress. Basically the challenge was twofold: a) Read the SKA format and B) Write a skeletal animation system ;-) Here's a video showing some animations: http://www.youtube.com/watch?v=64W0GCbpYmw http://www.youtube.com/watch?v=mc00ZFYTbcw Basically you can see that some rotations are broken (The right daemon wing is bent in the wrong direction). But I think that can be solved. Cu, Storm
Awesome work. Have you used information from 3ds max exporter source code or models viewer? If so can you please point out what was wrong? If not, can you tell details on transformations stored in SKA file?
Hi, I think i used both source-codes as a reference. I'll write a more detailed description on the format I used this evening (GMT+1). Basicaly I am still not entirely sure what the list of bones in the SKA file is used for. It's possible that they define the standard rotation for all bones not animated in an animation. I'll look into it. Cu, Storm
I am still trying to work out the quirks before I post definitive information... I hope I'll have it done by tomorrow. What i *know* so far is this: The Bones in the SKM file contain the correct world inverse matrix for the corresponding bone. What I am still trying to figure out is, what the bones in the SKA actually do, or mean. Logically they *should* be contain the initial world matrix for each bone. (If the standard pose for the mesh is the same as the pose for the SKM, they should just be the inverse of the world inverse). Cu, Storm
Animation Basics Normally to do skeletal animation, you need a set of bones, that are in a hierarchical relationship (the finger bones are children of the hand bone, the hand bone is a child of the forearm bone, etc.). They each define their own coordinate system and can be rotated, stretched, etc. To animate a character, you would transform the position of a triangle into the space of the bone using the "world inverse" matrix (world matrices convert from the coordinate system of a model to the world coordinate system, while world inverse matrices do the opposite). Then you apply the bone's animated world matrix. This step basically means, that you transform the triangle back into world space, but this time including any transformations that happened to the bone (rotations, etc.). These transformations also include all transformations of the parents. So if you rotate the hand bone, all finger bones (and all triangles animated by that boone) are rotated as well. You can also get the initial world matrix of a bone by inverting the world inverse matrix... SKM File Format The SKM file contains triangles, vertices, materials and bones. For each bone, the parent bone and the world inverse matrix is given. I verified the world inverse matrix by inverting it, transforming the 0,0,0 point by this matrix and drawing the result. What this does is this: It takes the point (0,0,0) in the coordinate system of the bone and transforms it to the coordinate system of the mesh/model. Here is how that looks: SKA File Format What's interesting here is, that the SKA files contain the exact same list of bones as the SKM file, but not with a world inverse matrix. This time, they contain a quaternion, translation and scale vector each. (Quaternions express rotation) In addition to that, they contain animations. These animations are structured like this: - First Frame (Contains the starting point for all the bones affected by this animation) - Key Frames (Intermediate rotations/translations/scales for bones. But only those that have starting values in the first frame). The SKM file format is pretty much done. The World inverse matrix is required for animating the model. The big question is this: What are the rotations/scales/transforms in the bone list for and to what are the transforms from the key frames related? Are they relative to the parent of the bone? Do they contain the entire transformation? Well. I am still working on that ;-) Cu, Storm
Thank you for detailed description. I thought that inverse matrix from skm isuseless and also I didn't understand how frames worked. Btw, is this open project? Can I take a look at source code?
Okay I finally figured it out and got working animations (I think. I have yet to encounter a broken one): http://www.youtube.com/watch?v=eiSxcMWXClk *edit*: The video is a bit choppy because my crappy screen capturing utility can only capture at 10fps. It's fluid on my computer. The animation being played is unarmed_unarmed_idle. *edit2*: Here's a video with visible bones: http://www.youtube.com/watch?v=ZUhk_4D2FBM *edit3*: Video with a more complex animation playing (unarmed_unarmed_rattack): http://www.youtube.com/watch?v=x4OjkLlHFbo *edit4*: Fixed the video What I did: I used the world-inverse matrix from the SKM file, then I simply assumed, that the bones in the SKA files specify a default pose for all animations. I built a world matrix for each bone from the scale, rotation and translation that are specified in the SKA file like this: bone.RelativeWorld = Matrix.Scaling(scale) * Matrix.RotationQuaternion(quaternion) * Matrix.Translation(translation); The values in the SKA bones definition are always relative to the bone's parent. That means it translates from the bone's coordinate space into the coordinate space of the parent... (They have to be "chained" to get the real world matrix). The Key frame data can be interpreted as (full) replacement values for the SKA bone values. This means: To build the world-matrix for a bone in a certain frame, you have two possibilities: a) The bone is part of the animation (present in the first frame of the animation), then simply build the world-matrix as above for the SKA bones, but use rotation,translation and scale of the key frame instead. b) The bone is not part of the animation, then simply use the default SKA bone world matrix as defined above. Cu, Storm
If you want to check out the animations, I updated the download in this post: http://www.co8.org/forum/showthread.php?p=88778#post88778 Basically there will always be a daemon at position 480,480 that you can manipulate. You can also manipulate all static objects (trees, doors, etc.) and add new mobiles via the menu. If you want to animate something, doubleclick on it and in the new window just select the animation and press animate. Loopable animations will loop, others will only play once. Two issues: 1. No blending between animations. This is an issue with my skeletal animation system, not the data files. 2. Loopable animations have the wrong speed (Walking is REALLY slow, like 100x slower than it should be). Those seem to use a different "drive type" and I don't know how that is calculated yet. Plase try it out and leave your comments here! Cu, Storm
Just a short update: I've been working on getting my ogre to walk ;-) Results so far: http://www.youtube.com/watch?v=9ekCgGVCUtM The walk speed was just a wild guess... I'll work on animation blending next to have a smooth progression from turn to walk to turn to stop. And here's some additional information: The DriveType field for animations seems to be defined in the following way: 0: Time (Passing time will progress the animation) 1: Distance (Moving will progress the animation) 2: Rotation (Rotating will progress the animation) Further evidence to support this: Animations with DriveType 1: *_walk, *_run, sneak, bullrush, tumble Animations with DriveType 2: *_lturn, *_rturn, In addition to this, the FrameRate field for DriveType = {1,2} is still unknown. But the "DPS" field which is 0 for DriveType = 0 animations seems to be the "duration" of the animation in either radians for rotation (type 2) or pixels for distance (type 1). Thats how I am animating the character in the video above. Since FrameRate is not zero for DriveType={1,2} animations, it still has some meaning, that I have to figure out yet. Cu, Storm
More Information: The structure of the event-block for animations is this: Code: for (int i = 0; i < anim.eventCount; ++i) { short frameId; char type[48]; char content[128]; } I.e. the walk animation has two events, one for each footstep. The frame-id contains the frame in which the foot hits the ground, type="script" and script="anim_obj.footstep()". Tested this in my engine by playing a windows chime sound when i encountered anim_obj.footstep() and it matched the footsteps of the animation! Cu, Storm
Yet more animation-stuff: The protos.tab column 6 (Which is for Scale) is used to scale the Distance per Second value of the walking animations... This took me two hours to figure out :/ Well anyway. It does make some sense. If the creature is scaled down, it doesn't become faster in a sense... These values are completely independent of the D20 rule-system "speed" (9ft, etc.) and only affect the animation speed. Well anyway. With this info I get closer to near-perfect animation reproduction *edit*: Uploaded a new video: http://www.youtube.com/watch?v=4xx0DfBTixc What's yet missing is blending between animations. Maybe I'll do that before I do other stuff, but since it's just a visual gimmick, I'll probably do something else first. Btw. comitted the code for the current version to SVN for anyone interested. Cu, Storm