Difference between revisions of "Animations explained (ASM code)"

From FSDeveloper Wiki
Jump to: navigation, search
Line 11: Line 11:
 
}}
 
}}
  
This article describes how the Fs2004 style scenery animations work. But not from the point of view of a design program like GMax or FSDS3, but from the ASM source code point of view. So this article is mainly of interest if you are not afraid of the ASM source code and want to understand the structure behind the animations a bit better.
+
This article describes how the FS2004 style scenery animations work. But not from the point of view of a design program like GMax or FSDS3, but from the ASM source code point of view. So this article is mainly of interest if you are not afraid of the ASM source code and want to understand the structure behind the animations a bit better.
  
 
I want to note that this article describes my current knowledge of the animations. So it could be that certain parts are not really explained correctly yet, as I do not fully understand them. Any comments are welcome of course.
 
I want to note that this article describes my current knowledge of the animations. So it could be that certain parts are not really explained correctly yet, as I do not fully understand them. Any comments are welcome of course.

Revision as of 06:48, 22 February 2009

This article describes how the FS2004 style scenery animations work. But not from the point of view of a design program like GMax or FSDS3, but from the ASM source code point of view. So this article is mainly of interest if you are not afraid of the ASM source code and want to understand the structure behind the animations a bit better.

I want to note that this article describes my current knowledge of the animations. So it could be that certain parts are not really explained correctly yet, as I do not fully understand them. Any comments are welcome of course.

Basic animations

In the first part of this article I will explain how a simple animation can be defined in the source code and what the different commands actually do. I use the source code of a simple animated box as example. This box has an animation that contains of a translation in the first 50 frames and a rotation in the next 50.

Animating your object

After you have defined your object in the source code, it is rather easy to apply a certain animation to it. Below you find the piece of source code that defines my box. To give this object a certain translation or animation all you have to do is to apply a matrix to it using the BGL_SET_MATRIX_INDIRECT command. In the code below you see that three matrices are applied. The first is a static translation that defines the coordinate system to use for the animations. The other two are the translation and the rotation part of the animation itself. And that is all you have to do in the BGL section of the MDL file to create an animation.

test_tutorial_MasterScale_1 label BGLCODE
    BGL_SET_MATRIX_INDIRECT	0
test_tutorial_anim_1 label BGLCODE
    BGL_SET_MATRIX_INDIRECT	1
    BGL_SET_MATRIX_INDIRECT	2
    VAR_BASE_32   VAR_BASE_PARAMS
    MATERIAL 0 ; <134,110,8,255> 
    DRAW_TRI_BEGIN 0, 24
    DRAW_TRI    2,   7,  18 ; poly=1 part=4
    DRAW_TRI   18,  13,   2 ; poly=2 part=4
    DRAW_TRI    5,  16,  21 ; poly=3 part=4
    DRAW_TRI   21,  10,   5 ; poly=4 part=4
    DRAW_TRI    1,  12,  15 ; poly=5 part=4
    DRAW_TRI   15,   4,   1 ; poly=6 part=4
    DRAW_TRI   14,  20,  23 ; poly=7 part=4
    DRAW_TRI   23,  17,  14 ; poly=8 part=4
    DRAW_TRI   19,   8,  11 ; poly=9 part=4
    DRAW_TRI   11,  22,  19 ; poly=10 part=4
    DRAW_TRI    6,   0,   3 ; poly=11 part=4
    DRAW_TRI    3,   9,   6 ; poly=12 part=4
    DRAW_TRI_END
    BGL_RETURN

Matrix relations

In the previous section is sounded really simple, how to make an animation. But of course the scenery engine needs to know a little more than the matrix number to apply. It needs to know the kind of transformation these matrices define and also how the different transformations should act together. This is defined in the SCEN section of the MDL file.

Below you see the code defining the SCEN section of my example file. As you can see it is a sort of table with three entries, one for each matrix.

scene_graph_riff_start_test_tutorial	label	word
    db  'S','C','E','N'   
    dd  scene_graph_riff_end_test_tutorial - $ - 4

    dw 3
