Frequency controlled animations (ASM tweak)

From FSDeveloper Wiki
Jump to: navigation, search



First I want to note that this is the origional tutorial I wrote. Since then I have learned new things about tweaking animations and this new knowledge is put into the CAT tool. I hope to update this article in the future to reflect this new knowledge. But this origional tutorial might still help some user who want to tweak something themself or understand the logic behind it.

This tutorial is for the Fs2002 ASM code, for the Fs2004 code the principle remains the same, although the structure of the source file has completely changed. Have a look at the article about the RIFF format for more details about this.

Introduction

Have you made an animation in GMax? Then you have probably noticed that it keeps on playing endlessly. This is not always what you want, just think of a gate that should dock to your aircraft. This document will describe how you can edit the BGLC code that GMax made to get better control over the animation. In the rest of this tutorial I will use the NAV2 frequency as an example, but you can of course replace this variable by another one. The technique used basically stays the same.

What do you need

You first question will probably be what do I need to have and need to know to be able to do these changes?

The first thing you need are the BGLC source files, if you check the “Keep files” option during exporting from GMax to files with the extension ASM will be saved in the same folder as the BGL file you exported. If you object is called test, these files have the names test.asm and test_0.asm.

To look at and edit these files you can best use a normal texture editor, like Notepad. If you want to use another program make sure you save the output as plain ASCII. The change these ASM files back into a BGL file you need a compiler, we are going to use BGLC for that. The latest version of this tool came with the BGL Compiler SDK for Fs2002. When you have all these things, we are ready to get started!

The principle

To change the display of the animation according to a certain setting of the NAV2 frequency we are going to use the following principle.

The animation is controlled by a table that contains the positions and rotation for the animation. We are going to add more of these tables, for example one for the reversed animation and also one that has the initial position for every position of the table, so for FS it will be an animation, but because all positions are constant it will look as static to us.

Then depending on the selected frequency we are going to call one of these tables. This way we can get an animation started when we select the frequency and so on. One off the problems that we will encounter is that you can only check in FS what the value of the frequency is, but you can not see if the frequency has changed. This means that we have to build in some sort of memory, so that we know when the animation has played once and should stop for example. We are going to use an user variable to create this memory. OK, enough talking about how we are going to do it, let's start the actual work.

Original ASM file

For this tutorial I have made a simple test scenery. The scenery contains of one static box (this is needed because otherwise GMax animations do not always work correct) and one animated box. The ASM files as GMax made them are included in the zipfile that you downloaded, they are named test.asm and test_0.asm. We are only going to make changes to the test_0.asm file, so open this file now.

Before we start making the changes, first scroll through this source file to make yourself a bit familiar with the different parts of it.

The first part we see are the material definitions. Next is the list of points (all the VERTEX_DEF commands). As we are not going to change how the object looks we don't need to make any changes to this part of the source file.

After the point list you will find the drawing commands for the static box. Now we have reached the interesting part of the source file. The next section contains the tables that are used for the animation. The part named test_trans_1 contains the table for the translational motion and the part test_quat_1 contains the table for the rotational motion. As described above in the principles we are going to add some extra tables of these two sorts. These tables are followed by some more tables, that are used to define the correct timing using the tick18 variable1.

Then we reach the part with the BGL_ANIMATE commands, here the actual animation is defined and the tables that should be used are called. This is also the place where we are going to add the checks on the variable to make sure the correct animation is called.

Finally you see the code that draws are box (the one that is animated). Of course when you have a more complex project, with more parts, you can find more source below this point, but it will either look like the code for the static box or like the one of the animated box.

Animation tables

The first thing we are going to change are the animation tables. We are going to add some extra tables for new animations. Here is how the original translational table looks:

test_trans_1       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values
    real4    100.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values

We also want the animation in the backwards order for example. To do this just copy this code, change the label and change the positions that belong to the times. Here is an example for the backwards animation:

test_trans_1a       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
    real4    100.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values

Notice I have changed the label to test_trans_1a. You can choose any name, as long as it is unique. If you have more then one animated part in your source, the table for the second part will be called test_trans_2 by default, so you would better not use that one here.

You can also add more points for the animation. You must change the line that specifies the number of entries that will follow and add the extra lines in the table. Let's say we want the object to do nothing the first 20 frames and then start animating, that code would look like this:

test_trans_1a       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, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
    real4     20.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
    real4    100.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values

I think you'll get the idea by now. For this example we need 4 tables, the original one, the backwards animation and two tables that have the static animations at both endpoints. The complete code for these four tables looks like this:

test_trans_1       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values
    real4    100.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
test_trans_1a       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
    real4    100.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values
test_trans_1b       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values
    real4    100.0, -176.025238, -97.791794,  0.000000 ; frame/x/y/z values
test_trans_1c       label word
    dw           1       ; 1: Point (translation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values
    real4    100.0, 162.454285, 99.359100,  0.000000 ; frame/x/y/z values

Now we have had the translational motion, we have to do the same for the rotational motion. Here we again need four tables. The original one, the one for the backwards animation and the two for the static animations at the endpoints. Because the principle is the same I will only give the end result here.

test_quat_1        label word
    dw           3       ; 3: Quaternion (rotation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw          26       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.000000,  1.000000 ; frame/x/y/z/w values
    real4      4.0,  0.000000,  0.000000, -0.102272,  0.994757 ; frame/x/y/z/w values
    real4      8.0,  0.000000,  0.000000, -0.203471,  0.979081 ; frame/x/y/z/w values
    real4     12.0,  0.000000,  0.000000, -0.302536,  0.953138 ; frame/x/y/z/w values
    real4     16.0,  0.000000,  0.000000, -0.398429,  0.917199 ; frame/x/y/z/w values
    real4     20.0,  0.000000,  0.000000, -0.490143,  0.871642 ; frame/x/y/z/w values
    real4     24.0,  0.000000,  0.000000, -0.576717,  0.816944 ; frame/x/y/z/w values
    real4     28.0,  0.000000,  0.000000, -0.657244,  0.753678 ; frame/x/y/z/w values
    real4     33.0,  0.000000,  0.000000, -0.748117,  0.663567 ; frame/x/y/z/w values
    real4     37.0,  0.000000,  0.000000, -0.812058,  0.583577 ; frame/x/y/z/w values
    real4     41.0,  0.000000,  0.000000, -0.867485,  0.497465 ; frame/x/y/z/w values
    real4     45.0,  0.000000,  0.000000, -0.913813,  0.406138 ; frame/x/y/z/w values
    real4     49.0,  0.000000,  0.000000, -0.950558,  0.310552 ; frame/x/y/z/w values
    real4     53.0,  0.000000,  0.000000, -0.977334,  0.211708 ; frame/x/y/z/w values
    real4     57.0,  0.000000,  0.000000, -0.993861,  0.110645 ; frame/x/y/z/w values
    real4     61.0,  0.000000,  0.000000, -0.999966,  0.008421 ; frame/x/y/z/w values
    real4     65.0,  0.000000,  0.000000, -0.995584, -0.093891 ; frame/x/y/z/w values
    real4     69.0,  0.000000,  0.000000, -0.980761, -0.195218 ; frame/x/y/z/w values
    real4     73.0,  0.000000,  0.000000, -0.955653, -0.294499 ; frame/x/y/z/w values
    real4     77.0,  0.000000,  0.000000, -0.920523, -0.390690 ; frame/x/y/z/w values
    real4     81.0,  0.000000,  0.000000, -0.875740, -0.482785 ; frame/x/y/z/w values
    real4     85.0,  0.000000,  0.000000,  0.821771,  0.569818 ; frame/x/y/z/w values
    real4     89.0,  0.000000,  0.000000,  0.759186,  0.650874 ; frame/x/y/z/w values
    real4     94.0,  0.000000,  0.000000,  0.669844,  0.742502 ; frame/x/y/z/w values
    real4     98.0,  0.000000,  0.000000,  0.590394,  0.807115 ; frame/x/y/z/w values
    real4    100.0,  0.000000,  0.000000,  0.548293,  0.836286 ; frame/x/y/z/w values
test_quat_1a        label word
    dw           3       ; 3: Quaternion (rotation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw          26       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.548293,  0.836286 ; frame/x/y/z/w values
    real4      2.0,  0.000000,  0.000000,  0.590394,  0.807115 ; frame/x/y/z/w values
    real4      6.0,  0.000000,  0.000000,  0.669844,  0.742502 ; frame/x/y/z/w values
    real4     11.0,  0.000000,  0.000000,  0.759186,  0.650874 ; frame/x/y/z/w values
    real4     15.0,  0.000000,  0.000000,  0.821771,  0.569818 ; frame/x/y/z/w values
    real4     19.0,  0.000000,  0.000000, -0.875740, -0.482785 ; frame/x/y/z/w values
    real4     23.0,  0.000000,  0.000000, -0.920523, -0.390690 ; frame/x/y/z/w values
    real4     27.0,  0.000000,  0.000000, -0.955653, -0.294499 ; frame/x/y/z/w values
    real4     31.0,  0.000000,  0.000000, -0.980761, -0.195218 ; frame/x/y/z/w values
    real4     35.0,  0.000000,  0.000000, -0.995584, -0.093891 ; frame/x/y/z/w values
    real4     39.0,  0.000000,  0.000000, -0.999966,  0.008421 ; frame/x/y/z/w values
    real4     43.0,  0.000000,  0.000000, -0.993861,  0.110645 ; frame/x/y/z/w values
    real4     47.0,  0.000000,  0.000000, -0.977334,  0.211708 ; frame/x/y/z/w values
    real4     51.0,  0.000000,  0.000000, -0.950558,  0.310552 ; frame/x/y/z/w values
    real4     55.0,  0.000000,  0.000000, -0.913813,  0.406138 ; frame/x/y/z/w values
    real4     59.0,  0.000000,  0.000000, -0.867485,  0.497465 ; frame/x/y/z/w values
    real4     63.0,  0.000000,  0.000000, -0.812058,  0.583577 ; frame/x/y/z/w values
    real4     67.0,  0.000000,  0.000000, -0.748117,  0.663567 ; frame/x/y/z/w values
    real4     72.0,  0.000000,  0.000000, -0.657244,  0.753678 ; frame/x/y/z/w values
    real4     76.0,  0.000000,  0.000000, -0.576717,  0.816944 ; frame/x/y/z/w values
    real4     80.0,  0.000000,  0.000000, -0.490143,  0.871642 ; frame/x/y/z/w values
    real4     84.0,  0.000000,  0.000000, -0.398429,  0.917199 ; frame/x/y/z/w values
    real4     88.0,  0.000000,  0.000000, -0.302536,  0.953138 ; frame/x/y/z/w values
    real4     92.0,  0.000000,  0.000000, -0.203471,  0.979081 ; frame/x/y/z/w values
    real4     96.0,  0.000000,  0.000000, -0.102272,  0.994757 ; frame/x/y/z/w values
    real4    100.0,  0.000000,  0.000000,  0.000000,  1.000000 ; frame/x/y/z/w values
test_quat_1b        label word
    dw           3       ; 3: Quaternion (rotation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.000000,  1.000000 ; frame/x/y/z/w values
    real4    100.0,  0.000000,  0.000000,  0.000000,  1.000000 ; frame/x/y/z/w values
test_quat_1c        label word
    dw           3       ; 3: Quaternion (rotation) 
    real4     -1.0       ; Previous panim value
    real4 16 dup (0.0)   ; Cached matrix 
    dw           2       ; number of entries
    real4      0.0,  0.000000,  0.000000,  0.548293,  0.836286 ; frame/x/y/z/w values
    real4    100.0,  0.000000,  0.000000,  0.548293,  0.836286 ; frame/x/y/z/w values

The animation code

The next part of the code we are going to make changes to is the part that generates the actual animation. In the original file this code looks like:

    BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1 - offset test_pvan_1),0.0,0.0,0.0
    BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1 - offset test_pvan_1),0.0,0.0,0.0

Note that this are only two lines of code, but they don't fit in this document. When we now want to call the backwards animation we use the following lines (using the names for the tables used above):

    BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1a - offset test_pvan_1),0.0,0.0,0.0
    BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1a - offset test_pvan_1),0.0,0.0,0.0

