Realtime 3D Animations (Morph-Targets) [+Source]


Realtime 3D Animations (Morph-Targets/Blendshapes)
optimized for use with Blender

GM Version
: GM: Studio
Target Platform: tested on Windows (should work on other platforms as well)
Download Link:

NOTE: JAVA is required in order to run the .bao converter!
Another Open-source project which allows you to use Blend-shapes/Morph-targets (exported from Blender or any other 3D animation software)
and use them for interpolated 3D animations inside GM-Studio.


This project is licensed under the Open-Source BSD-3 License.
Credits also go to TheSnidr who wrote a function for calculating a inverse transpose Matrix, which i'm using in an altered form for the normal calculation.

The following video showcases, what you can do with this technique:

Advantages of this technique:
  • very fast calculation of the 3D animations (vertex blending is offloaded to the GPU)
  • very simple from a technical standpoint. (and also easy to implement)
  • higher memory usage compared to other animation techniques (neglectable if you use low-poly models. > PCs have lots of video-ram nowadays.)
  • no blending between multiple animations (example: You cant have a fluid transition between an idle- and running animation.)
  • procedural animations aren't possible. (like dynamic placement of the foot of a character on slopes, etc...)

This technique is using .obj frames (which are exported from blender or any other 3D animation software) and converts those into a custom binary format (.bao which stands for "Binary Animated Object") with the help of a conversion tool (which is very easy to use.)

A screenshot of the example (+ source) which you can download

Howto tutorial:

Exporting 3D animations

Now, let's do a little "tutorial" on how to export the 3D animations from Blender, how to convert them and how to import them into an existing project.
First, download the .zip file of this demo. (Link can be found at the bottom of this post).
Extract the content and look into the "animations" folder. In this folder, you should see 3 .blend files and one .png file (which is the texture which we are using)
* glass_animation.blend
* torus_explode.blend
* cube_deform.blend

