Object Animation Textures - GhislainGir/BlenderGameToolsDoc GitHub Wiki
Object Animation Texture (OAT) is a technique very similar to Bone Animation Texture (BAT), and to a lesser extent, Vertex Animation Texture (VAT). OAT is based on a single key assumption that opens the door to a new way of thinking: vertices belonging to the same object move in a similar way.
Instead of baking offset and normal data for each vertex at every frame—which would result in a lot of duplicated data if the assumption holds true—OAT bakes the transform of the object itself. All vertices of a given mesh point to the same texel, and VAT-style UV scrolling can be used to play back the object animation. This approach naturally supports multiple objects, just as VAT supports multiple vertices.
Conceptually, this is similar to BAT, as if all vertices were skinned to a single bone—like a rigid, mechanical mesh. OAT removes the need for an armature, bones, and the indirection of sampling a skinning texture to locate the correct texel storing a bone transform. Instead, the object's transforms are baked directly into a texel within one or more textures, and all mesh vertices directly reference this texel via their UVs.
Multiple textures can be used to store position, rotation, scale, and other data. Reconstructing the object’s animation is conceptually similar to reconstructing skeletal animation using BAT. The object must first be rotated (and possibly scaled) around its pivot point, and then the positional offset is applied. Typically, the first baked frame contains absolute data representing the object’s rest pose in world or local space, while the following frames store relative transforms to move the object into its animated pose.
The main advantage of this technique is that it's both efficient and memory-friendly. Baking 200 frames for 200 objects results in a 200×200 texture which, when using 32-bit floats, can store both position data and rotation data (for example, as a bit-packed quaternion) in just around 160 kilobytes. Playing back the animation only requires two texture samples: one to retrieve the reference position and another to get the position and rotation for the current frame—making it very lightweight to perform.
The downside of this technique is its rigidity. It can only capture motion at the object level. Since only a single data point is baked per object, the objects themselves cannot be deformed.
The VAT and BAT articles provide more detailed information, along with important considerations and potential pitfalls to be aware of.
The process of baking Object Animation Textures should be really straightforward for most use cases. But before we dive in, let’s quickly review the common options and panels.
A panel called 'Frames' lets you customize how the frame range and reference frame are set.
- The NLA mode allows the tool to automatically derive the animation range based on the NLA tracks of the object(s).
- The Scene mode uses the scene’s current frame settings: start, end, and step.
- The Custom mode lets you specify a custom animation range and step.
Not much needs to be said about the Scene and Custom modes, but the NLA mode offers additional features and comes with extra constraints.
When using the NLA mode, objects must all have an NLA track, which in turn must contain NLA strips.
The NLA mode also allows you to add padding in between clips. It also allows you to specify a list of NLA clips to exclude when determining the frame range. This can be useful when providing a specific frame of reference (base pose) using a NLA clip, clip that would otherwise need to be excluded from the bake.
Also, when using the NLA mode, the step setting may be applied differently:
- Global: Bakes every nth frame across the entire animation
- NLA Clip: Bakes every nth frame, starting from the start frame of each clip.
Important
Clips that are looping should likely be clipped to remove the duplicated start and end frames. It’s also recommended to separate each clip by one frame; otherwise, the end frame of one clip will be replaced by the start frame of the next. The gif below illustrates this process using an armature—irrelevant for OATs—but the same principle applies to objects.
Note
NLA tracks will be scanned regardless of the mode used in order to extract animation information that may be useful when baking a OAT for use in a state machine. It may be important to know when each NLA clip starts and ends, etc. Note that their start/end range will be clamped to the specified start/end. This has no impact on the bake whatsoever and is purely informative.
Important
You may adjust Blender's scene frame rate settings and scale your animations to baking smoother animations (at the cost of baking more frames, resulting in heavier OATs).
A panel called 'Textures' lets you customize several options related to generating and exporting the object animation texture(s).
- Max Width: Maximum allowed texture width. Exceeding this may cancel the bake.
- Max Height: Maximum allowed texture height. Exceeding this may cancel the bake.
- Power of Two: Force textures to be power-of-two sizes. Not recommended, as non-power-of-two textures ensure tight packing and are widely supported
- Square: Force texture width and height to be equal if 'Power of Two' is enabled. Typically unnecessary, but provided as an option for specific use cases
- Mode: Control how frames are arranged in the texture when there's extra space (underflow) or not enough space (overflow). Underflow occurs when the number of objects per frame is less than the image width, causing gaps at the end of the line ('Power of Two' might cause this). Overflow happens when there are too many objects for a single line, and the data is spread across multiple lines, possibly leaving gaps. This setting determines how to handle these empty spaces
Textures can be added to or removed from a list, with each texture requiring a unique name (this includes textures listed in the Animation Data panel—more on that below).
A panel called 'Export', located in the 'Textures' panel, lets you customize how the generated textures (skinning & animation) are exported, if exported at all.
-
Export: Enable to export the generated textures to an EXR file upon bake completion
- Export|Filename: Name for the texture file (without the .exr extension). is a placeholder tag that can be used to be replaced with the object's name. is a placeholder tag that can be used to be replaced with the texture's custom name
-
Export|Path: Texture file path, excluding the file name. The path is relative to the Blender file if saved, or absolute otherwise
- Export|Advanced|Override: Enable to override any existing .exr file
A panel called 'Mesh' lets you customize several options related to generating & exporting the baked mesh.
- Origin: Optional object to use as the baking origin instead of the world origin. It takes into account the object's location, rotation, and scale, which may lead to unexpected results. For this reason, it's considered experimental, but it might be useful in rare cases
- Scale: Scale factor for the baked offsets/positions. This compensates for Blender's default unit (1 meter) and aligns with the target application's unit system. A default factor of 100 is used to convert from meters to centimeters, Unreal's default unit
- Invert X/Y/Z: Invert the world X/Y/Z axis (Y set to True for Unreal Engine compatibility)
- Name: Name of the baked object
- Materials: Enable to copy materials
-
Property: Custom property name for the custom source feature (to query data from an object targeted using a data-block custom property in an object)
- Previz|Anim: Enable to add a geometry node modifier to the baked mesh for previewing baked offsets and normals after bake completion (DISABLED for now)
- Previz|Bounds: Enable to display the animation bounds after bake completion
- UV|Name: Name of the UVMap to be created or used for baking mesh UVs
- UV|Invert V: Invert the V axis of the UVMap and flip the OAT texture(s) upside down. Typically True for exporting to Unreal Engine or DirectX apps, False for Unity or OpenGL apps
-
Export: Enable to export the generated mesh to an FBX file upon bake completion
- Export|Name: Name for the exported FBX file (without the .fbx extension). is a placeholder tag that can be used to be replaced with the object's name
- Export|Path: File path for the exported FBX, excluding the file name. The path is relative to the Blender file if saved, or absolute otherwise
- Export|Advanced|Override: Enable to override any existing .fbx file
A panel called 'XML' lets you customize the export options for the XML file.
-
Export: Enable to export an XML file containing information about the bake process (recommended)
-
Export|Mode: Select how the XML file name and path are generated
- Mesh Path: Use the same FBX file name and path for the XML file. Defaults to 'Custom' if mesh is not exported
- Custom Path: Specify a custom XML file name and path
- Export|Filename: Name for the exported XML file (without the .xml extension)
- Export|Path: Path for the exported XML file, excluding the file name. The path is relative to the Blender file if saved, or absolute otherwise
- Export|Override: Enable to override any existing .xml file
-
Export|Mode: Select how the XML file name and path are generated
The first step is to list the transforms you want to bake from the objects. Simply click the '+' icon in the Textures panel to create a new texture.
Important
Each texture must have a unique name! You can rename a texture by double-clicking it or using the exposed 'Name' property.
Next, configure what to store in the red channel. We'll assume most users will follow the common OAT approach: storing XYZ position in the RGB channels of one texture, and axis-angle rotation in the RGBA channels of a second texture. Start by setting the red channel to Position to store the X component, then set the green channel to Position for the Y component, and the blue channel to Position for the Z component.
Repeat the process for the objects' rotation, storing the axis and angle in a second texture.
Most data types can be remapped to the [0:1] range using the 'Remap' feature, allowing them to be stored in 8-bit textures—provided you're aware of the limitations that come with lower precision. When reading this data back from the texture, it must be remapped to its original [min:max] range using the reported offset and range values.
The tool also allows you to swizzle world axes to account for coordinate system differences between Blender and your target application.
As a final step, review the settings in the Mesh panel. You may need to invert certain world axes to match your target application, or flip the UV's V axis.
Next, set up your simulation however you like. I’ll keep it simple—with a stack of cubes falling onto a plane.
Once everything is set up, specify the frame range—either using a custom range or the scene range—click Bake, and voilà!
Baking an animation is just as simple. Set up the textures the same way as described in the Simulation section. Then, animate your objects using keyframes, and make sure each one has an NLA track. Voilà, the animation are ready to be baked!
The bake should generate a new mesh object, with the object animation image(s) saved within the Blender file.
It's a good idea to check the generated mesh's vertex-to-texel UV map. Each vertex should be centered on a unique texel in the first row of pixels, and the number of rows should match the configuration used for the bake.
This feature has not been implemented yet, but it’s expected to eventually allow you to visualize the bake directly in Blender.
Simply import the .fbx into your game engine. In Unreal Engine, you may want to check the following import options.
Note
If it wasn’t automatically exported, you’ll need to manually export the generated mesh to .fbx. Blender’s default export settings should work fine.
You'll also probably want to tweak the mesh's bounds using the info stated in the 'report' panel and/or XML file.
Important
Special care must be taken when reading the offset value, as it depends on the 'Scale' bake setting and how the Blender scene units are configured. Most game engines, including Unreal Engine, provide ways to visualize a mesh's bounds, so it’s usually quite clear if a multiplier needs to be applied when setting the bounds offset.
Simply import the .exr textures into your game engine.
Each may need to be compressed in different formats:
- Uncompressed 8-bit RGBA: Unlikely, but possible if data was remapped to the [0:1] range and you're aware of the limitations of working within the 8-bit range.
- Uncompressed 16-bit HDR: Likely the best choice for most use cases. This however exclude texture(s) storing an XYZW quaternion bit-packed into a single 32-bit float.
- Uncompressed 32-bit HDR: BATs are unlikely to require 32-bit HDR textures, unless the texture stores an XYZW quaternion bit-packed into a single float. Most object-related data—such as position, rotation, and scale—can typically be represented accurately enough with 16-bit precision.
If you're packing object data across multiple rows or using the experimental 'Continuous' mode, Nearest sampling is required to prevent pixel interpolation. This may also apply even when stacking single rows of data, as some types—such as axis and angle, or quaternions (especially when bit-packed)—cannot be safely interpolated.
Note
If the textures weren’t automatically exported, you’ll need to manually export them as .exr using the following settings.
Once a bake is attempted, a report panel will appear in the 'OAT Baker' main panel, providing valuable insights into the baked information.
- Export: Exports the report to an XML file following the XML export settings.
- Clear: Clears the report. This may be useful as the report holds pointers to the baked object(s), which will make them persist in the Blender file even though they are later deleted.
This panel provides global information about the bake that won’t be covered in detail in this documentation to avoid unnecessary clutter, as most of the information should be self-explanatory. Just make sure you’re aware of the coordinate system used by both Blender and your target application, so you can account for differences in world axes, UV orientation, and world scale.
For data that has been remapped to the [0:1] range for potential storage in 8-bit RGBA texture(s), it must be converted back to its original [-min:max] range using the reported 'offset' and 'range' values:
The process of reconstructing the baked animation in the vertex shader follows the same core principles described in the BAT article, so this section will be brief.
We'll assume the following: the object's XYZ positions are baked into the RGB channels of the first texture, and their rotations are baked as an axis-angle representation into the RGB/A channels of a second texture.
The idea is to sample each object's position in its rest pose, which is encoded in the very first frame of the position texture. Then, the rotation texture is sampled at the desired frame to rotate the object around its rest position. After that, the translation offset is applied by sampling the position texture again at the desired frame. The same process can be used to correct normals, but in this case, the rotation must be performed around (0, 0, 0), so the reference position isn't needed.
The report can be exported to XML, which may potentially be used to automate the import process in a game engine through a custom importer that parses this file.
Many things can go south with OATs. Here are some things to look out for.
- Double-check that the differences between Blender’s coordinate systems and those of your target application have been properly accounted for:
- Blender’s world axes are +X+Y+Z, while Unreal Engine’s world axes are +X-Y+Z.
- Blender uses OpenGL, meaning UV(0.0) is at the bottom-left corner, whereas Unreal Engine uses DirectX, where UV(0,0) is at the top-left corner.
- Blender’s default unit is 1 meter (though it can be adjusted), while Unreal Engine’s default unit is 1 centimeter, so a scale factor may need to be applied.
- Try 32-bit UVs in your targeted application if you think the issue you experience is related to 16-bit UVs imprecision.
- Double check the texture compression settings in your targeted application. Try uncompressed 32-bit HDR textures first, then see if 16-bit HDR textures lead to no visible differences. RGBA8 may be an option if you know what you're doing.
- Double check the texture sampling settings in your targeted application. Nearest sampling is almost always recommended if not mandatory. Use linear pixel interpolation at your own risk.
- Double check the generated mesh OAT UVs are laid out as expected and that you do sample the OAT texture according to the packing mode. Single-row-per-frame or Multiple-row-per-frame. Continuous packing mode is considered experimental, use at your own risk.
- Ensure OAT UVs are not overriden in your targeted application by lightmap UVs or any other process automated during mesh import.
- Disable any geometry-related optimization features in your game engine initially. This includes virtualized geometry systems like UE's Nanite, which may need to be deactivated both project-wide and on a per-asset basis.
The OAT tool's configuration and settings can be saved as a preset, which can be easily applied with a single click at any time. Presets can be added or removed using the button located to the right of the OAT tool header.
This feature uses Blender's internal preset system, which creates Python files to store all the necessary data. These files are stored in the following directories:
- Windows: C:\Users\<your username>\AppData\Roaming\Blender Foundation\Blender\<version number>\scripts\presets\operator\databaker_oat\
- MAC: Library\Application Support\Blender\<version number>\scripts\presets\operator\databaker_oat\
- Linux: ~/.config/blender/<version number>/scripts/presets/operator/databaker_oat/
Warning
Preset .py files can be copied, pasted, and shared across different computers. However, only install presets from trusted sources, as these files can execute malicious Python code on your machine.