Model and Animation File Formats

Legend of Grimrock 3D models are little endian binary files which contain a hierarchy of nodes. Each node has a transformation and a parent node (except the root node which has no parent). Optionally mesh entities and skeletons may be attached to nodes. Mesh entities are further split into segments. Each segment is a renderable triangle list which refers to a material by its name. Materials themselves are defined outside the model file in Lua script.

The simplest model file contains just a single node with a mesh attached and no skeleton. For example, items in the game are built like this. Monsters and other animated objects are more complex containing skeletons and skinning data.

In addition to model files, complex 3D objects such as monsters need animations. An animation file stores animation data for each bone in a model. However, separate animations such as “walk” and “attack” are stored in separate animation files.

The following basic data types are used in the animation and model files.

// A String is a variable length 8-bit string
struct String
	int32	length;
	byte	data[length];

// A FourCC is a four character code string used for headers
struct FourCC
	byte	data[4];

// A Vec3 is a 3D vector
struct Vec3
	float32	x, y, z;

// A Mat4x3 is a transformation matrix split into 3x3 rotation and 3D rotation parts
struct Mat4x3
	Vec3	baseX;
	Vec3	baseY;
	Vec3	baseZ;
	Vec3	translation;

// A Quaternion represents a rotation in 3D space
struct Quaternion
	float32 x, y, z, w;

Model Files

A model file simply contains a header and a variable number of nodes:

struct ModelFile
	FourCC magic;		// "MDL1"
	int32 version;		// always two
	int32 numNodes;		// number of nodes following
	Node nones[numNodes];

struct Node
	String name;
	Mat4x3 localToParent;
	int32 parent;		// index of the parent node or -1 if no parent
	int32 type;		// -1 = no entity data, 0 = MeshEntity follows
	(MeshEntity)		// not present if type is -1	

The first node is the root node and its parent field must be set to -1. A Node may or may not contain a MeshEntity. Nodes without a mesh entity are used as bones for skeletal animation or as intermediate nodes in transformation chains.

struct MeshEntity
	MeshData meshdata;
	int32 numBones;		// number of bones for skeletal animation
	Bone bones[numBones];
	Vec3 emissiveColor;	// deprecated, should be set to 0,0,0
	byte castShadow;	// 0 = shadow casting off, 1 = shadow casting on

struct Bone
	int32 boneNodeIndex;	// index of the node used to deform the object
	Mat4x3 invRestMatrix;	// transform from model space to bone space

struct MeshData
	FourCC magic;		// "MESH"
	int32 version;		// must be two
	int32 numVertices;	// number of vertices following
	VertexArray vertexArrays[15];
	int32 numIndices;	// number of triangle indices following
	int32 indices[numIndices];
	int32 numSegments;	// number of mesh segments following
	MeshSegment segments[numSegments];
	Vec3 boundCenter;	// center of the bound sphere in model space
	float32 boundRadius;	// radius of the bound sphere in model space
	Vec3 boundMin;		// minimum extents of the bound box in model space
	Vec3 boundMax;		// maximum extents of the bound box in model space

MeshData struct contains the vertices, index lists (also called triangle lists), mesh segments and bounding volumes of a mesh. Historically meshes used to be stored separately from models, so MeshData has its own header and versioning information.

The vertex data is split into vertex arrays. Up to 15 vertex arrays may be used. The vertex array indices are:


Unused vertex arrays should have its dataType, dim and stride fields set to zero. Tangent and binormal vertex arrays are only used for normal mapped models. Position, normal, tangent and bitangent vertex arrays contain three dimensional data and should have its dim field set to 3. Texcoords are two dimensional data and bone indices and weights are four dimensional data. Bone indices and weights should use the byte data type while all other vertex arrays should use the float32 data type. Vertex colors are currently unsupported.

struct VertexArray
	int32 dataType;		// 0 = byte, 1 = int16, 2 = int32, 3 = float32
	int32 dim;		// dimensions of the data type (2-4)
	int32 stride;		// byte offset from vertex to vertex
	byte rawVertexData[numVertices*stride];

Finally MeshSegment defines a contiguous segment of the index list that is rendered with a given material.

struct MeshSegment
	String material;	// name of the material defined in Lua script
	int32 primitiveType;	// always two
	int32 firstIndex;	// starting location in the index list
	int32 count;		// number of triangles

Animation Files

Animation files contain a header and a number of animated items. Each item is bound to a node in a model file at runtime. Animation data is stored as keyframes which are linearly interpolated. Typically animation data is stored at 30 frames per second. Due to nature of the interpolation quaternion data must be preprocessed so that the interpolation follows the shortest arc in quaternion space.

struct AnimationFile
	FourCC magic;		// "ANIM"
	int32 version;		// always one
	String name;		// name of the animation
	float32 framesPerSecond;
	int32 numFrames;	// length of the animation in frames
	int32 numItems;		// number of animation items following
	Item items[numItems];

struct Item
	String node;		// name of the node to be animated
	int32 numKeys;		// number of key frames following
	KeyFrame keys[numKeys];

struct KeyFrame
	Vec3 position;
	Quaternion rotation;
	Vec3 scale;