Open the glass_animation.blend in Blender (if you don't have blender, you can download it HERE.)
Note: I'm not going to show you how to animate a character in blender. There are tons of tutorials on how to do this. I'm just going to show you how to export an existing animation for GM: Studio.

The opened file should look like this:

You can start the animation in Blender by pressing ALT + A simultaneously. The animation has (at least in Blender) a very low framerate. The reason behind this is, that we want to use as few frames as possible for the entire animation. The more frames the animation has, the more memory it will consume in GM: Studio. GM: S will then interpolate between those few frames to make the animation fluid again.

Now, there is one important thing to note:
The 3D model needs to be exported as an triangulated 3D model. The obj-exporter can split the model into triangles for you automatically (as you will see later).
But sometimes, this can lead to problems. The triangulation option from the obj-exporter is applied AFTER the mesh deformation. This can lead to some vertex-order problems (the animation will work ,but the vertex order of the triangles gets messed up which results in a "buggy" animation.)
So to prevent this from happening, add a "triangulate" modifier to the mesh BEFORE the animation modifier:
(These modifiers are already added in the blender files of the source project.)
(NOTE: Don't click on the "apply" button on these modifiers!)

Triangulate first, armature second. This may not be nessecary (armatures seem to work without it while other types of animations have this issue) but it's generally a good idea to do this, if you don't want to have any problems with that.

Also make sure that your 3D model is UV-unwrapped (select all vertices in edit mode > press "u" > click "unwrap")

If you already use a mesh which is uv-unwrapped, you don't need to do this.

If your 3D model doesn't have UV-koordinates, the export of the 3D animation might fail.

Now we want to export the animation as .obj frames.
Click at the top-left of the window on "File > Export > Wafefront (.obj)

Now you should be in the export settings of the obj-format.
At the top of the screen, navigate your export path to the "source" directory of the "bao generator" which is included in the downloaded .zip file.

You can clearly see that there are already a few obj-files in the source-directory. I already exported the cube and torus animation for you (in order to show you a pretty nifty feature of the bao generator).

The way the converter/generator works, is as follows:
The .obj-frames of the 3D animations are placed in the "source" directory. The Converter reads the information from the source directory, and creates the .bao files in the "converted" directory.
The neat thing is, that you can have multiple animations in the "source" directory. The converter can distunquish between them and create individual .bao files for each animation.

Now, before you hit the export button, we need to configure the obj-exporter:

The only option which is optional, is the "Selection Only" option. (This option exports only the models, which were selected in the scene.)
> You can select a 3D model by clicking on it with the right mouse button (in the 3D view)

The 3 really important points are: Animation, Triangulate faces, Keep Vertex Order.
The conversion process from .obj to .bao is going to fail completely if these 3 options aren't enabled.
Also, change the "Forward" and "Up" axis to "Y Forward" and "Z Up" (as shown on the screen)
After that, you can click on the "Export Obj" button at the top right of the screen.

Now, before you start the converter, make sure that the "converted" directory of the "bao generator" is completely empty.
Now click on "bao_generator.bat".

Note: JAVA has to be installed in order to run the converter!

Wait until the converter displays " *finished* " or until the window is closed automatically.
After that, you can open the "converted" directory in which the final ".bao" files are located.

Now you can see that there are 3 different .bao files located in this directory, as we exported 3 different animations in the source-directory.
So you can convert multiple animations at once. Just make sure that every animation has an individual export-name. (To avoid name-collisions)

That's the whole conversion process. Sounds like a lot, but in reality it's really fast.

Importing animations into GM: Studio

Now, after converting your .obj-frames to .bao files, we want to import them into GM: Studio.
You can do this by dragging the files from the browser into the "included files" section of GMs IDE.
Import the .gmz file (which is provided in the download with the bao generator) into GM: Studio in order to see a working example, and look into the included files section:

You can see that those 3 files are already imported into GM: Studio. (In order to maintain a working example.)
So if you want to add custom animations, convert them to .bao files and drag them here into the IDE.

Now, this example is fully functional. So run it and play around with it. The important code is placed in the "obj_model" object.

I'm not going over the entire example file (as the code is mostly documented). Instead, i will highlight the important parts of the example, which will be needed if you want to implement it into your own project.

Note: If you want to use the following scripts in your own project, you need to import the "bao_scripts.gml" which is included in the download! (They are already imported in the example.)
Also don't forget to copy the "sh_blend" shader from the example over to your own project.

Now, let's look into the create event of the "obj_model" object.

The Create Event

The most important parts are:

scr_create_animation_vertex_format();//create animation format.
This is the first script which you need. Call it anyhwere in the project. (Like at the game start) in order to initialize the vertex format.

After that, you can load your animations:
//load multiple animations
animation_glass = scr_load_animation("./glass_animation.bao",true); //first argument: file-path, second argument: activate animation-looping.
animation_torus = scr_load_animation("./torus_explode.bao",false);//this animation isn't looped
animation_cube_deform = scr_load_animation("./cube_deform.bao",true);//this animation is looped
You can see that we load 3 different animations. The first argument (argument0) is the path to the 3D model. The second argument (which can be true or false) loops the animation.
true > animation is looped (smooth transition between last and first frame)
false > animation isn't looped (no smooth transition between last and first frame.)

Also create two variables, which will hold the animation progress.
The first variable (animation_length) will hold the length of the animation in steps.
The second variable (animation_step) will hold the current animation progress. (This variable will be increase by the value 1 each step.)

animation_length = room_speed*7; //specify the animation length for 1 animation loop (room_speed*4 = 4 seconds)
animation_step = 0;//this variable is holding the current animation progress (leave it to 0)
Then we need to initialize our shader variables:

//------------------------------------shader variables--------------------------------

//interpolation value
global.shader_interpol = shader_get_uniform(sh_blend, "u_interpol"); //make it global, so that the "scr_draw_animation_frame" script can call it from anywhere in the project
//WARNING! This variable must be named "global.shader_interpol"! Don't change the name unless you want to edit the variable name in the scripts!

//define light uniforms
shader_ambientColor = shader_get_uniform(sh_blend, "uAmbientColor"); //ambient light (color)
shader_lightDirection = shader_get_uniform(sh_blend, "uLightingDirection"); //direction of directional light
shader_lightDirectionalColor = shader_get_uniform(sh_blend, "uDirectionalColor"); //color of directional light

//world matrix for normal-calculation
shader_worldMatTranspInv = shader_get_uniform(sh_blend,"uMatWorldTransInv");
Then we can look into the draw-event of the same object.

The Draw Event


//set lighting

//pass modified world-matrix to the shader for normal-calculation
var worldMat = scr_TranspoInverse(matrix_get(matrix_world));

//NOTE!!!! "texture_set_interpolation(true);"  must be enabled in order to display textures correctly!

//draw frame

We enable the shader, set the uniform varaibles for lighting and draw the 3D model.

Now let's take a closer look at the "scr_draw_animation_frame()" call:
The first parameter, is the pointer to the loaded 3D animation. (in this example, it can be either "animation_glass","animation_torus" or "animation_cube_deform".)
The second parameter is the current animation progress (which needs to be between 0 and the value of "animation_length") while the third parameter is the overall length of the animation (in steps)
The animation_step variable starts at 0, and will be increased each step by the value 1 until it reaches "animation_length". (We wil get back to it in a moment.)
The last parameter is the pointer to a texture which we want to use on the 3D animations.

Now, in order to animate the object we need to increase the animation_step variable in the step event:

The Step Event
//---- animate
if (animation_step>=animation_length){
animation_step = 0;
Each step we will increase the value by one. if it reaches the animation_length value, it resets to 0. We have created an animation loop.

And that's all. Nothing more, nothing less.
Last edited:


Could you please give a simple example just like one animation with simple floor, and with the comments :D that'll help allot
EDIT: i cant open the link, please help!