HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Object Transform Model

Object Transform Model

In the simplest form, the world transform of an object is given by

W = L * W0

where L is the object local transform built from its transform parameters, and W0 is the world transform of the object's input node.

Unfortunately, this simple transform formula is not sufficient to capture various transformation possibilities offered by object nodes. For example, an object node can specify a "look at" point that determines the rotation of the object, and L denotes only the transform parameters, so there is no matrix in that formula that captures the "look at" option.

The "look at" is not the only type of transform that occurs in object nodes. The node may also specify the path object on which it sits, or it may wish to blend several parents together, as is the case for OBJ_Blend node. Another complicating factor is that the blend object requires the idea of an pre-transform that depends on its inputs. The Follow Path object parameter, on the other hand, requires the idea of a pre-transform which does not depend on its inputs (i.e., is input-independent).

One final requirement to take into account is a clean animation of transform parameters. It is often desirable to set a natural orientation of the object for animation. For example, we may wish to animate the x rotation parameter so that a value of 0 corresponds to 45 degrees in reality. This allows animators to work with much more meaningful channel data.

The solution to this is to add an additional pre-transform matrix to the transform model just before the local transformation. This also has the added advantage that now we can perform transform operations on objects without affecting their animation channels by modifying the pre-transform matrix instead. It is a big bonus for things like mirroring and transforming bone objects while preserving existing animation. The disadvantage to this however is that all expressions in objects that tried to maintain a sort of relative position or orientation to an input object must now take into account the pre-transform. For orthogonality, we also provide an output transform for manipulating the transform intended for child objects only.

Taking all these matrices together, the final formula for the world transform is:

W = K * L * P * Ii * Id * T0 * W0

where:

  • W is the world transform of the object (used for its SOPs and output objects)
  • K is the look-at rotation matrix
  • P is the pre-transform matrix (modified by the items in the Pre-Transform menu of the object parameters).
  • Ii is the input-independent pre-transform matrix.
  • Id is the input-dependent pre-transform matrix.
  • T0 is the output transform of its connected input object.
  • W0 is the world transform of its connected input object.

Virtual Methods

To use the model described above, we have the following overridable virtual methods in OBJ_Node:

  • OBJ_Node::buildLookAt()
    Returns K, the transform needed to aim the object towards the look-at target. By default it builds a rotation matrix and returns true to indicate that the look-at matrix needs to be taken into consideration.
  • OBJ_Node::getParmTransform()
    Returns L, the local transform formed from the object's transform parameters.
  • OBJ_Node::applyInputDependentTransform()
    Computes and pre-multiplies Id to the the given matrix. This transform is defined as semantically dependent on the object's inputs. The OBJ_Blend object overrides this to do special processing. Also, the OBJ_Bone object overrides this to move its origin to the tip of the parent bone. By default, this method does not modify the given matrix, which is equivalent to identity Id matrix.
  • OBJ_Node::applyInputIndependentTransform()
    Computes and pre-multiplies Ii to the the given matrix. This transform is defined as semantically independent of the object's inputs. The default implementation applies a follow path object transform if available.
  • OBJ_Node::applyPreTransform()
    Computes and pre-multiplies P to the the given matrix. The default implementation checks to see if it should use the pre-transform before actually pre-multiplying.
  • OBJ_Node::applyOutputTransform()
    Computes and pre-multiplies T0 to the given matrix. By default, this method does not modify the given matrix, which is equivalent to identity T0 matrix.
Note
Unlike other node contexts, you need to call flags().setTimeDep(true) in these method overrides if you compute a value based on the evaluation time explicitly. This is because objects break down the computations into smaller pieces than the entire cookMyObj() process.

