Index

Animation

Trial includes support for describing animations – meaning how properties aught to evolve over time. These systems can be used to animate individual properties of objects or full skeleton rigs. The constructs on offer for this are fairly standard, allowing import from model files, including skinning and shape keys/morphs.

The relevant concepts for animation are as follows:

When loading stuff from a model file you will not have to worry about any of the underlying stuff. All you need is an animated-entity and an asset to load the model with. From there you can just play, fade-to, and add-layer/remove-layer to manage your animations on the entities.

Animated-Assets

In order to use the animation pipeline from model files, the model format system needs to provide a subclass of the animation-asset. Currently the only format that supports this is the trial-gltf importer.

Using one should be as simple as this:

(define-asset (workbench model) model-file
    #p"model.gltf")

(make-instance 'animated-entity :animation-asset (asset 'workbench 'model))

Once loaded the asset must contain a skeleton, a hash table of clips, and a hash table of meshes. The animated-entity will automatically extract and reference the properties as needed when you play, fade-to, etc.

Trial will also parse out the following extra animation properties from the model, if supported:

Defining Clips

Aside from the fully automated import of animations and skins from model files as used with the animated-entity, you can also programmatically define animation clips to animate other properties and features.

To do so, use define-clip which has the following general structure:

(define-clip sandstorm
       (strength speed)
  0.0   0.0      (vec 0.0 0.0)
  1.0   0.8      (vec 0.2 0.0)
  1.5   _        (vec 1.0 0.0)
  2.0   1.0      _)

Wherein _ is used to omit a track from a keyframe. By default track interpolations are set to :linear. If you specify :hermite or :cubic for a track, you must wrap each keyframe value in a list to pass the extra values needed for the interpolation handles.

Once a clip is defined, you can retrieve it by its name via clip and use sample to apply the track's effects to an object that contains the properties to be animated.

Note that in general sample is modifying, meaning it updates the values in place to avoid producing garbage.

Animation clips are only meant for tweening properties. There is no support for evaluating functions at certain times or running other more complex code. If you need a system to put together sequences of actions and other such changes, please have a look at action-lists. They are trivial to integrate with Trial and are geared for that use.

Morphs / Shape Keys

In addition to rigged animation, Trial also allows animation or deformation based on shape keys. In order to make use of morphs, you should use the morphed-entity. This entity holds a vector that adds the respective morph-group and data texture to each of the vertex-arrays of the entity.

When rendering a vertex-array, you must use the same index of the vertex-array to retrieve the respective morph data cell in the morphs vector. The car holds the morph-group of which you should bind the morph-data buffer to the shader-program, and the cdr holds the texture that you should bind to the morph_targets uniform. The basic-animated-entity takes care of all of this logic for you.

In order to control the weights, you can setf the respective entry in the morph-groups weights array. All weights should be single-floats in the range [0,1]. After setting them, you should update-morph-data to refresh the buffer.

Weights can also be animated via a weights-track. When setting its frames, it expects a values array that is longer than the times array. Frame values for the different morph targets are stored interleaved, meaning for instance for a linear interpolation track with two morph targets, the values should be

frame0_morph0 frame0_morph1 frame1_morph0 frame1_morph1 ...

When sampling from the track you should either pass an array of weights to animate, or a pose in which case the weights array corresponding to the track's weights in the pose is updated. In either case the array must be present and must match in length to the number of morph targets that the track expects. Usually you'll want to use the weights array of the corresponding morph-group instance.