By changing the make of the label you can use any animation table you have made. If your object only has a translation or only a rotation you will only find one BGL_ANIMATE command in the source.

Frequency checking

OK, we can now change the animation tables and use the ones we want, be we still need to check for the frequency to start the animation. To check the value of a certain value we are going to use the IFIN1 command. Here is an example of the command:

    IFIN1 a00, tod, 2, 4

In this example the variable tod (time of day) is checked. If the value of this variable is between 2 and 4 (that means dusk/dawn or night) the commands following this instruction are executed, otherwise a jump to the label a00 is made.

As explained before while discussing the principles we are going to use an user variable to store the state of the animation. We will use the variable usrvr4 for this purpose. I have made the following list of possible values:

value    description
0        No animation, on start location
1        Ready for animation
2        Animation
3        No animation, on end location
4        Ready for backwards animation
5        Backwards animation

Depending on the type of animation your object is performing you could need more or less of these positions to be stored. But the principle for the implementation is the same as for this example below, so once you understand that code it shouldn't be too hard to change it.

Now we can make the following piece of code that calls the correct animation depending on the value of the user variable:

    IFIN1 a08, usrvr4, 0, 1
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1b - offset test_pvan_1),0.0,0.0,0.0              
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1b - offset test_pvan_1),0.000000,0.000000,0.000000
        BGL_JUMP_32 a11
    a08 label word
    IFIN1 a09, usrvr4, 2, 2
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1 - offset test_pvan_1),0.0,0.0,0.0              
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1 - offset test_pvan_1),0.000000,0.000000,0.000000
        BGL_JUMP_32 a11
    a09 label word
    IFIN1 a10, usrvr4, 3, 4
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1c - offset test_pvan_1),0.0,0.0,0.0              
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1c - offset test_pvan_1),0.000000,0.000000,0.000000
        BGL_JUMP_32 a11
    a10 label word
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_trans_1a - offset test_pvan_1),0.0,0.0,0.0              
        BGL_ANIMATE   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,(offset test_quat_1a - offset test_pvan_1),0.000000,0.000000,0.000000
    a11 label word

