I'm starting to get the hang of this format. I've been able to figure out a little more about it, and to my suprise, it actually does more than I thought. Not only are the bones and their corresponding animations in this file, but so is the mesh data, normals, and UV maps!

First there's a 12 byte header.

dwordUnknown Size
dwordSkeleton Size
dwordDataTable Size
Unknown Size is always 0. I don't know what it contains, since I've never found a file that had it present. Skeleton Size is the size of the skeleton block. DataTable Size is the size of the datatable, which is referenced from the skeleton block.

First let's detail some of the conventions used in this file format. All offsets are relative to the start of the block. For example, if you're referencing something in the skeleton block, you'll need to add the offset of the skeleton block in order to locate it's exact location in the file.
Quick example: You find an offset that references the skeleton block, the offset is 0x3044. Since Unknown block never exists, the start of the skeleton block is 0xc (right after the header). So the exact file location of the offset is 0x3044+0xc or 0x3050.

Another convention is the heavy use of strange TAGs. A typical tag looks like this: 0x40bbc0, or 0x40bbd0. I have no idea what these tags represent.

The final convention is the fact that all strings in this file are fixed width. These strings aren't zeroed out before writing, so there might be junk after the null character.

Now let's look at the skeleton block. The skeleton starts off with a header that looks like this:

Skeleton Header
dwordTAG (0x40bbc0)
dwordTAG (0x40bbd0)
char[64]Skeleton Name
dwordBody Offset
dword[11]Body Args
dwordAnimation Offset
dword[11]Animation Args
char[64]Skeleton Desc

Ok. Body Offset points to the main Body in the SkeletonTable. Body Args are unknown. Animation Offset points to the animations that are applied to the Body. The first dword in the Animation Args is the number of Animations present. Let's look at the Body first.

It all starts with a single bone, which references all the other bones in the object.

dwordBone Number
char[40]Body Name
char[40]Texture Name
Whew! Anyways, I'm not sure why there are duplicate counts for all of those.. there just are. They're always the same too.
ChildrenOffset points to a list of child bone offsets.
PosFlagsOffset points to a list of PositionFlags (see below)
PosDataOffset points to Positional Data. This data is defined by the PositionFlags entries.
If Flags&0x20, then the second half of this block is present. Otherwise this block ends right after the Flags.
PolyTexOffset points to a polygon texture object, I think. I'm not sure what this data represents yet, but there are 4 floats and 8 words in each "PolyTex".
ObjCountOffset points to a list of Polygon Counts. Each one represents the number of Polygons in a Group.
PolyGroupOffset points to a list of PolygonGroup Offsets (which point to a bunch of polygon definitions... actually triangles. 3 words per polygon, each represents a vertex).
MeshOffset points to a list of vertices. XYZ baby.
NumPoints&0xffff is the number of vertices at MeshOffset. if NumPoints&0xf0000, then a texturemap is present.
If there is a texturemap present, then textureOffset points to the UV points, otherwise textureOffset is -1.
They claim you can have more than 1 texture per object, so I bet NumPoints&0xffff0000 is actually the # of textures. I've only ever seen "1", so I cannot say for sure
NormalsOffset points to a list of vertex normals.
NegOneOffset points to a bunch of -1's... I believe it's real use is to determine the lenght of the Normals data (although there should be one normal per vertex)

wordPositon Type (0x8 = translation, 0x14 = rotation)
wordNum Changes
There we go. Now that we know this, we can look at the PositionData. This data is structured after these flags. The first part of the data is the timeline. There are NumChanges floats that represent the timeline, from 0 to 1. Following this is the actual positioning data. If the type is 0x8, then each block is 3 floats, if the type is 0x14 then the block is 4 floats. There are NumChanges blocks, corresponding to each time on the timeline.

For example, if there are 2 PositionFlags, the first being type 0x8, and NumChanges=2, and the second being type 0x14, and NumChanges=4, then the Position Data would look like this:
2 floats for the translation timeline,
2 sets of 3 floats (6 floats total) for the XYZ data,
4 floats for the rotational timeline,
4 sets of 4 floats (16 floats total) for the xyzw quaterion data.

Now, lets take a look at the animation data (noo!!) First things first. The animation table starts out with a table of offsets. There is an offset for each animation. Each offset points to a full animation tree. Let's look at that structure now.

char[64]Animation Name
char[40]Root Bone Name
Not much to say here. The FirstBoneOffset points to a Bone object just like the one mentioned above. Only this time the data inside the Bone is different, and no mesh data is present (at least not in any of the files I've seen).

That's really all there is to it.

Using what I've found so far, I've written a quick little utility that'll output a skeleton hierarchy. Get it here. You'll need to extract the file from the BIF to use it.
It also demonstrates how to read in each section of data.
once I figure out a few more things, I'll write a 3DS/LWO converter.

Gimp LogoSourceForge Logo