bgl_scene_graph_entry_test_tutorial_0	label	byte
BGL_SCENEGRAPH_ENTRY	0, 1, -1, bgl_animation_command_end_test_tutorial_0 - bgl_animation_command_start_test_tutorial_0, bgl_animation_command_start_test_tutorial_0 - bgl_scene_graph_entry_test_tutorial_0
bgl_scene_graph_entry_test_tutorial_1	label	byte
BGL_SCENEGRAPH_ENTRY	1, 2, -1, bgl_animation_command_end_test_tutorial_1 - bgl_animation_command_start_test_tutorial_1, bgl_animation_command_start_test_tutorial_1 - bgl_scene_graph_entry_test_tutorial_1
bgl_scene_graph_entry_test_tutorial_2	label	byte
BGL_SCENEGRAPH_ENTRY	2, -1, -1, bgl_animation_command_end_test_tutorial_2 - bgl_animation_command_start_test_tutorial_2, bgl_animation_command_start_test_tutorial_2 - bgl_scene_graph_entry_test_tutorial_2
scene_graph_riff_end_test_tutorial label word

When you take a look at the BGL_SCENEGRAPH_ENTRY command you see that it has 5 parameters. The last two define which part of the animation command (ANIC) section of the MDL file creates the actual matrices. So that is where the actual transformation that needs to be applied is defined. We will take a look at the ANIC section later on.

The first three parameters define the relation between the different matrices and that is what we will discuss here. The first parameter is the actual matrix number, this is also the number that we called with the BGL_SET_MATRIX_INDIRECT command in the BGL section. The second parameter defines the next child matrix of this matrix, while the third one defines the next peer matrix.

So what do these child and peer relations do? When two matrices have a child relation, this means that if you apply them after each other the resulting transformation is the combination of both of them. When you look at the example code above, you can see that the matrix 1 and matrix 2 have a child relation (these were the translation and rotation matrix), so when you apply them both as we did in the BGL section you object will get both the translation and the rotation.

When two matrices have a peer relation this means that the transformation state is popped back till before the next matrix is applied. So in this case only the last matrix will have an effect on your final transformation of the object.

So in the example animation you can see that the matrix 1 is a child of matrix 0, while matrix 2 is a child again of matrix 1. So when you apply them all three, as happened in the BGL section, the total transformation is the combination of all three matrices.

Creating the transformation matrix

As we saw in the previous section, the SCEN table points to part of the MDL ANIC section. In the ANIC section the animation matrix, that we want to apply, is actually calculated.

For the static transformation the code to calculate this matrix is rather easy, you can see it below. The command points to a table storing the transformation we want and assigns it to the matrix number that we can then call afterwards. The table that stores the transformation will be discussed later.

bgl_animation_command_start_test_tutorial_0	label	BGLCODE
     BGL_TRANSFORM_INDIRECT  0, 0
 bgl_animation_command_end_test_tutorial_0	label	BGLCODE

For the animations matrices the code to calculate them is a bit more complex. The basic idea is that we have a timer that increases its value each 1/18th of a second (thus at 18Hz) and we want to use that to drive the animation. So our first task is to generate a frame number from this running timer.

Below you see the first piece of the code that does this. The BGL_INTERPOLATE command is used to interpolate a given table based on another value. So in the code below we use the table at the location test_tutorial_1_tick18_mod_1 to interpolate the value of BGL_TICK18 and we store the result in the variable usrvar. How this table actually works will be discussed later on, for now it is enough to remember that we use it to split the running timer into smaller pieces. So that we can get a frame number that is running in the range we want (0 to 100 frames in this example).

bgl_animation_command_start_test_tutorial_1	label	BGLCODE
    LOCAL_BASE_32 test_tutorial_1_tick18_mod_1
    BGL_INTERPOLATE VAR_BASE_GLOBAL,BGL_TICK18,\
                    VAR_BASE_GLOBAL,usrvar,\
                    VAR_BASE_LOCAL,(offset test_tutorial_1_tick18_mod_1 - offset test_tutorial_1_tick18_mod_1)

The next part of the code performs a similar task, but this time we read the result of the previous interpolation and interpolate this again. When we discuss the tables used to do this, it will become clear why splitting this in two pieces is easier.

    LOCAL_BASE_32 test_tutorial_1_tick18_mod_2
    BGL_INTERPOLATE VAR_BASE_GLOBAL,usrvar,\
                    VAR_BASE_GLOBAL,usrvar,\
                    VAR_BASE_LOCAL,(offset test_tutorial_1_tick18_mod_2 - offset test_tutorial_1_tick18_mod_2)