There are also a few non-virtual convenience methods that return some combination or accumulation of the object transform model matrices.

  • OBJ_Node::getLocalTransform()
    Returns K*L*P*Ii, the effective local transform which does not depend on its inputs. This function cooks the object first and then returns the member variable OBJ_Node::myXform (ie, myXform=K*L*P*Ii).
  • OBJ_Node::getParentToWorldTransform()
    Returns Id*T0*W0, the effective world transform of the parent. In terms of methods this is calculated by
    applyInputDependentTransform(
    getParentObject()->getChildToWorldTransform())
    If getParentObject() is NULL, then it simply returns applyInputDependentTransform(identity_matrix). Use this function to convert co-ordinates that have not been transformed by the follow path or pre-transform. Typically, this function is only used internally for manipulating the pre-transform. If the object is in a subnet, then this will include the subnet's world transform.
  • OBJ_Node::getObjectToWorldTransform()
    Returns P*Ii*Id*T0*W0, which is the transform that is only missing the local and look-at components. In terms of methods this is calculated by
    applyPreTransform(
    applyInputIndependentTransform(
    getParentToWorldTransform()))
    Use this function to convert co-ordinates from object space to world space. Object space includes everything except the object's local transform parameters. Typically, this function is used by handles to place themselves relative to the origin of the object.
  • OBJ_Node::getLocalToWorldTransform()
    Returns W, which is the full world transform of the object including all the component sub-matrices. This is equivalent to
    getLocalTransform() * getParentToWorldTransform()
    Use this function to convert co-ordinates from the local (geometry) space to world space. It cooks the object first and returns the member variable myWorldTransform. Typically, this function is used to place child SOPs of the object in world space.
  • OBJ_Node::getChildToWorldTransform()
    Returns T0*W0, which are the transforms provided by the object's parent. This is equivalent to
    applyOutputTransform(
    getLocalToWorldTransform())
    Typically, this function is only used internally for the implementation of getParentToWorldTransform().

    Note that in general, the following inequality holds, unless applyInputDependentTransform() has no effect:
    getParentToWorldTransform() != getParentObject()->getChildToWorldTransform()
  • OBJ_Node::getPreLocalTransform()
    Returns P*Id which is the portion of the local transform that does not depend on the parameters (transform or look-at). It is not widely used and only relevant for the handles to orient themselves in parent space.

Object Cooking

The generic OP_Node::cook() method checks if the node has been dirtied by a modified parameter or any other change within dependency chains, and if the node needs to cook it invokes OP_Node::cookMe(). This method is overloaded in OBJ_Node::cookMe() and it checks if there is already a computed and cached matrix available in the global cache. If the matrix is available, it is returned, and if it is not, then OBJ_Node::cookMyObj() method is invoked to perform the calculation of the protected members OBJ_Node::myXform and OBJ_Node::myWorldXform. These member data are later used in calls that query the local and world transforms. In addition, if caching of the node's matrix is enabled (via a node parameter, "caching") then OBJ_Node::cookMe() stores the calculated matrices in the cache. Below is a simplified skeleton of the OBJ_Node::cookMyObj() method.

{
UT_DMatrix4 pmat, parm_xform, lookat;
if (lockInputs(context) >= UT_ERROR_ABORT)
return error();
getParentToWorldTransform(context, pmat);
myXform.identity();
applyInputIndependentTransform(context, myXform);
applyPreTransform(context, myXform);
getParmTransform(context, parm_xform);
myXform = parm_xform * myXform;
// Check for expression errors during pipeline processing
goto quit;
myWorldXform = myXform * pmat;
if (buildLookAt(context, myWorldXform, lookat))
{
myXform = lookat * myXform;
myWorldXform = lookat * myWorldXform;
}
myInverseDirty = 1;
quit:
return error();
}

The primary purpose of the cookMyObj() method is to compute the OBJ_Node::myXform and OBJ_Node::myWorldXform members. However, when implementing a new object, instead of explicitly calculating these matrices in own version of cookMyObj() method, it may suffice to override some of the following virtual methods:

Note, that during cooking, the inverse, myIWorldXform, is not computed, but rather it is dirtied. The method OBJ_Node::getInverseXform() will compute and return the inverse of the OBJ_Node::myWorldXform when it is called the next time.

Pre-transform Support

To support the use of the pre-transform (P) matrix, there are several utility methods that manipulate that transformation.

  • OBJ_Node::resetPreTransform()
    This sets the pre-transform to the identity matrix. This will modify the object's world transform unless the pre-transform was already the identity.
  • OBJ_Node::transferLocalToPreTransform()
    This transfers the object's transform parameters (L) to the pre-transform (P). This does not modify the effective world transform.
  • OBJ_Node::transferPreToLocalTransform()
    This extracts the object's pre-transform into its transform parameters. If the extraction involved shears, then only the non-sheared portion is extracted. This does not modify the effective world transform.
  • OBJ_Node::setPreTransform()
    This sets the pretransform to the given matrix. This will modify the object's effective world transform unless the given pre-transform is identity.
  • OBJ_Node::getPreTransform()
    This returns the pretransform matrix.
  • OBJ_Node::keepWorldTransform()
    This function is used to set the world transform of an object via manipulation of the pre-transform.

Note that it is crucial that L and P is always next together in the transformation model (i.e., L*P) without any intervening transformations in order for the above functions to work.