Chapter 1. Realsoft 3D Choreography System

Realsoft 3D Animation System is based on choreographies. One or more choreographs can be associated with an object. When any of the animation parameters is changed, the object's choreography stack is executed. Each choreography modifies the object from the state the previous choreography left it.

Animateable Attributes

Attributes that can be managed by the animation system must be declared as public and tagged with R3TNF_ANIMATEABLE flag. Whenever the attribute is changed the class should call:


    animattr = R3DoA2(obj, R3TTM_NEWANIMTAG, attrid, attrindex);

Where the attrid and attrindex specify the attribute to be animated. Attrindex must be -1 for regular attributes.

This will create and return a handle to the animated attribute. Note: this handle is not an object.

Choreography stack

Choreography stack can consists of any number of choreographs that are indentified by the following attributes:


            0       1       2       3
            init    keyfr   skel    noise
------------------------------------------
Translate   x       x               x
Rotate      x       x
Scale       x       x
Points      x               x       
-------------------------------------------
            null    null    leg     null

Translate, Rotate, Scale and Points attributes are marked animated. Each animated attribute has a list of choreographs identified by their ordnum. The first choreography is responsible for restoring the initial value of an attribute. It is always the first choreography in the choreography stack. The remaining choreographs then modify the initial value.

A choreography may also have an input object that the choreography is sensitive to. For example, bend choreography can modify points based on a nurbs curve.

To create a new choreography for an object:


    R3DoA3(obj, R3TTM_NEWCHOR, clid, chorindex, name);

Binding animated attributes and choreographs

The next step is to create so called choreography object for the given choreography and the attribute.


    chorobj = R3DoA2(obj, R3TTM_NEWCHOROBJ, animtag, chorindex);

Some choreographs need geometric objects, such as skeletons, to define their effect. This is done via input objects.

Associate a choreography with an object, call:


    R3Do(obj, R3TTM_SETKEYFRAMEINPUTOBJECT,
         R3TTA_KiChoreographyIndex, chorindex,
         R3TTA_KiObj, inputobj,
         R3TTA_KiBeginMth, R3CONSTRUCTORM_BEGIN,
         R3TTA_KiMth, R3CONSTRUCTORM_TRANSFORMATTR,
         R3TTA_KiEndMth, R3CONSTRUCTORM_END,
         R3TAG_END);

When the animation system start to execute the given choreography, it first calls the KiBeginMth method of the input object. In this method the input object can read various target attributes and prepare itself for transforming the attributes.

KiMth method is then called once per animated attribute. This method is the actual workhorse. In this method the object modifies the attribute in question. For example, skeleton evaluates its geometry applies the deformation to the target points.

KiEndMth is called to finalize the execution of the choreography.

Note that input objects should be derived from the r3constructor.h base class because there are certain tasks they need to do. They need to support binding and unbinding methods, for example.

About choreography 0

The first choreography (the one with choreography index 0) plays a special role in the system. It is exceptional in that it overwrites the attribute values rather than modifies them. Its job is to manage the initial value of the attribute. The class id of the choreography 0 should therefore be R3CLID_ACASSIGN.

An example

Let us imagine we want to create a simple keyframing animation to animate translate and rotate attributes.

So we need two choreographs, which are both associated with the rotate and translate attributes, as follows:


            0       1     
            init    keyfr 
--------------------------
Translate   x       x     
Rotate      x       x
---------------------------
            null    null 

To animate the attributes, call:


    t = R3DoA2(obj, R3TTM_NEWANIMTAG, R3PRIMA_MatrixTranslate, -1);
    r = R3DoA2(obj, R3TTM_NEWANIMTAG, R3PRIMA_MatrixRotate, -1);

Create two choreographs:


    R3DoA3(obj, R3TTM_NEWCHOR, R3CLID_ACASSIGN, 0, "init");
    R3DoA3(obj, R3TTM_NEWCHOR, R3CLID_ACKEYFR, 1, "keyfr");

Then bind the choreographs to the animated attributes:


    R3DoA2(obj, R3TTM_NEWCHOROBJ, t, 0);
    R3DoA2(obj, R3TTM_NEWCHOROBJ, r, 0);

    R3DoA2(obj, R3TTM_NEWCHOROBJ, t, 1);
    R3DoA2(obj, R3TTM_NEWCHOROBJ, r, 1);

