AFF - Animated File Format
by
Jonas Lext, Ulf Assarsson, and Tomas Möller
Department of Computer Engineering, Chalmers University of Technology, Sweden.

History:
Draft #1, 05/15/00
Mesh primitive description added, 06/26/00

The AFF (Animated File Format) is used for BART (A Benchmark for Animated Ray Tracing). AFF is an extension of NFF (Neutral File Format), version 3.9 by Eric Haines. It was designed to be extremely simple to parse and to get into your renderer, and is by no means a flexible and extensive format. Yet, you can do quite alot with it (see for example the MPEGs on the BART-page).

AFF is a text based uncompressed ASCII format. The purpose is to extend NFF with entities suitable for the description of animated scenes. AFF includes all NFF version 3.9 commands, which are:

and also extends the syntax with following new commands:

All commands are explained in detail below.


Comment
Description:

"#" [ string ]
or
"%" [ string ]

Format:

# [ string ]
or
% [ string ]

As soon as a "#" (or "%") character is detected, the rest of the line is considered as a comment.


Viewpoint
Description
:

"v"
"from" Fx Fy Fz
"at" Ax Ay Az
"up" Ux Uy Uz
"angle" angle
"hither" hither
"resolution" xres yres

Format:

v
from %g %g %g
at %g %g %g
up %g %g %g
angle %g
hither %g
resolution %d %d

The parameters are:
From: the eye location in XYZ.
At: a position to be at the center of the image, in XYZ world coordinates. A.k.a. "lookat".
Up: a vector defining which direction is up, as an XYZ vector.
Angle: in degrees, defined as from the center of top pixel row to bottom pixel row and left column to right column. In AFF, if the width is different from the height, then we interpret the Angle as the FOV in the y-direction (from top to bottom), and we set the aspect ratio to width/height. In NFF, the aspect ratio is always 1.0.
Resolution: in pixels, in x and in y.
Note that no assumptions are made about normalizing the data (e.g. the from-at distance does not have to be 1). Also, vectors are not required to be perpendicular to each other. For all databases some viewing parameters are always the same: Yon is "at infinity." A view entity must be defined before any objects are defined (this requirement is so that NFF/AFF files can be used by hidden surface machines).


Positional light. A light is defined by XYZ position and an optional color. For animated lights (currently not used in BART) we need to be able to identify light sources. We do this by giving them a name.

Description:

"l" X Y Z [R G B]
"la" name X Y Z [R G B] #animated light

Format:

l %g %g %g [%g %g %g]
la %s %g %g %g [%g %g %g]

All light entities must be defined before any objects are defined (this requirement is so that NFF/AFF files can be used by hidden surface machines). Lights have a non-zero intensity of no particular value [this definition may change soon, with the addition of an intensity and/or color]. The name of an animated light must not contain any white spaces.


Background color. A color is simply RGB with values between 0 and 1

Description:

"b" R G B

Format:

b %g %g %g

If no background color is set, assume RGB = {0,0,0}.


Fill color and shading parameters.

Description:

"f" red green blue Kd Ks Shine T index_of_refraction
"fm" amb_r amb_g amb_b diff_r diff_g diff_b spec_r spec_g spec_b Shine T index_of_refraction

Format:

f %g %g %g %g %g %g %g %g
fm %g %g %g %g %g %g %g %g %g %g %g %g

RGB is in terms of 0.0 to 1.0. Kd is the diffuse component, Ks the specular, Shine is the Phong cosine power for highlights, T is transmittance (fraction of light passed per unit). Usually, 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required that Kd + Ks == 1. Note that transmitting objects ( T > 0 ) are considered to have two sides for algorithms that need these (normally objects have one side). The "fm" (fill material) version (not part of NFF) is a simple extension of the material description: it involves RGB for the ambient, the diffuse, and the specular component (instead of RGB, Ks, Ld) plus Shine, T, and index_of_refraction. The fill color is used to color the objects following it until a new color is assigned.


