Difference between revisions of "Animations explained (ASM code)"
m (Animation - Explained moved to Animations explained (ASM code)) |
|||
Line 11: | Line 11: | ||
}} | }} | ||
− | This article describes how the | + | 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 page is a work-in-progress. Generic message - Please note some detail may possibly be missing or incorrect. |
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.
Contents
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
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
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