ZenGin Animations¶
Quick Infos
Type: Asset Format
Format Name: Model Animation
File Extension: .MAN
Class Name: zCModelAni
Encoding: Binary, Chunked
ZenGin animation files contain skeletal animations for rigged meshes. Each file contains the data for one animation, including metadata and animation samples.
Animations are found within the Anims.vdf
file found in the Data/
directory in the Gothic installation.
Format Description¶
Model animations are stored in a chunked binary file which contains the following chunks. Also refer to the Datatype Reference for general information about often used datatypes.
The marker chunks marks the beginning of a model animation file.
struct zCModelAni_Source {
zDATE date; // Broken. See below.
string sourcePath;
string sourceScript;
};
This source chunks was originally written to the file using a function called
zCFileBIN::WriteBinSrcFileStats
. It has a bug which causes it to use an uninitialized
zDATE
and write it to the output file. This is why the values in the date don't make
any sense. Tread with care.
struct zCModelAni_Samples {
uint checksum;
uint nodeIndices[/* zCModelAni_Header.numNodes */];
struct zTMdl_AniSample {
ushort rotation[3];
ushort position[3];
} samples[/* zCModelAni_Header.numNodes * zCModelAni_Header.numFrames */];
};
The zCModelAni.checksum
field is used to match compatible the animation to a specific model. It is the same for
assets belonging to the same model. Linked assets are zCModelAni.checksum
, zCModelMeshLib.checksum
and
zCModelHierarchy.checksum
.
It is important to understand that the animation samples do not contain the actual values for the rotation and position. To save on memory and disk space consumption, the values are packed into shorts which are converted back to floats on demand. See Sample Positions and Sample Rotations for more information.
Sample Positions¶
The positions are represented as multiples of zCModelAni_Header.samplePositionScale
. The formula for packing a
given zVEC3
position into the stored format can be summarized like this:
where zCModelAni::AddTrafoMatrix
and zTMdl_AniSample::PackTrans
.
Alongside these packed positions, both zCModelAni_Header.samplePositionMin
and zCModelAni_Header.samplePositionScale
respectively. Thus, to convert
the stored position back to its floating point representation, the following calculation may be applied:
where zTMdl_AniSample.position[0]
, zTMdl_AniSample.position[1]
and
zTMdl_AniSample.position[2]
respectively. This operation is performed by zTMdl_AniSample::UnpackTrans
.
Sample Rotations¶
The rotations are represented as a packed quaternion which is calculated in the following way:
where
and
This operation packs a quaternion of the form zCModelAni::AddTrafoMatrix
and
zTMdl_AniSample::PackQuat
.
They can be unpacked using some quaternion wizardry as described below.
where
and
The values zTMdl_AniSample.rotation[0]
,
zTMdl_AniSample.rotation[1]
and zTMdl_AniSample.rotation[2]
respectively. This is possible since the quaternion
zTMdl_AniSample::UnpackQuat
.
(todo) How animated models are loaded¶
Animated models are loaded by using their associated model script file. While parsing the model script, the parser
will encounter a meshAndTree
directive (in MSB files a chunk of type 0xf300
) which points to a model hierarchy.
This model hierarchy is then loaded and acts as the ground truth of the model. If a hierarchy can't be loaded for any reason, the engine tries to load a model mesh instead.
The model script parser continues and eventually hits the aniEnum
section. While loading it, it will come across
ani
sections. Those sections contain a model and animation name which are then loaded as animations and model meshes
respectively.
Linking these parts together is a checksum calculates from the list of hierarchy nodes. It is just the CRC32 checksum
of all node names appended after each other in the order they are saved in. I.e. a model hierarchy with two nodes
"BIP01"
and "BIP01 LEVER STICK"
(in that order) will have a checksum of ea809bf7
(which
is crc32("BIP01BIP01 LEVER STICK")
.