Here is a BFM/LWO converter that was just made available.

Features supported:

Creation, deletion and editing of points.

Creation, deletion and editing of polygons.

Editing of bone weight maps. Limit these to 4 per point as per BFM specs. Extras will be ignored.

Recomputes normals, binormals and tangents of all points. Normals are properly averaged with separated polygons when located in the same mesh. Has a smoothing angle of 72 degrees. Normals that are away from each other by more than that are not averaged together and you will see a sharp angle there.

In order to create new points or polygons, each point must have at least one non-zero bone weight and must have a valid uv coordinate.


How to use:

To export to LWO (lightwave object), just set the filenames and click "To LWO". The SKB file must be in the same folder as the BFM file.

To import, you must first load a BFM into memory and then inject the LWO file into it. If you've done the previous step, and the app is still open, you may now edit the LWO file and then click "To BFM" when done.

If you already have a LWO file from a previous export and you've closed the app since, you can simply use a different filename for the LWO file. This step is just to load the BFM back into memory. We don't care about the exported LWO file. We'll delete it, or just leave it. Click "To LWO". Once the BFM file has been loaded, select the LWO file you have been working on and click "To BFM".
br2lwo.zip
File Size: 526 kb
File Type: zip
Download File

 
I found out how the bone hierarchy is listed. The rest of the file is not described as I'm not familiar with it yet.

The header is two 32 bit integers. The first integer, I'm not sure what it represents. But the second integer represents the number of bones that follow.

Each bone descriptor is as follows.

0: (char * 24) bone name
24: (float) unknown. Might not be a float
28: (dword) parent bone index. This is it!!!
32: (dword) Optional sibling bone index. This is -1 if no sibling.
36: (float * 9) Appears to be a 3x3 transform matrix. But the last three floats are actually the first row of the matrix.

The parent bone index can now be used to index into the bone offset 3D coordinates described in the BFM format. These are relative offsets. So now that we have the parent bones, we can build the bone hierarchy.




 
BloodRayne 2 is a hack and slash game that came out around 2005 depending on the platform. While there are many mods out there, there isn't much info on the BFM object file needed to create those mods. After having some fun looking at these files and modifying them, here is what I found out. Example values are for RAYNE.BFM. The data shows the offset followed by the value and the type along with a description.
Update: I've been able to figure out some more details about the points.

BFM File Sections.

1. Header
2. Parts
3. Textures
4. Bones
5. Mesh Descriptors
6. Meshes (Points and Triangles)

1. Header

0: 6 (dword) ?
4: 1 (dword) ?
8: 22 (dword) Number of Parts
12: 95 (dword) Number of Bones
16: 2 (dword) Number of Textures
20: 4 (dword) Number of Attached Parts (guns, weapons)
24: 1 (dword)
28: 0 (dword)
32: (80 bytes) skeleton filename nil terminated

2. Parts

Offset is from start of file.

Each part descriptor is 58 bytes long. There are as many as indicated by offset 8 in the header.

This is followed by Attached Parts. There are as many as indicated by offset 20 in the header.

112: (58 bytes * Number of Parts) Part names. followed by (76 bytes * Number of Attached parts)
Each regular part is as follows.

0: Part name (30 bytes)
30: (dword) Primary bone index associated with this Part (though multiple bones are allowed per part).
34: (float * 6) Bounding box in global space of each part. (x1,y1,z1)-(x2,y2,z2).

Extra parts have the following format

0: Part name (24 bytes)
24: (dword) Primary bone index associated with this Part.
28: (float * 12)  Unknown. Looks like two bounding boxes. Perhaps original is resized.

3. Textures

Don't know too much about this. 

There as many as indicated by offset 16 in the header. Each texture descriptor is 360 bytes long. There are usually three texture names. One for color, bump and gloss. Not sure what the rest of the data is for.

4. Bones

Not exactly sure on all the details.

It starts with bone offsets in 3D (x,y,z). There are as many of these as indicated by offset 12 in the header. These offsets are relative to other bones except for the first offset which is a global offset for everything. However, you don't use it when computing the bounding boxes in the Parts Descriptor.

What follows are 6 floats per bone. Not sure what this data is yet.

This is followed by one 32bit integer for each bone. Probably a bone type since the values are all 1 or 2.

Then this follows one 32bit integer for each bone again. This time, it indicates a child bone. It's rather odd. Many bones have many children. Bone 0 has 7 children. But I can't figure out how this works yet. But for certain, the number indicated here is a child bone. The position of the child bone mentioned above is relative to its parent bone.

5. Mesh Descriptors

The very first 32bit integer indicates how many mesh descriptors that follow. These are variable size.

Each mesh descriptor is as follows:

0: (dword) 3
4: (dword) 0 texture pack
8: (dword) 1
12: (dword) 16 part index
16: (word) 0 Number of extra words to follow (if 1, 1 extra word follows. If 4, 2 extra words follow. Not sure what they are.)
18: (dword) 2
22: (dword) 26068 number of bytes in the point and triangle chunk
26: (dword) 4
30: (dword) 194 number of points in mesh
34: (dword) 206 number of triangles in mesh
38: (dword) 95 Always the same. Perhaps an end marker.

6. Mesh (Points and Triangles)

I thought this part would be simple, but it's quite involved. Luckily, the triangle are dirt easy.

Each mesh follows in the same order as their descriptors. For each mesh, you have points followed immediately by the triangles. Then you have the next mesh and so on until the end of the file.
The number of points and triangles is specified in its respective Mesh Descriptor.

Points. Each point structure can actually contain 4 points. Each of these points represent the exact same point in global space. What these "subpoints" are is defining their positions relative to the bone that affects them. Offset 80 has 4 dwords (one for each subpoint) that indicates the bone index for each of these subpoints.

Points: (128 bytes)
0: (dword) # of subpoints. Max 4 subpoints.
4: (float * 3 * 4) coordinates. Only # of subpoints are valid.
52: (float * 4) This is the weight assigned to each subpoint. Adding all four must equal to 1.0.
68: (float * 3) Normal vector. Is a unit vector.
80: (dword * 4) bone index for each point
96: (float * 2) UV coordinate into texture. Range is 0 to 1.
104: (float * 3) 3D unit vector. Binormal. Points inward to center of model.
116: (float * 3) 3D unit vector. Tangent. Inverted.

The weights are very important. In determining the final point on screen, it will use bones to deform each subpoint. Then, if there are more than 1 subpoint, it will use the weights to use a percentage of each subpoint in figuring out the final point displayed on screen. This is what I've been able to understand as I've succesfully been able to add points and triangles.   

The normal vector is also important for shading. If you're updating points, you will want to take the normal vector of all adjacent triangles, then add each axis together and normalize it. This will give you the normal for this point. It is used in determining which direction light should be reflected.

Triangles:
0: (word * 3 * triangles) just a list of three words indicating the index of the points in each triangle.

That's it. If you can fill in the other details, please leave a comment.