Incomplete Notes on Heretic II FlexModel File Format
by Gwynhala (serotonin@earthlink.net)
Last Update: 6 September, 1999


Revision History
----------------
6 September, 1999:
Added Revision History.
Fixed swapped H2CORVUS_RF and H2CORVUS_LF reference points.
Changed H2CORVUS_MAX_JOINTS to H2CORVUS_NUMJOINTS.
Added typedef for skelpos_t and for refinfo_t.
Added information on lengths of directional vectors.
Corrected definition of H2CORVUS_HDJOINT and skeleton section.
Added notes about order of items in the different parts of the skels sections.
Corrected descriptions of scaled and unscaled items in refs and skels sections.
Big thanks to Pat Lipo and the guys at Raven for their hints on refs and skels!


General
-------

All FlexModels seem to be called tris.fm. In general, a FlexModel file has a ".fm" suffix.

The file contains a set of chunks. Each chunk has a header. The header looks like this:

typedef struct
{
	char	ident[32];		//	The identifier of the chunk
	int		version;		//	The version number of the chunk
	int		size;			//	The size of the chunk, not including header
} header_t;

The version and size fields are each 32 bits. The value is stored in the following order:
reading from the beginning of the version field, byte values of 0x01, 0x00, 0x02, and 0x00
would indicate a version number of 0x000201.

Typicaly the following chunks are included in a Heretic II player model, in this order,
with these names and approximate sizes:

header		Some header information (40 bytes)
skin		List of paths to skin texture files (a few kbytes)
st coord	Skin texture coordinates (a few kbytes)
tris		Model triangle information (about 10 kbytes)
frames		Animation frames (about 3 megabytes)
glcmds		Model triangle rendering information (about 20 kbytes)
mesh nodes	Model node information (about 10 kbytes)
skeleton	Model skeleton information (about 150 kbytes)
references	Other stuff (about 350 kbytes)


The Header Chunk
----------------
Version: 2
This contains information about the sizes of the other chunks in the file. Integer values
are stored in the same order as in the header_t

typedef struct
{
	int			skinwidth;		// in pixels
	int			skinheight;		// in pixels
	int			framesize;		// size of each frame (in bytes)
	int			skincnt;		// number of skins
	int			vert3dcnt;		// number of unique vertices in 3D space
	int			vertuvcnt;		// number of unique vertices in texture space
	int			tricnt;			// number of unique triangles
	int			glcmdcnt;		// # 32-bit elements in strip/fan command list
	int			framecnt;		// number of animation frames
	int			meshnodecnt;	// number of mesh nodes
} fmheader_t;


The Skin Chunk
--------------
Version: 1
This contains a list of null-terminated ASCII paths to skin names, relative the the
Heretic 2 "base" directory. Each path is null-terminated in a 64-character field.

The number of skins is given by the skincnt field of the model header.

typedef struct
{
	char	path[64];
} skinpath_t;


The St Coord Chunk
------------------
Version: 1
This contains a list of (u,v) texture coordinates. The "tris" chunk uses indeces
in this list to identify the texture coordinates associated with each vertex of
a model triangle.

The short data type takes up 2 bytes in the file. The value is stored in the
following order: reading from the beginning of the u field, byte values of 0x01,
0x00 would indicate a coordinate of 0x0001.

The number of entries is given by the vertuvcnt field of the model header.

typedef struct
{
	short	u;		//	x-coordinate in texture
	short	v;		//	y-coordinate in texture
} stcoord_t;


The Tris Chunk
--------------
Version: 1
This contains a list of triangle descriptions. For each triangle, indeces are used
to indicate the 3D space coordinates and the texture space coordinates of of each
vertex.

The number of triangles is given by the tricnt field of the model header.

typedef struct 
{
	short	index_3d[3];
	short	index_uv[3];
} triangle_t;


The Frames Chunk
----------------
Version: 1

