Animations with over 1024 frames
This page is a work-in-progress. Generic message - Please note some detail may possibly be missing or incorrect. |
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.
Contents
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 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 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 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: