User Guide > Rigging > Master Controller > Applying Widget Coordinates to Deformers

Applying the Coordinates of a Master Controller Widget to a Deformation Node


It is possible to use Master Controller widgets to apply transformations to deformers. However, before attempting that, it is important to understand the difference between deformations that have absolute attributes and the ones that have relative attributes.

When using the Rigging tool to create Bone, Game Bone and Curve transformation chains, Harmony creates each deformation node with the attribute "Apply Parent Transformation" enabled. This attribute makes it so each point is deformed by their parent—the previous point in the chain—before they apply their own deformations to the drawing. This makes manipulating these types of deformation points difficult as their deformation matrices are not fully reversible.

Hence, using a Master Controller widget to manipulate deformers is more easily achieved with Envelope type deformations. The Rigging tool creates Curve deformation nodes with the attribute "Apply Parent Transformation" disabled. This means envelope deformation points are not affected by their parent's deformations. Therefore, the deformation matrix for all the points in a Curve deformation chain is the same, that is, the one carried by the parent of the chain's offset, which would typically be a peg or a drawing node, and would hence be fully reversible. The process then becomes similar to when a widget is used to manipulate pegs or elements.

TIP: If you want to use the Master Controller to control deformation nodes that are relative to other deformation nodes, you can do so using the getDeformationComponentMatrix method of the DeformationUtils namespace. For more information, refer to the Harmony scripting reference–see Scripting Reference.

With the inverse matrix of a deformation node, we can convert world coordinates to a deformation node's coordinates system. However, it is important to understand that curve and envelope deformation nodes are represented by three points: The central point, and the two tangent points defining the secondary points of the Bezier curves. If you plan to change the position of a curve or envelope deformation node, you will also need to modify its tangent positions, with an algorithm such as:

Tangent0 in fields = Previous-Offset + rotation(Vector(length0 * 0.1, 0), orientation0 degrees)
Tangent1 = Offset + rotation(Vector(length1 * 0.1, 0), orientation1 degrees)

In the Harmony scripting interface, this would be expressed like so:

var t0FldU = add(previousDeformationNode.fieldOffset, rotate(Point2d(deformationNode.length0 * 0.1, 0), deformationNode.orientation0)); var t1FldU = add(p1FieldUndeformed, rotate(Point2d(deformationNode.length1 * 0.1, 0), 180 + deformationNode.orientation1));

In this example, the deformationNode and previousDeformationNode are Deformation type objects defined as:

function Deformation (nodePath) { var currentFrame = frame.current(); this.fieldOffset = node.getAttr(nodePath, currentFrame, "offset").pos2dValue(); this.OGLOffset = scene.toOGL(this.fieldOffset); this.nodePath = nodePath; this.nodeType = node.type(nodePath); if(this.nodeType == "OffsetModule") { this.orientation0 = node.getAttr(nodePath, currentFrame, "orientation").doubleValue(); this.orientation1 = 0; this.length0 = 0; this.length1 = 0; } if(this.nodeType == "CurveModule") { this.orientation0 = node.getAttr(nodePath, currentFrame, "orientation0").doubleValue(); this.orientation1 = node.getAttr(nodePath, currentFrame, "orientation1").doubleValue(); this.length0 = node.getAttr(nodePath, currentFrame, "length0").doubleValue(); this.length1 = node.getAttr(nodePath, currentFrame, "length1").doubleValue(); } }

In the following example, we use a Point2dWidget to move the position of envelope deformation nodes towards it proportionally to the inverse distance between those two points. As the user drags the widget around the stage, the deformation points will inch towards it:

This is the node system for this example:

First, we calculate the position of the Point2dWidget in the coordinates system of each parent peg of each deformation group in the scene.