As you can see above the correct animation table is used depending on the value of the user variable. The names for the tables are the same as before, when we defined them here, so if you have chosen others names, you should check which ones to call.

The next thing we need is to check the value of the NAV2 frequency and change the value of the user variable if the frequency has been set or unset. The following code can do that:

    IFIN1 a04, pvfreq, 1200h, 1200h
	IFIN1 a05, 320h, 0, 0
	    SETWRD 320h, 1
	a05 label word
	BGL_JUMP_32 a06
    a04 label word
    	IFIN1 a07, 320h, 3, 3
    	    SETWRD 320h, 4
    	a07 label word
    a06 label word

The variable pvfreq is the NAV2 frequency. In this case we check if the frequency has been set to 112.002. If the frequency is selected and the user variable has the value of 0, which means that it is at the initial location, then the user value will be set to 1. This means that the animation should be started, as soon as the start of it has been reached. Because the ticker is running all the time it is likely that at the moment you set the frequency the animation is not at the beginning and therefore we have to wait until the begin has been reached again. We do the same for the end of the animation. If the position is set to the end location, user variable has the value of 3, and the frequency is no longer selected, we set the user variable to 4 and the backwards animation will start as soon as possible.

Now we are almost done, only one more step to take. When the animation has run one time, it should stop. And also when the ticker has reached zero again the simulation should be started if the user variable is set to ready for animation. We can use the following code for that:

    IFIN1 a00, 320h, 1, 1
    	IFIN1 a00, usrvar, 0, 3
    	    SETWRD 320h, 2
    a00 label word
    IFIN1 a01, 320h, 4, 4
    	IFIN1 a01, usrvar, 0, 3
    	    SETWRD 320h, 5
    a01 label word
    IFIN1 a02, 320h, 2, 2
    	IFIN1 a02, usrvar, 97, 100
    	    SETWRD 320h, 3
    a02 label word
    IFIN1 a03, 320h, 5, 5
    	IFIN1 a03, usrvar, 97, 100
    	    SETWRD 320h, 0
    a03 label word

This code works as follows. When the user variable (usrvr4) is set to 1, which means ready for animation, and the usrvar3 has a value between 0 and 3, then the user variable is set to 2, which means that the actual animation will start. You might expect that we would only check if the value is equal to 0, but this was the source of animations not stopping and cycling a few times. It should be remember that the tick18 timer is 18Hz, which means that there are 18 frames a second. If the framerate in your scenery is lower, which is often the case is detailed sceneries (especially on slower computers) then some keyframes are skipped. This means that it is possible the the 0 frame is skipped a few times before it is reached and that can result in the animation playing a few times. Therefore we need to check for a small range of frames around 0, I have chosen to check for the frames between 0 and 3.

The same is done for the backwards animation. And also when the animation is playing, the user variable has the value 2 or 5, and the end of the animation has been reached, usrvar has a value between 97 and 100, the user variable (usrvr4) is changed to display the static condition at either the start or end location.

Final note

OK, you have now seen all the parts that are needed to control your animation. I hope this tutorial makes it clear to me which changes I made to the source and why I did so.