Let's imagine we want to deform R3PRIMA_Points attribute with a radial deformer. Most constructor based objects use r3acpoint choreography object. The job for this choreogrpahy class is to read an attribute of the target object and pass it over to a constructor object (such as the radial deformer).


    deformer = R3New(R3CLID_RADIALDEFORMER, ....
    ...

    /* bind radial deformer to the target object 'obj' */
    p = R3DoA2(obj, R3TTM_NEWANIMTAG, R3PRIMA_Points, -1);
    R3DoA3(obj, R3TTM_NEWCHOR, R3CLID_ACPOINT, 2, "rad.deformer");

    R3Do(obj, R3TTM_SETKEYFRAMEINPUTOBJECT,
         R3TTA_KiChoreographyIndex, 2,
         R3TTA_KiObj, deformer,
         R3TTA_KiBeginMth, R3CONSTRUCTORM_BEGIN,
         R3TTA_KiMth, R3CONSTRUCTORM_TRANSFORMATTR,
         R3TTA_KiEndMth, R3CONSTRUCTORM_END,
         R3TAG_END);

Other useful methods

In the above we described the low level interface to the animation system.

However, in many cases you don't need to use this very low level interface.


    R3Do(target, R3TTM_MANAGETAG,
         R3TTA_AniTag, R3PRIMA_Points,
         R3TTA_AniClid, R3CLID_ACKEYFR,
         R3TTA_AniName, "my keyframer",
         R3TTA_AniForceNewChor, TRUE,
         R3TAG_END);

The above method make the R3PRIMA_Points animated, if not already animated, and creates a keyframer choreography to manage it. It also takes create of creating init choreography if needed.

Note: If R3TTA_AniForceNewChor is FALSE, the method will create a new keyframer only if the current choreography is not a keyframer.


    R3Do(target, R3TTM_MANAGETAG,
         R3TTA_AniTag, R3PRIMA_Points,
         R3TTA_AniClid, R3CLID_ACPOINT,
         R3TTA_AniName, "skeleton",
         R3TTA_KiObj, skeleton,
         R3TTA_KiBeginMth, R3CONSTRUCTORM_BEGIN,
         R3TTA_KiMth, R3CONSTRUCTORM_TRANSFORMATTR,
         R3TTA_KiEndMth, R3CONSTRUCTORM_END,
         R3TTA_AniForceNewChor, TRUE,
         R3TAG_END);

The above method makes R3PRIMA_Points animateable and creates a new R3CLID_ACPOINT choreography object to manage the attribute. The method also links the choreography object to 'skeleton' object. The method also creates the init choreography for the attribute when needed.

To find out whether a certain attribute is already animated, call:


    animtag = RDoA2(obj, R3TTM_TAGISANIMATED, tag, index);

If the tag is animated, the method returns the descriptor to the animated attribute.

To find out whether there is a choreography object managing certain attribute, call:


   managed = R3Do(obj, R3TTM_ISMANAGED, 
                  R3TTA_AniTag, attrrid,
                  R3TTA_AniTagIndex, attrindex,
                  R3TTA_KiChoreographyIndex, chorindex,
                  R3TAG_END);

The above method returns true if the attribute specified by R3TTA_AniTag and R3TTA_AniTagIndex is managed by choreography 'chorindex'.

It is also possible to pass R3TTA_KiObj, R3TTA_AniClid tags to specify input object and choreography class id.

For example, to find out whether certain attribute is managed by a certain choreography class (for example, to find out whether R3PRIMA_Points is key framed, call):


   managed = R3Do(obj, R3TTM_ISMANAGED, 
                  R3TTA_AniTag, R3PRIMA_Points,
                  R3TTA_AniClid, R3CLID_ACKEYFR
                  R3TAG_END);

To find out whether there is a skeleton managing R3PRIMA_Points attribute:


   managed = R3Do(obj, R3TTM_ISMANAGED, 
                  R3TTA_AniTag, R3PRIMA_Points,
                  R3TTA_KiObj, skeleton,
                  R3TAG_END);

To unbind a choreography 1 from an animated attribute, call:


    if(aniobj = R3DoA2(obj, R3TTM_TAGISANIMATED, R3PRIMA_Points, -1))
        R3DoA2(obj, R3TTM_DELCHOROBJ, aniobj, 1);

Supporting Animation System

Class implementors should support the animation system for all classes derived from the r3ttag.h base class. For example, geometric objects, VSL objects, materials, post effects and image effects are derived from this base class.

To support animation recording interface, you have to expose the attribute as 'public' with R3TN_ANIMATEABLE option:


    static R3TAGNAME sphere_tags[] = {
        {"Foo", MYA_Foo, R3TID_FLOAT, R3TN_ANIMATEABLE, },
        {NULL,}

An animateable attributes must be handled as follows by model objects:


    R3Do(obj, R3TTM_PREPANIMTAG,
         R3TTA_AniTag, MYA_Quality,
         R3TTA_AniTagType, R3TID_INTEGER,
         R3TAG_END);
    self->quality = ...;
    R3DoA(obj, R3TTM_CHANGEANIMATEDTAGVALUE, (void*)MYA_Quality;

Typically the above code is implemented in R3RM_SET methods, but any method that changes an animated attribute must implement it.

The direct consequence of the above code is that the user will be able to key frame the attribute in animation recording mode. The program automatically creates a keyframer or pose choreography to manage the modified attribute.

Supporting HPB and Quaternion Angles

If you have implemented geometric objects which define 3D angles, you can choose whether the attribute is interpolated in Quaternion space or in HPB (Euler) space.

There is a sub type R3TNS_QUATERNIONANGLE for R3TAGNAME structure.

To make attribute 'Quaternion' interpolated:


    {"QAngle", MYA_QAngle, R3TID_VECTOR, R3TN_ANIMATEABLE, 0,0,0,R3TNS_QUATERNIONANGLE},

HPB interpolation can be achieved:

    {"HPBAngle", MYA_HPBAngle, R3TID_VECTOR, R3TN_ANIMATEABLE, 0,0,0,R3TNS_ANGLE},
[Note] Note
You can support the both interfaces and let the user to decide which one to use.