// Get the deformation chains getDeformationChains(leftmostParent, deformationChains); for(var i=0; i<deformationChains.length; ++i) { // The first node of a deformation chain is always an Offset in our case. // Convert the position from world coordinates to the target peg // model coordinates. // Apply the inverse of the peg parent node transformation to the // world position to go from world coordinates to (the peg) model // coordinates var pegParentNode = node.srcNode(node.parentNode(deformationChains[i][0].nodePath), 0); var pegParentTransformation = node.getMatrix(pegParentNode, frame.current()); var inversePegTransformation = pegParentTransformation.getInverse(); var pegModelPoint = inversePegTransformation.multiply(worldPoint); // A deformation chain is simply an array of deformation node parameters. var deformationChain = deformationChains[i]; if(deformationChain.length == 0) continue; // Apply the magnet translation for the deformation node offset and // both tangent points. // The first node is the Offset which doesn’t require a previous position. moveDeformationNode(deformationChain[0], new Deformation(""), pegModelPoint); for(var j=1; j<deformationChain.length; ++j) { moveDeformationNode(deformationChain[j], deformationChain[j-1], pegModelPoint); } }

Then, we compute the new position of the offset and tangent points of the deformation chains. We must also compute new orientation values from the new tangent point positions.

function moveDeformationNode(deformationNode, previousDeformationNode, magnetPoint) { if(deformationNode.nodeType == "OffsetModule") { var p1FieldDeformed = scene.fromOGL(computePosition(magnetPoint, deformationNode.OGLOffset)); node.setTextAttr(deformationNode.nodePath, "offset.X", frame.current(), p1FieldDeformed.x); node.setTextAttr(deformationNode.nodePath, "offset.Y", frame.current(), p1FieldDeformed.y); } else if(deformationNode.nodeType == "CurveModule") { // Compute the new Offset attribute position var p1FieldUndeformed = deformationNode.fieldOffset; var p1FieldDeformed = scene.fromOGL(computePosition(magnetPoint, deformationNode.OGLOffset)); // Set the new Offset attribute values. node.setTextAttr(deformationNode.nodePath, "offset.X", frame.current(), p1FieldDeformed.x); node.setTextAttr(deformationNode.nodePath, "offset.Y", frame.current(), p1FieldDeformed.y); // Compute the Tangent 0 position var t0FldU = add(previousDeformationNode.fieldOffset,rotate(Point2d(deformationNode.length0*0.1,0),deformationNode.orientation0)); var t0OGLU = scene.toOGL(t0FldU); // Compute the new Tangent 0 position var t0FldD = scene.fromOGL(computePosition(magnetPoint, t0OGLU)); // To compute the new orientation 0 position, // compute the new position of the previous node. var p0FieldDeformed = scene.fromOGL(computePosition(magnetPoint, previousDeformationNode.OGLOffset)); // Compute the inverse of the tangent calculation // to get the new orientation 0 position var vRef0 = Point2d(1,0); var ori0D = -angleDelta(minus(toPoint2d(t0FldD),p0FieldDeformed),vRef0); // Compute the Tangent 1 position var t1FldU = add(p1FieldUndeformed,rotate(Point2d(deformationNode.length1*0.1,0),180+deformationNode.orientation1)); var t1OGLU = scene.toOGL(t1FldU); // Compute the new Tangent 1 position var t1FldD = scene.fromOGL(computePosition(magnetPoint, t1OGLU)); // Compute the inverse of the tangent calculation // to get the new orientation 1 position var vRef1 = Point2d(-1,0); var ori1D = -angleDelta(minus(toPoint2d(t1FldD),p1FieldDeformed),vRef1); // Set the new orientation attribute values. node.setTextAttr(deformationNode.nodePath, "orientation0", frame.current(), ori0D); node.setTextAttr(deformationNode.nodePath, "orientation1", frame.current(), ori1D); } }
TIP: Should you want to apply the deformations of a deformation chain to another deformation chain, you can use the applyVertexTransform method of the DeformationUtils namespace. For more information, refer to the Harmony scripting interface reference.