The third and last interpolation then converts the output of the previous interpolation, which was an integer number, into a float. This is because the actual animation command that we use next requires a float.

    LOCAL_BASE_32 test_tutorial_1_float_convert_hi
    BGL_INTERPOLATE VAR_BASE_GLOBAL,usrvar,\
                    VAR_BASE_GLOBAL,usrvr3,\
                    VAR_BASE_LOCAL,(offset test_tutorial_1_float_convert_hi - test_tutorial_1_float_convert_hi)
    VAR_BASE_32   VAR_BASE_GLOBAL
    SETWRD usrvr2,0

The final piece of code than does the actual calculation of the matrix we want to use for our animation. The floating frame number we calculated before is the input, together with the table that defines the animation we want. This table is discussed in the next section in more detail. The three parameters at the end, that are set to 0.0 in this example seem to define an offset for the animation result. The last parameter is the matrix number that we use to call this animation.

    LOCAL_BASE_32 test_tutorial_trans_1
    BGL_ANIMATE_INDIRECT   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_tutorial_trans_1 - offset test_tutorial_trans_1),0.0,0.0,0.0,1
 bgl_animation_command_end_test_tutorial_1	label	BGLCODE

Storing the animation data

We have now seen how the transformation matrices are calculated and how their relations are defined. But we have not yet seen in which format their data is stored. That will be discussed in this section.

We will start the static transformation matrices. These are stored in the TRAN section of the MDL file. Below you see the lines of code that define such a matrix. There are no opcodes in this section, just a serie of 16 floating values that define one matrix. If you find 32 values, you know there must be two matrices defined.

; Static matrix parameters for transform: 0
    real4      1.000000,     0.000000,     0.000000,     0.000000  
    real4      0.000000,     0.000000,     1.000000,     0.000000  
    real4      0.000000,    -1.000000,     0.000000,     0.000000  
    real4      0.000000,     0.000000,     0.000000,     1.000000

What do these 16 values define? The upper 3x3 matrix defines the rotation of the coordinate system and the bottom 1x3 matrix is the translation. If you want a more mathematical explanation, including some nice sine and cosine formulas on how to calculate the rotation part, have a look at this article from the DirectX SDK.

When we look at the source code given above, you can see that a XZY coordinate system is changed into a XYZ coordinate system with this matrix. And also the positive direction of the Z-axis is changed.

test_tutorial_1_tick18_mod_1 label word
    dw  82
    dw -32768,0,-31154,1615
    dw -31152,0,-29538,1615
    dw -29536,0,-27922,1615
    dw -27920,0,-26306,1615
    dw -26304,0,-24690,1615
    dw -24688,0,-23074,1615
    dw -23072,0,-21458,1615
    dw -21456,0,-19842,1615
    dw -19840,0,-18226,1615
    dw -18224,0,-16610,1615
    dw -16608,0,-14994,1615
    dw -14992,0,-13378,1615
    dw -13376,0,-11762,1615
    dw -11760,0,-10146,1615
    dw -10144,0,-8530,1615
    dw -8528,0,-6914,1615
    dw -6912,0,-5298,1615
    dw -5296,0,-3682,1615
    dw -3680,0,-2066,1615
    dw -2064,0,-450,1615
    dw -448,0,1166,1615
    dw 1168,0,2782,1615
    dw 2784,0,4398,1615
    dw 4400,0,6014,1615
    dw 6016,0,7630,1615
    dw 7632,0,9246,1615
    dw 9248,0,10862,1615
    dw 10864,0,12478,1615
    dw 12480,0,14094,1615
    dw 14096,0,15710,1615
    dw 15712,0,17326,1615
    dw 17328,0,18942,1615
    dw 18944,0,20558,1615
    dw 20560,0,22174,1615
    dw 22176,0,23790,1615
    dw 23792,0,25406,1615
    dw 25408,0,27022,1615
    dw 27024,0,28638,1615
    dw 28640,0,30254,1615
    dw 30256,0,31870,1615
    dw 31872,0,32767,896

Anim table01.jpg