This is a list of animation frames, each specifying the coordinates and "light normal"
index of every vertex of the model in 3D space.

For each frame there are 40 bytes of header, which specify floating-point scaling and
translation factors for the model and the name of the animation sequence (null-terminated
in a 16 byte field).

typedef struct
{
	float			scale[3];		// multiply byte verts by this
	float			translate[3];	// then add this
	char			name[16];		// frame name
} framehdr_t;

In both the scale and the translate arrays, the 1st element represents front-to-back,
the 2nd element represents right-to-left, and the 3rd element represents up-to-down.

Floating point numbers are stored in 4 bytes, in IEEE format (?). This looks something
like this: if the bytes in the file are a b c d, the floating point number is:
(a + (b<<8) + ((c|0x80)<<16)) * 2^(d-89) (??).
 
The name field indicates which animation the frame belongs to, and also the number
of the frame within the animation. Any digits at the end of the name are a frame
number within an animation; all frames with the same name up to, but not including,
any trailing digits, are part of the same animation, A name with no trailing digits
indicates a one-frame animation. For example. "foo" means the animation has one frame,
but "bar1" and "bar2" are two frames of an animation named "bar".

The header is followed by 3D vertex data structures. The number of verteces is given by
the vert3dcnt field of the model header.

typedef struct
{
	byte	v[3];					//	x, y, z scaled by header info
	byte	lightnormalindex;		//	index of closest vertex normal in canned table
} vertex3d_t;

To obtain actual coordinates in 3D space, the x, y, and z members of v are scaled
by the corresponding members of scale in the frame header, and then the corresponding
values of translate in the frame header are added to each. So, if v[0] = 22, and
scale[0] = 0.5, and translate[0] = 100, the resulting x-coordinate is 22 * 0.5 + 100
or 111.

All coordinates (whether X, Y, or Z) within a frame are scaled to integers in the
range 0..255. The scale and translate values for the frame are calculated based on
the minimum and maximum values of the given coordinate in the frame.

The lightnormalindex field contains a number in the range 0..161, which is an index
into a "canned" table of unit vectors spaced around a unit sphere. The "vertex normal"
of the vertex is calculated by averaging the normals of all triangles that use the
vertex, and then the closest vertex normal is chosen from the table. The table can
be found in the source code for the QDATA program, in anorm.h. Note that there are
162 unique vertex normals; 162 = 2 x 3 x 3 x 3 x 3. The pattern of arrangement of
the vertex normals is unknown.


The GLCmds Chunk
----------------
Version: 1

This is a set of lists of variable-length drawing commands used to render the model.
Each command begins with a 32-bit integer. The sign of the integer determines what
kind of command, while the magnitude of the integer determines how much data follows.

A <positive> integer indicates a TRISTRIP command. Draws a connected group of
triangles. One triangle is defined for each vertex presented after the first two
vertices.  For odd n, vertices n, n+1, and n+2 define triangle n.  For even n,
vertices n+1, n, and n+2 define triangle n.  N-2 triangles are drawn, using N
verteces, where N is the value of the integer. The verteces follow the integer
immediately.

A <negative> integer indicates a TRIFAN command. Draws a connected group of
triangles.  One triangle is defined for each vertex presented after the first two
vertices.  Vertices 1, n+1, and n+2 define triangle n.  N-2 triangles are drawn,
using N verteces, where N is the absolute value of the integer.

Each vertex for either the TRISTRIP or the TRIFAN command is specified using a
floating-point U, a floating-point V, and an integer vertex index, each of which is
32 bits. The vertex index is an index into the list of verteces defined for each frame.

A command of <zero> indicates the end of the list of commands in a group. However, the
total number of 32-bit elements in the GLCMDS chunk is given by the glcmdcnt field of
the model header. More groups follow until the specified total number of GLCMD 32-bit
elements.