Cylinder or cone. A cylinder is defined as having a radius and an axis defined by two points, which also define the top and bottom edge of the cylinder. A cone is defined similarly, the difference being that the apex and base radii are different. The apex radius is defined as being smaller than the base radius. Note that the surface exists without endcaps.

Description:

"c" base.x base.y base.z base_radius apex.x apex.y apex.z apex_radius

Format:

c %g %g %g %g %g %g %g %g

A negative value for both radii means that only the inside of the object is visible (objects are normally considered one sided, with the outside visible). Note that the base and apex cannot be coincident for a cylinder or cone.


Sphere. A sphere is defined by a radius and center position.

Description:

"s" center.x center.y center.z radius

Format:

s %g %g %g %g

If the radius is negative, then only the sphere's inside is visible (objects are normally considered one sided, with the outside visible).


Polygon. A polygon is defined by a set of vertices. With these databases, a polygon is defined to have all points coplanar. A polygon has only one side, with the order of the vertices being counterclockwise as you face the polygon (right-handed coordinate system). The first two edges must form a non-zero convex angle, so that the normal and side visibility can be determined.

Description:

"p" total_vertices
vert1.x vert1.y vert1.z
[etc. for total_vertices vertices]

Format:

p %d
[ %g %g %g ] <-- for total_vertices vertices


Polygonal patch. A patch is defined by a set of vertices and their normals. With these databases, a patch is defined to have all points coplanar. A patch has only one side, with the order of the vertices being counterclockwise as you face the patch (right-handed coordinate system). The first two edges must form a non-zero convex angle, so that the normal and side visibility can be determined.

Description:

"pp" total_vertices
vert1.x vert1.y vert1.z norm1.x norm1.y norm1.z
[etc. for total_vertices vertices]

Format:

pp %d
[ %g %g %g %g %g %g ] <-- for total_vertices vertices


Include of another file (typically containing geometry, but can be any aff-file)

Description:

"i" detail_level filename

Format:

i %d %s

The file name may not include any white spaces.


Detail level. Used together with the ‘i’ command to decide whether or not to include another file

Description:

"d" detail_level

Format:

d %d

The detail level (DL) number is used to exclude objects from the scene so that different a scene can have different complexities (number of primitives in them). The include command (i) is the only one that have a detail number. If the detail level of an include command is less or equal to DL then that object is included, else we skip it. Is 0 (zero) by default.

Example:

d 3
...
i 4 filename

The file "filename" will not be included.


Textured triangle. A triangle with texture coordinates at each vertex. Can also be a textured triangle patch (with normals at each vertex).

Description:

"tt" texturename
vert0.x vert0.y vert0.z texcoord0.u texcoord0.v
vert1.x vert1.y vert1.z texcoord1.u texcoord1.v
vert2.x vert2.y vert2.z texcoord2.u texcoord2.v

"ttp" texturename
vert0.x vert0.y vert0.z norm0.x norm0.y norm0.z texcoord0.u texcoord0.v
vert1.x vert1.y vert1.z norm1.x norm1.y norm1.z texcoord1.u texcoord1.v
vert2.x vert2.y vert2.z norm2.x norm2.y norm2.z texcoord2.u texcoord2.v

Format:

tt %s
%g %g %g %g %g
%g %g %g %g %g
%g %g %g %g %g

ttp %s
%g %g %g %g %g %g %g %g
%g %g %g %g %g %g %g %g
%g %g %g %g %g %g %g %g

The texture name may not include any white spaces. Note that the texturing works like OpenGL REPEAT mode.


Animated triangle. An animated triangle patch

Description:

tpa num_times
time0
vert0_time0.x vert0_time0.y vert0_time0.z norm0_time0.x norm0_time0.y norm0_time0.y
vert1_time0.x vert1_time0.y vert1_time0.z norm1_time0.x norm1_time0.y norm1_time0.y
vert2_time0.x vert2_time0.y vert2_time0.z norm2_time0.x norm2_time0.y norm2_time0.y

time1
vert0_time1.x vert0_time1.y vert0_time1.z norm0_time1.x norm0_time1.y norm0_time1.y
vert1_time1.x vert1_time1.y vert1_time1.z norm1_time1.x norm1_time1.y norm1_time1.y
vert2_time1.x vert2_time1.y vert2_time1.z norm2_time1.x norm2_time1.y norm2_time1.y

etc...

Format:

tpa %d
%g
%g %g %g %g %g %g
%g %g %g %g %g %g
%g %g %g %g %g %g

%g
%g %g %g %g %g %g
%g %g %g %g %g %g
%g %g %g %g %g %g

etc...

Definition: this animated triangle patch depends on the time;

  1. If time<time0 then use the vertices and normals from time0, i.e., use the first triangle patch in the list
  2. If time>time_{num_times-1} then use the vertices and normals from time_{num_times-1}, i.e., the last triangle patch in the list.
  3. Otherwise find two subsequent triangle patches with times time_a and time_b, such that time_a <= time <= time_b. Then interpolate linearly between these two triangle patches to find the animated triangle patch.

Keyframes.

Description:

"k" xform_name
{
type num_keyframes
keyframe_data
}

Format:

The xform_name identifies the xform which is to be animated. For each type of keyframes (transl, rot, scale) there must be at least 4 keyframes, and the first and the last keyframes are only used internally to get starting tangents and similar stuff. There is also a fourth type of keyframes (visibility), which determines whether an object is visible or not at a certain time. This one needs at least one keyframe. Each of the types may appear once in a keyframe description, i.e., you can have a translation, scaling, rotation, and a visibility in the keyframe description (but not, say, two rotations). It is always the case that the total transform is T*R*S, where T=translation, R=rotation, and S=scaling. Is there is no, say, rotation, then R=I (identity matrix). This holds for the other as well. Note the order: the scaling is applied first, then rotation, the translation.

Translation example:

k the_ball
{
transl 5
-0.50 0 -3.0 0 0 0 0
0.00 0 0.0 0 0 0 0
0.50 0 1.0 0 0 0 0
1.00 0 0.0 0 0 0 0
1.50 0 -3.0 0 0 0 0
}

In this example, we can only use times from 0.00 to 1.00. Each row looks like this: time x y z tension continuity bias, where (x,y,z) is the translation at time, and tension, continuity, and bias, are the constants for interpolation at time.

Rotation example:

k the_ball2
{
rot 4
-0.5 1 0 0 45 0 0 0
0.0 1 0 0 0 0 0 0
0.5 1 0 0 90 0 0 0
1.0 0 1 1 10 0 0 0
}

Each row looks like this: time x y z degrees tension continuity bias, where (x,y,z) is the rotation axis and degrees is the amound which is rotated around the axis at time, and tension, continuity, and bias, are the constants for interpolation at time.

Scaling example:

k the_ball3
{
scale 7
-0.5 1 1 1 0 0 0
0.0 1 1 1 0 0 0
0.5 2 1 1 0 0 0
1.0 1 2 1 0 0 0
1.5 1 1 2 0 0 0
2.0 1 1 1 0 0 0
2.5 1 1 1 0 0 0
}

Each row looks like this: time x y z tension continuity bias, where (x,y,z) is the scaling parameters, and tension, continuity, and bias, are the constants for interpolation at time.

Visibility example:

k the_ball4
{
visibility 2
0.5 0
2.0 1
}

Each row looks like this: time visbility_flag

where visibility_flag is either 0 (invisible) or 1 (visible). From time=-infinity, each object is assumed visible until the first visibility keyframe. At that time (0.5 in the example above) the visibility switches to what is given in that keyframe (0 in the example). At the next keyframe (time=2.0 in the example) the visibility may change again (changes to visible (1) above). The last visibility_flag determines the visibility until time=infinity. Note also that if the name of an animation is "camera", then the viewpoint should be animated after those key frames (only translation and rotation). Light sources can also be animated (only translation).


Transform. (either static or keyframe animated)

Description:

"xs" sx sy sz
rx ry rz angle_deg
tx ty tz
{
here follows objects, materials, new transforms, etc that are to be statically transformed with: T*R*S, i.e., first scaling (sx,sy,sz) then rotation of angle_deg degrees around the axis (rx,ry,rz), and finally translation (tx,ty,tz)
}

or

"x" transform_name
{
here follows objects, materials, new transforms, etc that are animated
}

The actual keyframes must be found later in the file, and these are given with the "k" descriptor. Everything inside the { } is transformed, and are thus in its own coordinate system (in a subtree).


Animation parameters.

Description:

"a" start_time end_time num_frames

Format:

a %g %g %d

start_time indicates the start of the animation, end_time indicates the end of the animation, num_frames is the number of frames in the animation. Note: the step time (from one frame to the next) is then (end_time-start_time)/(num_frames-1).


Global ambient light.

description:

"am" red green blue

Format:

am %g %g %d

There is one global ambient light source in the scene, and it can be set with, e.g., "am 0.5 0.5 0.5". Default value is "am 1.0 1.0 1.0".


Mesh

Note that the normals and the texture coords are optional in the mesh description, and that everything that is optional is inbetween '[' and ']'.

description:

"m" "vertices" num_verts v0.x v0.y v0.z v1.x v1.y v1.z ... [ "normals" num_norms #normals are optional n0.x n0.y n0.z n1.x n1.y n1.z ... ] [ "texturecoords" num_texcoords texturefile.ppm #texture coords are optional tc0.u tc0.v tc1.u tc1.v ... ] "triangles" num_tris tri0_v0 tri0_v1 tri0_v2 [tri0_n0 tri0_n1 tri0_n2] [tri0_tc0 tri0_tc1] tri1_v0 tri1_v1 tri1_v2 [tri0_n0 tri0_n1 tri0_n2] [tri0_tc0 tri0_tc1] ...

Format:

m vertices %d %g %g %g %g %g %g ... [ normals %d %g %g %g %g %g %g ...] [texturecoords %d %s %g %g %g %g %g %g ...] triangles %d %d %d %d [%d %d %d] [%d %d %d] %d %d %d [%d %d %d] [%d %d %d] ...

An example of a mesh with 3 triangles, using 5 different vertices and no normals, and no texture coords is shown below:

m 
  vertices 5
  0.0 0.0 0.0  # this vertex has index 0
  1.0 0.0 0.0  # this vertex has index 1
  2.0 0.0 0.0  # this vertex has index 2
  0.5 1.0 0.0  # this vertex has index 3
  1.5 1.0 0.0  # this vertex has index 4
  triangles 3
  0 1 3    # each triangle has three indices to the vertices
  1 2 4  
  3 1 4 
An example of a mesh with 2 triangles, using 5 different vertices and 2 normals, and no texture coords is shown below:
m 
 vertices 5
 0.0 0.0 0.0
 1.0 0.0 0.0
 2.0 0.0 0.0
 0.5 1.0 0.0
 1.5 1.0 0.0
 normals 2
 0 0 1
 1 0 1
 triangles 2
 0 1 3  0 0 0  # the first three are indices to the verts, the next three to the normals
 1 2 4  1 1 0
An example of a mesh with 1 triangle, using 4 (actually, one is not used) different vertices and no normals, and three texture coords is shown below:
m 
 vertices 4
 0.0 0.0 0.0
 1.0 0.0 0.0
 2.0 0.0 0.0
 0.5 1.0 0.0
 texturecoords 3 cover.ppm
 0.0  1.0
 1.0  1.0
 0.5  0.0
 triangles 1
 0 1 3 0 1 2  # first three vertex indices, then three texture coord indices
An example of a mesh with 1 triangle, using 4 (actually, one is not used) different vertices and no normals, and three texture coords is shown below:
m 
 vertices 6
 0.0  0.0 0.0
 1.0  0.0 0.0
 2.0  0.0 0.0
 0.5  1.0 0.0
 normals 2
 5.0 0.0 1.0
 0.0 0.0 1.0
 texturecoords 3 cover.ppm
 0.0  1.0
 1.0  1.0
 0.5  0.0
 triangles 1
 0 1 3  0 0 1  0 1 2 # vertex indices, normal indices, then texture coords indices


Last update by Tomas Möller on June 26, 2000