test_tutorial_1_tick18_mod_2 label word
    dw  32
    dw 0,0,100,100
    dw 101,0,201,100
    dw 202,0,302,100
    dw 303,0,403,100
    dw 404,0,504,100
    dw 505,0,605,100
    dw 606,0,706,100
    dw 707,0,807,100
    dw 808,0,908,100
    dw 909,0,1009,100
    dw 1010,0,1110,100
    dw 1111,0,1211,100
    dw 1212,0,1312,100
    dw 1313,0,1413,100
    dw 1414,0,1514,100
    dw 1515,0,1615,100

Anim table02.jpg

test_tutorial_1_float_convert_hi  label word
    dw  12               ; num entries
    dw  0,0
    dw  1,16256
    dw  2,16384
    dw  4,16512
    dw  8,16640
    dw  16,16768
    dw  32,16896
    dw  64,17024
    dw  128,17152
    dw  256,17280
    dw  512,17408
    dw  1024,17536
test_tutorial_trans_1       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           3       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.000000 ; frame/x/y/z values
    real4     50.0, 20.000000,  0.000000,  0.000000 ; frame/x/y/z values
    real4    100.0, 20.000000,  0.000000,  0.000000 ; frame/x/y/z values
test_tutorial_quat_1        label word
    dw           3       ; 3: Quaternion (rotation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw          27       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.000000,  1.000000 ; frame/x/y/z/w values
    real4     51.0,  0.000000,  0.000000, -0.015707,  0.999877 ; frame/x/y/z/w values
    real4     53.0,  0.000000,  0.000000, -0.047106,  0.998890 ; frame/x/y/z/w values
    real4     55.0,  0.000000,  0.000000, -0.078459,  0.996917 ; frame/x/y/z/w values
    real4     57.0,  0.000000,  0.000000, -0.109734,  0.993961 ; frame/x/y/z/w values
    real4     59.0,  0.000000,  0.000000, -0.140901,  0.990024 ; frame/x/y/z/w values
    real4     61.0,  0.000000,  0.000000, -0.171929,  0.985109 ; frame/x/y/z/w values
    real4     63.0,  0.000000,  0.000000, -0.202787,  0.979223 ; frame/x/y/z/w values
    real4     65.0,  0.000000,  0.000000, -0.233445,  0.972370 ; frame/x/y/z/w values
    real4     67.0,  0.000000,  0.000000, -0.263873,  0.964557 ; frame/x/y/z/w values
    real4     69.0,  0.000000,  0.000000, -0.294040,  0.955793 ; frame/x/y/z/w values
    real4     71.0,  0.000000,  0.000000, -0.323917,  0.946085 ; frame/x/y/z/w values
    real4     73.0,  0.000000,  0.000000, -0.353475,  0.935444 ; frame/x/y/z/w values
    real4     75.0,  0.000000,  0.000000, -0.382683,  0.923880 ; frame/x/y/z/w values
    real4     77.0,  0.000000,  0.000000, -0.411514,  0.911403 ; frame/x/y/z/w values
    real4     79.0,  0.000000,  0.000000, -0.439939,  0.898028 ; frame/x/y/z/w values
    real4     81.0,  0.000000,  0.000000, -0.467930,  0.883766 ; frame/x/y/z/w values
    real4     83.0,  0.000000,  0.000000, -0.495459,  0.868631 ; frame/x/y/z/w values
    real4     85.0,  0.000000,  0.000000, -0.522499,  0.852640 ; frame/x/y/z/w values
    real4     87.0,  0.000000,  0.000000, -0.549023,  0.835807 ; frame/x/y/z/w values
    real4     89.0,  0.000000,  0.000000, -0.575005,  0.818150 ; frame/x/y/z/w values
    real4     91.0,  0.000000,  0.000000, -0.600420,  0.799685 ; frame/x/y/z/w values
    real4     93.0,  0.000000,  0.000000, -0.625243,  0.780430 ; frame/x/y/z/w values
    real4     95.0,  0.000000,  0.000000, -0.649448,  0.760406 ; frame/x/y/z/w values
    real4     97.0,  0.000000,  0.000000, -0.673012,  0.739631 ; frame/x/y/z/w values
    real4     99.0,  0.000000,  0.000000, -0.695913,  0.718126 ; frame/x/y/z/w values
    real4    100.0,  0.000000,  0.000000, -0.707107,  0.707107 ; frame/x/y/z/w values

Tweaking animations