NOTE: A list of drawing commands corresponds to a "node" of the model. Heretic II player
models have 16 nodes. The sets of GLCmds for each node can be arranged in any order in the
GLCMDS chunk, because the MESH NODES chunk specifies which set of GLCMDS corresponds to
each node.

NOTE: The same set of GLCmds is used for a given meshnode in every frame. If a mesh node
has variable geometry (that is, an alternate mesh), the same set of GLCmds is used for
both versions of the mesh.

typedef struct
{
	int		theCmd;		//	32-bit integer command
} glcmd_t;

typedef struct
{
	float	u;						//	Texture 's' coordinate (range 0..1)
	float	v;						//	Texture 't' coordinate (range 0..1)
	int		index_3d;				//	Index of 3D vertex coordinate in frame data
} glvert_t;


The Mesh Nodes Chunk
--------------------
Version: 3

This is a set of bitfields indicating which triangles and verteces are members of
a given "node". The start of the GLCmds (offset into the GLCMDS chunk) and the
number of GLCmds for each node are also indicated. There are 16 "nodes" in the
model, stored in the order described above for the GLCMDS chunk.

The number of verteces (and set of indeces of verteces) in a given node MUST be
the same in every frame. If a mesh node has variable geometry (that is, an
alternate mesh), the same set of vertex indeces is used for both versions of the
mesh; only the frame-specific vertex coordinates are changed to make the new
geometry.

NOTE: The node H2MODELNODE_LF_HAND_BOW has variable geometry. In most frames,
this is just a hand grasping a bow, but in frames DRAW1 - DRAW5 an arrow is
nocked in the bow. This is the only known instance of variable geometry in
the Corvus and Kiera models.

Data for each node consists of a 2048-bit bitfield for triangles, followed by a
2048-bit bitfield for verteces, followed by a 16 bit integer indicating the
start of the GLCmds (measured in units of 32 bits), followed by a 16-bit integer
indicating the number of GLCmds (number of 32-bit entities).

Each bitfield is stored as 256 bytes of data. The LS bit of byte zero corresponds
to the zeroth element, the MS bit of byte zero to the seventh element, and so on.
A "1" indicates that the triangle or vertex with the corresponding index is part
of the current node.

NOTE: Heretic II player models have 16 nodes, each corresponding to a specific
subset of model geometry. These nodes are always stored in the following order:

enum
{
	H2MODELNODE_FRONT_TORSO = 0,	//	Front torso, shoulders, pelvis
	H2MODELNODE_BACK_TORSO,			//	Back torso, shoulders, pelvis
	H2MODELNODE_STAFF_AWAY,			//	The thingy on the right hip when the staff is stowed
	H2MODELNODE_BOW_AWAY,			//	The bow, slung over the shoulder
	H2MODELNODE_ARMOR,				//	Armor
	H2MODELNODE_RIGHT_ARM,			//	The right upper and lower arm (no hand)
	H2MODELNODE_RT_HAND_OPEN,		//	The right hand, empty and open
	H2MODELNODE_RT_HAND_STAFF,		//	The right hand, gripping the staff thingy (no staff)
	H2MODELNODE_STAFF,				//	The Durnwood Staff
	H2MODELNODE_HELLSTAFF,			//	The Hellstaff
	H2MODELNODE_LEFT_ARM,			//	The left upper and lower arm (no hand)
	H2MODELNODE_LF_HAND_OPEN,		//	The left hand, empty and open
	H2MODELNODE_LF_HAND_BOW,		//	The left hand gripping the entire bow
	H2MODELNODE_RT_LEG,				//	The right upper leg, lower leg, and foot
	H2MODELNODE_LF_LEG,				//	The left upper leg, lower leg, and bag thingy on the hip
	H2MODELNODE_HEAD				//	The head and hair
};

typedef struct
{
	byte	trimap[ MAX_FM_TRIS >> 3 ];		//	Bit set for each triangle part of this node
	byte	vertmap[ MAX_FM_VERTS >> 3 ];	//	Bit set for each vertex part of this node
	short	glcmdstart;						//	Offset into GLCMDS chunk to start of this node's GLCMDS
	short	glcmdcnt;						//	Number of GLCMDS for this node
} meshnode_t;


The Skeleton Chunk
------------------
Version: 1

This describes skeletal information for the model (information used to bend the model
at the neck, torso, or hips in addition to any bend or rotation caused by the current
animation, and also to mix separate animations of the upper and lower body, e.g. run
while strafing).

The data consists of a bunch of 32-bit integers, followed by a much larger bunch of
32-bit floats.

The integer portion is formatted as follows. The chunk begins with a "skeleton type",
which for Corvus and Kiera models is always 5. This is followed by the number of
"clusters" (spinal joints) in the skeleton, which for Corvus and Kiera is always 3.
This is followed by an integer for each "cluster", telling how many vertex indeces
are associated with that cluster. After all the counts, the vertex indeces for the
first cluster follow, then the vertex indeces for the 2nd cluster, etc. Note that
each successive cluster includes all of the preceding verteces plus its own.

These are essentially all of the verteces that must be updated if the model is bent
at the corresponding spinal joint.

There are three skeletal "bones" that are really used in the Heretic II player model,
and each has an associated cluster of verteces. The order the vertex clusters occur
in the flexmodel file is as follows:
0:  The head, with the joint at the top of the neck
1:  The ribcage and above, with the joint at the middle torso
2:  The whole upper body, with the joint at the upper hips
The list of verteces for each cluster contains only unique verteces - every vertex
at or above the upper hips, including weapons, MUST belong to exactly one of the
clusters.

Following all the cluster vertex info is an integer which has the value 0 or 1. If
zero, this is the end of the SKELETON chunk. If 1, skeletal positioning data for
each frame follows as 9 floating-point values per cluster per frame.

NOTE: Per-frame positioning data is stored in a different order than vertex cluster
data. The positionig data is stored in order whole upper body, ribs and above, head.

Positioning data for each frame is scaled and offset using the scale[] and translate[]
fields of the corresponding frame. These scaled values are stored in the model file.
Note that the coordinates of the skeletal points themselves ARE NOT included in the
scaling calculations for the frame, they are just scaled by the values calculated
using only frame verteces.

The positioning data for each cluster in each frame consists of origin, direction,
and up fields. For skeletal nodes, the origin is the pivot point (joint) that the
cluster of vertices affected by that "bone" use when the angle is changed, and also
the junction point when combining separate upper and lower body animations. The
direction and up fields define perpendicular vectors originating at the origin field
- these indicate the starting orientation of each joint.

For all skeletal nodes, assuming the character is standing upright with arms at its
sides, the direction vector points up along the spine, and the up vector points out
the back.

Note that these are NOT unit vectors; they have a length of 20 units each, possibly
to preserve the accuracy of the angle through frame compression and decompression.

#define	SKELETON_POSITION_VECTOR_LENGTH	20.

typedef struct
{
	vec3_t	origin;			//	Joint location
	vec3_t	direction;		//	Direction vector up through spine
	vec3_t	up;				//	Direction vector out backside
} positioning_t;

typedef struct
{
	int		skeltype;		//	Skeleton type, always 5 for Corvus / Kiera
	int		numjoints;		//	Joint count, always 3 for Corvus / Kiera
	int		jointverts[1];	//	Variable length; # elements = # joints
} skelhdr_t;

enum
{
	H2CORVUS_HDCLUSTER=0,	//	Head cluster (first set of verteces in chunk)
	H2CORVUS_UBCLUSTER,		//	Upper back cluster
	H2CORVUS_LBCLUSTER,		//	Lower back cluster (last set of verteces in chunk)
	H2CORVUS_NUMCLUSTERS
};

enum
{
	H2CORVUS_UBJOINT=0,		//	Upper back JOINT
	H2CORVUS_LBJOINT,		//	Lower back JOINT (last set of verteces in chunk)
	H2CORVUS_HDJOINT,		//	Head JOINT (first set of verteces in chunk)
	H2CORVUS_NUMJOINTS
};

typedef struct
{
	positioning_t	pos[H2CORVUS_NUMJOINTS];
} skelpos_t;


The References Chunk
--------------------
Version: 1

This chunk contains hard-coded, model-specific information on the location of important
points of the model during each animation frame. The information is used to generate
special effects such as weapon trails, shadows, etc.

The following points are important in the Corvus / Kiera models:

enum
{
	H2CORVUS_LH = 0,			//	0: Location of left hand
	H2CORVUS_RH,				//	1: Location of right hand
	H2CORVUS_RF,				//	2: Location of right foot
	H2CORVUS_LF,				//	3: Location of left foot
	H2CORVUS_STAFF,				//	4: Location of the staff
	H2CORVUS_BLADE,				//	5: Location of the blade
	H2CORVUS_HELLHEAD			//	6: Location of the head of the hellstaff
};

The data begins with a 32-bit integer identifying the type of reference information
to follow (always 0 for Corvus / Kiera / player models). Then there's another integer
which specifies ??? and has the value 1.

typedef struct
{
	int		reftype;			//	What type of reference data (0 for Corvus)
	int		idunno;				//	??? (1 for Curvus)
} refheader_t;

The rest of the data is 32-bit floating-point numbers. For each animation frame,
there are 63 floating point numbers. Each set of 63 floating points numbers contains
positioning data (9 numbers) for each of the 7 "important points" described above.

typedef struct
{
	vec3_t	origin;		//	Where the important point is
	vec3_t	direction;	//	Direction of first positioning vector
	vec3_t	up;			//	Direction of second positioning vector
} imp_point_t;


Positioning data for each frame is scaled and offset using the scale[] and translate[]
fields of the corresponding frame. These scaled values are stored in the model file.
Note that the coordinates of the reference points themselves ARE NOT included in the
scaling calculations for the frame, they are just scaled by the values calculated
using only frame verteces.

The positioning data consists of origin, direction, and up fields. For reference
nodes, the origin is the position of the important point (used mainly to position
special effects like shadows). The direction and up fields define perpendicular
vectors originating at the origin field; the orientation of the vectors differs
from body part to body part, as follows:

 - Left hand: direction points out along the finger tips, up points out through the
   BACK of the hand.
 - Right hand: direction points out along the finger tips, up points out through the
   PALM of the hand.
 - Right foot: direction points outward (to the right) through the outer arch, up
   points down through the sole of the foot.
 - Left foot: direction points inward (to the right) through the inner arch, up
   points down through the sole of the foot.
 - Staff: assuming the staff is held in the right hand, hands at side, with the
   blade forward and edge facing down, direction points outward (to the right), up
   points out the back of the blade (straight up)
 - Blade: assuming the staff is held in the right hand, hands at side, with the
   blade forward and edge facing down, direction points outward (to the right), up
   points out the back of the blade (straight up)
 - Hellhead: assuming the hellstaff is held in the right hand, hands at side, with
   its head upright and its chin facing down, direction points outward (to the right),
   up points up through the top of the head (straight up)

Note that these are NOT unit vectors; they have a length of 20 units each, possibly
to preserve the accuracy of the angle through frame compression and decompression.

#define	REFERENCE_POSITION_VECTOR_LENGTH	20.

typedef struct
{
	imp_point_t	refpoints[H2CORVUS_NUMREFPOINTS];
} refskel_t;

Note that these points are not used to determine the location of "hits" during
combat. Instead, a table of blade positions during various attack animations
is hard-coded into game source file g_weapon.c.

The locations of the reference points do not correspond to any vertex on
the model, but (we hope) they occur at a constant radius from some "joint"
in the motion capture skeleton.
