Animations with over 1024 frames

From FSDeveloper Wiki
Revision as of 10:45, 15 March 2009 by Arno (talk | contribs) (Animation interpolation)
Jump to: navigation, search

The FS2004 MakeMDL has a limitation build in that it can only export animations with a length of 1024 frames. Because the animation timer is a 18 Hz timer that means that the maximum animation length is only about 55 seconds. That might be fine for a hangar door that is opening or a radar that is rotating, but if you want to make some animated traffic on your airport for example 55 seconds is a bit short.

Unfortunately there is no workaround for this limitation from within GMax, but when you start editing the ASM code of your object it is possible to get beyond 1024 frames for an animation. So the limitation is purely in MakeMDL, not in FS2004 itself. This tutorial will explain which pieces of the ASM code you have to edit to make a longer animation.

Although I will try to explain everything as clearly as possible, I would say this is a task that a novice scenery designer might find a bit hard. But if you are a little bit familiar with ASM tweaking then I hope this tutorial will give you enough knowledge of how the animations are defined to be able to tweak them. Let's get started.

There are two sections of the ASM code where you will have to make changes. The first is the section with the animation tables (ANIP) and the second is the section where the actual interpolation for the animation is performed (ANIC). Which changes to make in these two seconds is discussed next.

Animation tables

In this section I will describe with changes you need to make the animation tables. There are two sorts of tables you will have to change. The first is the table that stores your animation keyframes and the second is the table that is used to determine the frame for the animation timer variable.

Keyframes table

The animation keyframe table stores all the keyframes of your animation. They contain of an frame number and then the actual data. This can be 3 values for a position or 4 for a rotation (given as a quaternion). Below you see a sample of how the keyframe table can look for a simple translation of 1024 frames.

 anim_long_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,   0.000000,  0.000000,  0.000000 ; frame/x/y/z values
     real4    1023.0, 100.000000,  0.000000,  0.000000 ; frame/x/y/z values

As we want to get a longer animation, it is obvious that we need to add more keyframes to this table. So you will have to add additional lines until you reach the length in frames you want for your animation. Below you see an example where I extended the table to 4096 frames in total.

 anim_long_trans_1       label word
     dw           1       ; 1: Point (translation) 
     real4     -1.0       ; Previous panim value
     real4 16 dup (0.0)   ; Cached matrix 
     dw           5       ; number of entries
     real4       0.0,   0.000000,   0.000000,  0.000000 ; frame/x/y/z values
     real4    1024.0, 100.000000,   0.000000,  0.000000 ; frame/x/y/z values
     real4    2048.0, 100.000000, 100.000000,  0.000000 ; frame/x/y/z values
     real4    3072.0, 200.000000, 100.000000,  0.000000 ; frame/x/y/z values
     real4    4095.0, 200.000000,   0.000000,  0.000000 ; frame/x/y/z values

How you get the data to extend the keyframe table is up to you. Obviously GMax can not export the longer table for you. So you either have to type in the new data manually, or you could export multiple sections of 1024 frames from GMax and stitch them all together manually. In the last case you need to be sure that the frame value continues nicely.

Timer interpolation table

The second type of table that you will have to change is the interpolation table that is used to determine the correct frame number for the animation timer. The animation timer is nothing more than a variable that is increased by 1 every 1/18th of a second. It runs between a value of -32768 and 32767. That is not something we can use to interpolate our keyframes with, so therefore there is an interpolation table that is used to interpolate from the timer variable to a variable that is running between 0 and the amount of frames we want in our animation.

For the situation with 1024 frames that we exported from GMax you see the interpolation table below.

 anim_long_1_tick18_mod_1 label word
     dw  8
     dw -32768,0,-16386,16383
     dw -16384,0,-2,16383
     dw 0,0,16382,16383
     dw 16384,0,32766,16383
 anim_long_1_tick18_mod_2 label word
     dw  32
     dw 0,0,1023,1023
     dw 1024,0,2047,1023
     dw 2048,0,3071,1023
     dw 3072,0,4095,1023
     dw 4096,0,5119,1023
     dw 5120,0,6143,1023
     dw 6144,0,7167,1023
     dw 7168,0,8191,1023
     dw 8192,0,9215,1023
     dw 9216,0,10239,1023
     dw 10240,0,11263,1023
     dw 11264,0,12287,1023
     dw 12288,0,13311,1023
     dw 13312,0,14335,1023
     dw 14336,0,15359,1023
     dw 15360,0,16383,1023
 anim_long_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
 

As you can see this table actually consists of three sections. Let's take a look at what these actually do before we start tweaking them. The first part (named mod_1) is used to cut the timer running between -32768 and 32767 into sections running from 0 to 16383. This two step approach is only done because else the second interpolation table would become very long. The picture illustrates how this sawtooth pattern works for interpolating from the timer variable to a temporarily variable running between 0 and 16383.

The sawtooth pattern for the first interpolation

The second table (named mod_2) does the same, but it takes a variable running between 0 and 16383, which is the output of the first table, and then interpolates it so that we get a variable as output that is running between 0 and 1023. Below you see an image of this interpolation pattern as well.

The sawtooth pattern for the second interpolation

