The binary format of the model files contains 3 main sections, the header, the model data and the vertex or raw data.
The header is a only twelve bytes long. It provides us with the offset and size of the raw data, and a 32 bit value of 0. The value of 0 is important since it can be used to tell the difference between an ASCII model file and a binary one. The chances of an ASCII model file starting with 4 bytes of 0 is somewhere between zero and none.
Offset | Type | Description |
---|---|---|
0x0000 | UINT32 | Value of 0 for binary models |
0x0004 | UINT32 | Offset to the raw data/Size of the model data |
0x0008 | UINT32 | Size of the raw data |
0x000C | Total length of the structure |
The model data follows the header in the file. Immediately following the model data is the raw data. Thus, the second value in the header can be either the model size or the offset from the start of the model data to the raw data. Either interpretation works since they are the exact same value.
The structure of the model is reasonably complex. Not only does it contain structures of data, but those structures reference other structures. This is accomplished using arrays and pointers.
There are two types of pointers inside of a model file, model data pointer and raw data pointer. Each of these pointers are stored as a 32 bit value. In the case of model data pointers, the pointer will contain an offset from the start of the model data to the data in question. A value of zero represents a "NULL" pointer or a pointer that doesn't reference anything. For raw data pointers, the pointer will contain an offset from the start of the raw data to the data in question. A value of 0xFFFFFFFF (unsigned) or -1 (signed) represents a "NULL" pointer or a pointer that doesn't reference anything. The reason the raw data uses a value of -1 is that an offset of zero is a valid pointer into the raw data. Excluding one special case, this isn't true for model data pointers. (Only one element in a binary model file points to the data at offset zero in the model data. However, this value is transient and isn't actually stored in the model file.)
Some of you might be wondering why I am referring to these offsets as pointers. After all, in the binary model format, they are always offsets and never actually a real pointer. That is very true for the disk image of a model. However, on 32 bit address processors (or processors such as the Alpha that can run in 32 bit address mode with sign extend), after the model is loaded from disk, all the offsets can be converted to pointers. This improves greatly the run time performance.
Arrays in models are a slightly more complicated beast. They consist of the following three elements.
Offset | Type | Description |
---|---|---|
0x0000 | UINT32 | Pointer/Offset to the first elements of the array |
0x0004 | UINT32 | Number of used entries in the array |
0x0008 | UINT32 | Number of allocated entries in the array |
0x000C | Total length of the structure |
For binary model files, the number of used entries and number of allocated entries will always be the same. During run time or complication time, these value will usually differ since these arrays by nature will grow as more elements are added.
Arrays can contain most anything as long as the elements are all the same type. Examples of common arrays in model files would be arrays of vertices, faces, and even pointers. In the case of pointers, these arrays are commonly used to represent a model hierarchy.
Each node (to be defined later), in a model can contain zero or more controllers. Controllers can be considered as attributes of a node. They defined certain aspects of the node. One of the most common controller is the position controller. This controller dictates the position of the node relative to the parent node.
One of the first obvious questions about controllers is why isn't this information included as part of the standard node structures. There are two reasons for this. The first reason is that controllers are optional. In the case of an emitter node, there are around 50 different controllers that can be specified. Having to dedicate storage space to all of these controllers, used or not, would be a waste of space. The second and more important reason is that controllers can be animated.
To animate a model, the model must change over time. The way to do this is with time keyed controllers. All animations take a given amount of time from start to finish. For example, the swing of a sword might take 1 second of total animation time. In order to draw the model properly, controllers have different that are time keyed. For example, normally, the position controller would be specified as follows.
position 0.2 1.0 0.124
However, in the case of a time keyed position, it would be specified as follows.
positionkey 3 0.0 0.2 1.0 0.124 0.4 0.4 1.0 0.510 0.8 0.6 1.0 0.516
When specifying a time keyed controller, the first value is the start time for that value. Following the start time is the actual value. In this case, during animation time 0.0 through 0.4, the first position will be used. Between 0.4 and 0.8, the second position will be used. Between 0.8 and the animation time for the given animation, the 3rd value will be used.
To provide smoother animations, the values will be interpolated. For example, given our previous data, if the current animation time was 0.2, then that would be half way between the first and second positions. Thus each position will contribute equally to the actual position used. If the animation time was earlier, then the first position would be given more weight. The opposite is true if the animation time was later.
To provide an even smoother animation, a second style of interpolation is available. It is know as "bezier" interpolation. Bezier interpolation is used when you specify "bezierkey" as the suffix to the controller name. (Note: At this time, I have not verified that any Bioware model uses bezier interpolation. I don't even know if the rendering engine supports this option.)
Inside a binary model file, controllers are stored as two arrays in the model data. The first array is an array of a controller structure. The second array is a simple array of floating point numbers. The second array contains the actual controller data while the first tells us about all the controllers in the array. The controller structure is as follows.
Offset | Type | Description |
---|---|---|
0x0000 | INT32 | Type of controller |
0x0004 | INT16 | Number of rows of controller data |
0x0006 | INT16 | Index into the float array of the first time key |
0x0008 | INT16 | Index into the float array of the first controller data value |
0x000A | INT8 | Number of columns excluding the time key column |
0x000B | INT8 | Pad, not used |
0x000C | Total length of the structure |
There are a few important notes about the controller structure. The first thing of note is that for controllers that aren't time keyed, they are still stored as if they are time keyed but with a single row and a time key value of zero. Thus, it is impossible to tell the difference between a controller that isn't time keyed, and a time keyed controller with a single row and a time key value of zero. The second important note is that if the controller is actually bezier keyed, then the value of 0x10 is ORed in with the number of columns. This is how you can tell the difference between a normal keyed controller and a bezier keyed controller. Finally, all key values are stored continuously and all the data values are stored contiguously. Thus, if a keyed controller had 3 rows with the time keys starting at floating point value 5, then the time keys for the other two rows would be value 6 and value 7. Also, it appears that for some models, the controller "detonate" when used as a key controller doesn't even list a time key. Thus, the number of columns listed is -1.
Following is a list of all controllers:
Name | Value | Used in nodes |
---|---|---|
Position | 8 | All |
Orientation | 20 | All |
Scale | 36 | All |
Color | 76 | Light |
Radius | 88 | Light |
ShadowRadius | 96 | Light |
VerticalDisplacement | 100 | Light |
Multiplier | 140 | Light |
AlphaEnd | 80 | Emitter |
AlphaStart | 84 | Emitter |
BirthRate | 88 | Emitter |
Bounce_Co | 92 | Emitter |
ColorEnd | 96 | Emitter |
ColorStart | 108 | Emitter |
CombineTime | 120 | Emitter |
Drag | 124 | Emitter |
FPS | 128 | Emitter |
FrameEnd | 132 | Emitter |
FrameStart | 136 | Emitter |
Grav | 140 | Emitter |
LifeExp | 144 | Emitter |
Mass | 148 | Emitter |
P2P_Bezier2 | 152 | Emitter |
P2P_Bezier3 | 156 | Emitter |
ParticleRot | 160 | Emitter |
RandVel | 164 | Emitter |
SizeStart | 168 | Emitter |
SizeEnd | 172 | Emitter |
SizeStart_Y | 176 | Emitter |
SizeEnd_Y | 180 | Emitter |
Spread | 184 | Emitter |
Threshold | 188 | Emitter |
Velocity | 192 | Emitter |
XSize | 196 | Emitter |
YSize | 200 | Emitter |
BlurLength | 204 | Emitter |
LightningDelay | 208 | Emitter |
LightningRadius | 212 | Emitter |
LightningScale | 216 | Emitter |
Detonate | 228 | Emitter |
AlphaMid | 464 | Emitter |
ColorMid | 468 | Emitter |
PercentStart | 480 | Emitter |
PercentMid | 481 | Emitter |
PercentEnd | 482 | Emitter |
SizeMid | 484 | Emitter |
SizeMid_Y | 488 | Emitter |
SelfIllumColor | 100 | All Meshes |
Alpha | 128 | All Meshes |
All nodes in a model begin with six values which have also been called "tokens". During the early process of decoding the binary models, many people including myself made the mistake that these tokens were used to identify the different types of nodes. As silly as it sounds, this was the best we could do at the time. These six tokens did uniquely identify each of the nodes. However, I and I would imagine most others knew there had to be a deeper meaning to these tokens. Nobody uses 6 4-byte values to uniquely identify a handful of different nodes. All of these tokens were values in the range of 0x00400000 and 0x00500000. There was no apparent bit mask to the values. However, the difference between the token values was interesting since it was usually either 0x10 or 0x20. Now, if someone was really had everything on the ball and had a reasonable knowledge of the Win32 image loader, they would have realized something that eluded everyone, including myself for the longest time. These tokens were not tokens, they were routine addresses. You see, the Win32/NT image loader loads images at 0x0041000. Thus, the funny start to all the numbers.
So, as of now, these "tokens" should be consider unreliable as a method of identifying model node types. What if Bioware releases new models using a new build of their model compiler and these routine addresses change. All the existing software that utilizes these "tokens" would fail.
Luckily, the proper way to identify the node type has been located. Every node contains a 32 bit bit mask that identifies which structures make up the node. Following is a list of all the flags.
Name | Value |
---|---|
HasHeader | 0x00000001 |
HasLight | 0x00000002 |
HasEmitter | 0x00000004 |
HasReference | 0x00000010 |
HasMesh | 0x00000020 |
HasSkin | 0x00000040 |
HasAnim | 0x00000080 |
HasDangly | 0x00000100 |
HasAABB | 0x00000200 |
Every node contains a node header. In the case of a dummy node, only a node header is required. All mesh nodes also contain a mesh structure. In the case of a trimesh node, only the node header and the mesh header is required. By looking at a combination of flags, not only do we know what structures make up the node, but we know what the node type is.
Name | Value |
---|---|
Dummy | 0x00000001 |
Light | 0x00000003 |
Emitter | 0x00000005 |
Reference | 0x00000011 |
TriMesh | 0x00000021 |
SkinMesh | 0x00000061 |
AnimMesh | 0x000000A1 |
DanglyMesh | 0x00000121 |
AABBMesh | 0x00000221 |
As you can see, each of the different node types contains one or more flags.
Part numbers are values assigned to nodes as the model compiler creates them. However, after the complete geometry has been compiled, these values are adjusted.
If a model has a super model, then the geometry for the model is compared against the geometry for the super model. Any node that matches the name of a node in the super model will be given the part number assigned to the node in the super model. If a node in the model isn't found in the super model, then it receives a part number of -1. If a model doesn't have a super model, then the part numbers are left as is.
After the geometry for an animation has been compiled, the same process is used to match the nodes in the animation with the nodes in the main model geometry. It is important to note that the animation geometry is compare against the model's geometry and not the model's super model geometry.
The basic layout of the binary model file is as follows:
File Header | |
Model Data |
Model Geometry Header |
Node 1 | |
Node 2 | |
... | |
Node N | |
Animation Geometry Header 1 | |
Node 1 | |
Node 2 | |
... | |
Node N | |
Animation Geometry Header 2 | |
Node 1 | |
Node 2 | |
... | |
Node N | |
Animation Geometry Header N | |
Raw Data |
Not all models contain animation headers. Also, in many models, there might be no raw data. It is also important to remember that this diagram is a simplification of the real model layout. For example, to fully defined a node, it might take 100 bytes of data or 10,000 bytes of data. This information might be scattered in sections throughout the model data.
All models contain at least one geometry header. This header is part of the larger model geometry header. If the model contains animations, then there will be a geometry header in each of the animation geometry headers.
The format of the geometry header is as follows:
Offset | Type | Description |
---|---|---|
0x0000 | Function Pointer | Pointer to a function |
0x0004 | Function Pointer | Pointer to the function to parse a ASCII model line |
0x0008 | CHAR [64] | Geometry Name (model or animation name) |
0x0048 | Node Header Pointer | Pointer/Offset to the root node of the geometry |
0x004C | UINT32 | Number of nodes in the geometry. In the case of the model geometry, if the model has a super mode defined, then this value also includes the number of nodes in the super model plus one. |
0x0050 | Pointer Array | Array of unknown data (probably runtime only) |
0x005C | Pointer Array | Array of unknown data (probably runtime only) |
0x0068 | UINT32 | Reference count, initialized to 0. When another model references this model, then this value is incremented. When the referencing model dereferences this model the count is decremented. When this count goes to zero, the model can be deleted since it is no longer needed. |
0x006C | UINT8 | Geometry Type 0x01 = Basic geometry header (not in models) 0x02 = Model geometry header 0x05 = Animation geometry header 0x80 = If bit is set, then model is a compiled binary model loaded from disk and converted to absolute addresses. |
0x006D | UINT8 [3] | Padding |
0x0070 | Total length of the structure |
As noted, the two arrays at 0x50 and 0x5C, and the data at 0x68 is unknown at this time.
There is only one model header per model file. This header always starts at offset 0 in the model data section or offset 12 from the start of the file.
Offset | Type | Description |
---|---|---|
0x0000 | Geometry Header | Geometry Header |
0x0070 | UINT8 | Unknown value initialized to 0 |
0x0071 | UINT8 | Unknown value initialized to 1 |
0x0072 | UINT8 | Model classification: 0x01 = Effect 0x02 = Tile 0x04 = Character 0x08 = Door |
0x0073 | UINT8 | If non-zero, model should be fogged |
0x0074 | UINT32 | Unknown value initialized to 0 |
0x0078 | Animation Header Pointer Array | Array of pointers to all the animation geometries |
0x0084 | Pointer to parent model | Pointer to the parent model, always 0 |
0x0088 | FLOAT [3] | Bounding box min for the model, defaults to (-5, -5, -1) |
0x0094 | FLOAT [3] | Bounding box max for the model., defaults to (5, 5, 10) |
0x00A0 | FLOAT | Radius of the model, defaults to 7.0 |
0x00A4 | FLOAT | Animation scale, defaults to 1.0 |
0x00A8 | CHAR [64] | Super model name, defaults to "" |
0x00E8 | Total length of the structure |
There are zero or more animation headers per model, one header for each animation. All animations contain their own geometry information. However, this usually consists of dummy nodes containing controller information.
Offset | Type | Description |
---|---|---|
0x0000 | Geometry Header | Geometry Header |
0x0070 | FLOAT | Animation length, defaults to 1.0 |
0x0074 | FLOAT | Trans time, defaults to 0.25 |
0x0078 | CHAR [64] | Animation root, defaults to "" |
0x00B8 | Animation Event Array | Array of all the events associated with this animation |
0x00C4 | Total length of the structure |
Each animation can have zero or more events. Unlike other arrays that contain pointers to the structures, the animation event array is an array of the actual structure. The structure is as follows:
Offset | Type | Description |
---|---|---|
0x0000 | FLOAT | After |
0x0004 | CHAR [32] | Event name |
0x0024 | Total length of the structure |
Currently, there are nine different nodes types that make up a model's geometry. These nodes specify such elements as lighting, animated graphics emitters, and different types of meshes. Each node type share a common header that supplies us with enough information to tell what type of node it is and information about controllers and children.
Offset | Type | Description |
---|---|---|
0x0000 | Function Pointer | Unknown function |
0x0004 | Function Pointer | Function to parse a line of an ASCII model file |
0x0008 | Function Pointer | Function to perform post node processing |
0x000C | Function Pointer | Unknown function |
0x0010 | Function Pointer | Unknown function |
0x0014 | Function Pointer | Unknown function |
0x0018 | UINT32 | Inherit color flag |
0x001C | UINT32 | Part number/Node number |
0x0020 | CHAR [32] | Node name |
0x0040 | Geometry Pointer | Pointer to the parent geometry, always zero |
0x0044 | Parent Node Pointer | Pointer to the parent node, always zero |
0x0048 | Node Header Pointer Array | Array of pointer to the children nodes |
0x0054 | Controller Key Array | Array of controller key structures |
0x0060 | FLOAT Array | Array of controller data values |
0x006C | UINT32 | Node flags/type |
0x0070 | Total length of the structure |
All nodes that contain mesh information share a common header. The header is as follows:
Offset | Type | Description |
---|---|---|
0x0000 | Node Header | Common node header |
0x0070 | Function Pointer | Function to prepare the mesh information |
0x0074 | Function Pointer | Function to cleanup after mesh has been built |
0x0078 | Face Array | Array of face structures |
0x0084 | FLOAT [3] | Bounding box min, defaults to (0, 0, 0), computed |
0x0090 | FLOAT [3] | Bounding box max, defaults to (0, 0, 0), computed |
0x009C | FLOAT | Mesh radius, defaults to 0, computed |
0x00A0 | FLOAT [3] | Average of all points in the mesh, defaults to (0, 0, 0), computed |
0x00AC | FLOAT [3] | Diffuse color, defaults to (0.8, 0.8, 0.8) |
0x00B8 | FLOAT [3] | Ambient color, defaults to (0.2, 0.2, 0.2) |
0x00C4 | FLOAT [3] | Specular color, defaults to (0, 0, 0) |
0x00D0 | FLOAT | Shininess, defaults to 1 |
0x00D4 | UINT32 | Shadow flag, defaults to 1 |
0x00D8 | UINT32 | Beaming flag, defaults to 0 |
0x00DC | UINT32 | Render flag, defaults to 1 excluding AABB mesh where it defaults to 0 |
0x00E0 | UINT32 | Transparency hint, defaults to 0 excluding AABB mesh where it defaults to 1 |
0x00E4 | UINT32 | Unknown value, defaults to 0. Known values probably 0, 1, 2, and 4. |
0x00E8 | CHAR [64] | Texture0/Bitmap |
0x0128 | CHAR [64] | Texture1 |
0x0168 | CHAR [64] | Texture2 |
0x01A8 | CHAR [64] | Texture3 |
0x01E8 | UINT32 | Tile fade, defaults to 0 |
0x01EC | UINT32 Pointer Array | Vertex Indices, compile only, not stored in binary |
0x01F8 | UINT32 Array | Left over faces, compile only, not stored in binary?? |
0x0204 | UINT32 Array | Vertex Indices count array |
0x0210 | Raw UINT16 Pointer Array | Vertex Indices offset array (The pointers exist in the model data, however, the list of UINT16 that they point to exist in the raw data. |
0x021C | Raw Data Pointer | Unknown, probably used with triangle strips, initialized to -1 |
0x0220 | UINT32 | Unknown, probably used with triangle strips, initialized to 0 |
0x0224 | UINT8 | Triangle mode 0x03 = Triangle 0x04 = Triangle Strips |
0x0225 | UINT8 [3] | Padding |
0x0228 | Pointer | Pointer to a compile only structure, always zero |
0x022C | Raw FLOAT [3] Pointer | Pointer to the vertex data, stored in the raw data region, -1 if not present |
0x0230 | UINT16 | Vertex count |
0x0232 | UINT16 | Texture count, usually 1 |
0x0234 | Raw FLOAT [2] Pointer | Pointer to the texture 0 vertex data, stored in the raw data region, -1 if not present |
0x0238 | Raw FLOAT [2] Pointer | Pointer to the texture 1 vertex data, stored in the raw data region, -1 if not present |
0x023C | Raw FLOAT [2] Pointer | Pointer to the texture 2 vertex data, stored in the raw data region, -1 if not present |
0x0240 | Raw FLOAT [2] Pointer | Pointer to the texture 3 vertex data, stored in the raw data region, -1 if not present |
0x0244 | Raw FLOAT [3] Pointer | Pointer to the vertex normals, stored in the raw data region, -1 if not present |
0x0248 | Raw UINT32 Pointer | Pointer to the vertex RGBA colors, stored in the raw data region, -1 is not present |
0x024C | Raw FLOAT [3] Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x0250 | Raw FLOAT [3] Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x0254 | Raw FLOAT [3] Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x0258 | Raw FLOAT [3] Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x025C | Raw FLOAT [3] Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x0260 | Raw FLOAT? Pointer | Pointer to texture animation data, stored in the raw data region, -1 if not present |
0x0264 | UINT8 | Light mapped flag, defaults to 0 |
0x0265 | UINT8 | Rotate texture flag, defaults to 0 |
0x0266 | UINT16 | Padding |
0x0268 | FLOAT | Vertex normal sum divided by 2, initialized to 0 |
0x026C | UINT32/FLOAT | Unknown, initialized to 0 |
0x0270 | Total length of structure |
The two arrays at 0x0210 and 0x0204 are interrelated. The array at 0x0210 isn't an array of UINT16s, it is an array of pointers to lists of UINT16. The number of UINT16s in each of the lists is specified by the elements of the array at 0x0204. For the second entry in both 0x0210 and 0x0204, the array 0x0204 might tell us the there are 14 vertices in the list while the 0x0210 array gives us the pointer to the list.
All the pointers 0x022C through 0x0248 point to lists of data. The number of elements in these list is specified by the UINT16 at 0x0230 which is the vertex count.
The face structure is as follows:
Offset | Type | Description |
---|---|---|
0x0000 | FLOAT [3] | Plane normal |
0x000C | FLOAT | Plane distance |
0x0010 | INT32 | Surface ID |
0x0014 | INT16 [3] | Adjacent face number or -1 |
0x001A | INT16 [3] | Vertex indices |
0x0020 | Total length of structure |
The dummy node is a default node in a geometry that only contains children nodes and controller information. It has no other data associated with it.
Offset | Type | Description |
---|---|---|
0x0000 | Node Header | Common node header |
0x0070 | Total length of structure |
The light node specifies light sources in the geometry of the model.
Offset | Type | Description |
---|---|---|
0x0000 | Node Header | Common node header |
0x0070 | FLOAT | Flare radius |
0x0074 | UINT32 Array | Array of unknown information |
0x0080 | FLOAT Array | Flare sizes |
0x008C | FLOAT Array | Flare positions |
0x0098 | FLOAT [3] Array | Flare color shifts |
0x00A4 | CHAR Pointer Array | Array of pointers to the flare texture names |
0x00B0 | UINT32 | Light priority, defaults to 5 |
0x00B4 | UINT32 | Ambient only flag, defaults to 0 |
0x00B8 | UINT32 | Dynamic type, defaults to 1 |
0x00BC | UINT32 | Affect dynamic flag, defaults to 1 |
0x00C0 | UINT32 | Shadow flag, defaults to 1 |
0x00C4 | UINT32 | Generate flare flag, defaults to 0 |
0x00C8 | UINT32 | Fading light flag, defaults to 1 |
0x00CC | Total length of structure |
The emitter node specifies dynamic graphical elements that are emitted from the model such as smoke or sparkles.
Offset | Type | Description |
---|---|---|
0x0000 | Node Header | Common node header |
0x0070 | FLOAT | Dead space |
0x0074 | FLOAT | Blast radius, defaults to 0 |
0x0078 | FLOAT | Blast length, defaults to 0 |
0x007C | UINT32 | X grid |
0x0080 | UINT32 | Y grid |
0x0084 | UINT32 | Space type, defaults to 0 |
0x0088 | CHAR [32] | Update |
0x00A8 | CHAR [32] | Render |
0x00C8 | CHAR [32] | Blend |
0x00E8 | CHAR [64] | Texture |
0x0128 | CHAR [16] | Chunk name |
0x0138 | UINT32 | Two sided texture flag, defaults to 0 |
0x013C | UINT32 | Loop flag, defaults to 0 |
0x0140 | UINT16 | Render order, defaults to 0 |
0x0142 | UINT16 | Padding |
0x0144 | UINT32 | Emitter flags 0x0001 = P2P 0x0002 = P2P Sel 0x0004 = Affected by Wind 0x0008 = Is Tinted 0x0010 = Bounce 0x0020 = Random 0x0040 = Inherit 0x0080 = Inherit Vel 0x0100 = Inherit Local 0x0200 = Splat 0x0400 = Inherit Part |
0x0148 | Total length of structure |
The reference node... TBD
Offset | Type | Description |
---|---|---|
0x0000 | Node Header | Common node header |
0x0070 | CHAR [64] | Ref model |
0x00B0 | UINT32 | Reattachable flag |
0x00B4 | Total Length of structure |
The trimesh node provides the basic drawing mesh used to render elements of the game.
Offset | Type | Description |
---|---|---|
0x0000 | Mesh Header | Common mesh header |
0x0270 | Total Length of structure |
The skin mesh node provides a specialized mesh where the texture is stretched and contorted as the model moves to provide a more realistic look to skin. It is mostly used just for things such as dragon wings.
Offset | Type | Description |
---|---|---|
0x0000 | Mesh Header | Common mesh header |
0x0270 | Weight Array | Used during compile time to store weight information |
0x027C | Raw FLOAT [4] Pointer | Compiled weight information for each vertex |
0x0280 | Raw UINT16 [4] Pointer | Reference index for bones |
0x0284 | Raw UINT16 Pointer | Bone reference mapping array |
0x0288 | UINT32 | Number of entries in the mapping array |
0x028C | Quaternion Array | QBone ref inv |
0x0298 | FLOAT [3] Array | TBome ref inv |
0x02A4 | UINT32 Array | Bone constant indices |
0x02B0 | UINT16 [17] | Bone part numbers |
0x02D2 | UINT16 | Spare |
0x02D4 | Total length of structure |
Most all skin data is computed from the weights array.
The animmesh nodes are used for the game's GUI
Offset | Type | Description |
---|---|---|
0x0000 | Mesh Header | Common mesh header |
0x0270 | FLOAT | Sample Period |
0x0274 | FLOAT [3] Array | Animation vertices, not stored in binary |
0x0280 | FLOAT [3] Array | Animation texture vertices, not stored in binary |
0x028C | FLOAT [3] Array | Animation vertex normals, not stored in binary |
0x0298 | FLOAT [3] Pointer | Pointer to the stored animation vertex information |
0x029C | FLOAT [2] Pointer | Pointer to the stored animation texture vertex information |
0x02A0 | UINT32 | Number of vertex sets |
0x02A4 | UINT32 | Number of texture vertex sets |
0x02A8 | Total length of structure |
The stored arrays are much like the other stored vertex arrays. There size is the number of sets times the number of mesh vertices. There is also a difference between how this information is parsed from the ASCII version and stored in the binary. In the ASCII version, each vertex is listed sequentially for each vertex or texture vertex sets. However, in the binary version, each vertex is store with the different sets store sequentially.
The danglymesh node provides a model with the look of movement by allowing faces to move due to momentum even after the whole model has stopped. Thus is much like how a car passenger jerks forward in a car when it stops suddenly.
Offset | Type | Description |
---|---|---|
0x0000 | Mesh Header | Common mesh header |
0x0270 | FLOAT Array | Vertex constraints |
0x027C | FLOAT | Displacement value |
0x0280 | FLOAT | Tightness value |
0x0284 | FLOAT | Period value |
0x0288 | Total length of structure |
Much like the vertex information for the mesh, the constrains are expanded to match the vertex list stored in the model.
The aabb node provides the game with the ability to test for collisions.
Offset | Type | Description |
---|---|---|
0x0000 | Mesh Header | Common mesh header |
0x0270 | AABB Entry Pointer | AABB table root pointer |
0x0274 | Total length of structure |
The following is the layout of the AABB entry structure.
Offset | Type | Description |
---|---|---|
0x0000 | FLOAT [3] | Min bounding box |
0x000C | FLOAT [3] | Max bounding box |
0x0018 | AABB Entry Pointer | Left node |
0x001C | AABB Entry Pointer | Right node |
0x0020 | INT32 | Leaf face part number or -1 if not a leaf |
0x0020 | UINT32 | Most significant plane??? 0x01 = Positive X 0x02 = Positive Y 0x04 = Positive Z 0x08 = Negative X 0x10 = Negative Y 0x20 = Negative Z |
0x0024 | Total length of structure |
I wanted to thank Zaddix and Revinor. Their information about the binary model formats provided an excellent starting point for my work.