The third part of the table (named convert_hi) is used to convert from the integer frame number we have determined in the previous step, to a floating point representation of that frame number. This is because the keyframe data is stored as floating point numbers. In the section about the interpolation we will see in more detail how this works.

So now we know how the tables that GMax has written for us work and we can start to adjust them for the situation with longer animations. The first part (named mod_1) does not have to be changed, unless you want an animation with over 16384 frames of course. Let's assume that is not the case now.

The second part (named mod_2) will have to be changed, as our animation now has 4096 instead of 1024 frames. So we need to interpolate between 0 and 4095 now. The picture below shows how our sawtooth of the interpolation needs to look now.

The sawtooth pattern for the modified second interpolation

The third part we will also have to change as our interpolation runs till a higher frame number now. Here we will just show what needs to be changed, in the section about the interpolation you will learn what it actually means.

Below you see how the total interpolation table looks after these changes.

 anim_long_1_tick18_mod_1 label word
     dw  8
     dw -32768,0,-16386,16383
     dw -16384,0,-2,16383
     dw 0,0,16382,16383
     dw 16384,0,32766,16383
 anim_long_1_tick18_mod_2 label word
     dw  8
     dw 0,0,4095,4095
     dw 4096,0,8191,4095
     dw 8192,0,12287,4095
     dw 12288,0,16383,4095
 anim_long_1_float_convert_hi  label word
     dw  14               ; 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
     dw  2048,17664
     dw  4096,17792

After these changes we are done with tweaking the animation tables, so we can continue to alter the code where the actual interpolation is performed.

Animation interpolation

In the ANIC section of the MDL file you will find that code that performs the actual interpolation, using the tables defined in the ANIP section. These table we already tweaking in the previous section, so let's look at the interpolation itself now.

For the original 1024 frames animation the code looks like this:

 bgl_animation_command_start_anim_long_1	label	BGLCODE
     ; usrvar = tick18 mod 1024
     LOCAL_BASE_32 anim_long_1_tick18_mod_1
     BGL_INTERPOLATE VAR_BASE_GLOBAL,BGL_TICK18,\
                     VAR_BASE_GLOBAL,usrvar,\
                     VAR_BASE_LOCAL,\
                     (offset anim_long_1_tick18_mod_1 - offset anim_long_1_tick18_mod_1)
     LOCAL_BASE_32 anim_long_1_tick18_mod_2
     BGL_INTERPOLATE VAR_BASE_GLOBAL,usrvar,\
                     VAR_BASE_GLOBAL,usrvar,\
                     VAR_BASE_LOCAL,\
                     (offset anim_long_1_tick18_mod_2 - offset anim_long_1_tick18_mod_2)
     ; convert usrvar to float and store in usrvr2-usrvr3
     LOCAL_BASE_32 anim_long_1_float_convert_hi
     BGL_INTERPOLATE VAR_BASE_GLOBAL,usrvar,\
                     VAR_BASE_GLOBAL,usrvr3,\
                     VAR_BASE_LOCAL,\
                     (offset anim_long_1_float_convert_hi - anim_long_1_float_convert_hi)
     VAR_BASE_32   VAR_BASE_GLOBAL
     SETWRD usrvr2,0
     IFIN1 anim_long_1_fc_512, usrvar,257,512
     IFMSK anim_long_1_fc_512, usrvar,0001h
     SETWRD usrvr2,08000h
 anim_long_1_fc_512 label word
     IFIN1 anim_long_1_fc_1024, usrvar,513,1024
     IFMSK anim_long_1_fc_1024, usrvar,0003h
     SETWRD usrvr2,04000h
     IFMSK anim_long_1_fc_1024, usrvar,0002h
     SETWRD usrvr2,08000h
     IFMSK anim_long_1_fc_1024, usrvar,0001h
     SETWRD usrvr2,0C000h
 anim_long_1_fc_1024 label word
     LOCAL_BASE_32 anim_long_trans_1
     BGL_ANIMATE_INDIRECT   VAR_BASE_GLOBAL,usrvr2,VAR_BASE_LOCAL,\
        (offset anim_long_trans_1 - offset anim_long_trans_1),0.0,0.0,0.0,1

This code looks quite complex, so lets go through it line by line to see what is happening. The LOCAL_BASE_32 command is used to define which piece of memory should be used for local variables. This is the way that the animation tables with all the data will be referred to.

Then we see that three times the BGL_INTERPOLATE command is used. As the name suggests this command performs an interpolation. It takes three sets of arguments, first the input variable, then the output variable and lastly the table with the interpolation data. Each of these sets consists of two arguments, the first determines if we use the local or the global variable space. So this sets if we use the local variables, set with the LOCAL_BASE_32 command or if we refer to the global FS variables, which is where the timer variable is for example.

As mentioned there are three interpolations in the code. The first takes the 18 Hz timer as input and using first interpolation we discussed before. It writes its temporarily output to the usrvar global variable. The second interpolation takes this temporarily variable and interpolates it again using the second interpolation table. The result is stored in the usrvar variable again. Now we have the frame number stored as an integer.

The third interpolation is a bit more difficult. In the table with the keyframes for our animation we store the animation number as a floating point value. So we need to convert the integer value we have now to a floating point. The table below lists how these floating point value would look for a number of frames: