Softimage SDK Programmer's Guide > Plug-in Development > Animation >

# Constraints

Constraints allow you to animate one object via another object's animation. You constrain different properties, such as position or direction, of one object to another object. You cannot create your own constraints, but Softimage provides many diverse types that you can apply and manipulate through the SDK.

Tip

For general information about using constraints in Softimage, see the Softimage User's Guide.

A constraint recognizes one or more objects as the constraining object(s) and one object as the constrained object. It is the constrained object which "owns" the constraint: you can access the constraint via the constrained object's kinematics. From the constraint, you can then access both the constraining and constrained objects (see Accessing Constraints through the SDK for more information).

Note

Constraints use only global space for transformations, so be aware that any changes you make to local parameters may be overridden by constraints (because global transformations override local transformations, no matter how the parameters are animated).

A constraint is a piece of data that mediates between the constraining and constrained objects. Depending on its type, the constraint expects a specific set of connection points, for example a surface constraint expects the constrainer to be a NURBS surface whereas the pose constraint expects the constraining object to be any 3D object.

Applying Constraints through the SDK

You can apply a constraint using scripting commands, the OM, or the C++ API:

- Scripting command: SIApplyCns

  Tip

  Alternatively, you can use the ApplyCns command, which performs cycle checking; however, cycle checking can create problems because it can log a lot of warnings that can be safely ignored in some cases (some cycles are on purpose). Another problem is that a popup message appears if there's a cycle, which halt automated tools and requires manual intervention for each cycle warning message.

- Object Model:Kinematics.AddConstraint method on the object to be constrained

- C++ API:Kinematics::AddConstraint member on the object to be constrained

  Note

  To remove a constraint through the SDK, you can only use one of these scripting commands:

  - RemoveCns: removes the constraint identified by name

  - RemoveCnsType: removes all constraints matching the specified constraint type.

  - DeleteObj: this is a general purpose command that deletes the specified object

Accessing Constraints through the SDK

You can get the constraints applied to an object through its Kinematics property for both the object model (JScript Example: Accessing the Constraint object) and the C++ API (C++ API Example: Accessing the Constraint object). For scripting commands, you can get the constrained objects using the GetConstrainedObjects command.

From there, the Constraint object gives you access to the collection of constraining objects as well as the constrained object, and the UpVector reference (the object that determines in which direction the constrained object should point). The following examples demonstrate how to access the Constraint object and its properties.

JScript Example: Accessing the Constraint object

    // "obj" is a pointer to a constrained object
    var cnslist = obj.Kinematics.Constraints;

    // Enumerate over the ConstraintCollection
    for ( var i=0; i<cnslist.Count; i++ ) {
    	var cns = cnslist(0);
    	var myself = cns.Constrained; // this should point to the original 'obj'
    	var stuckonyou = cns.Constraining; // everyone constraining 'obj'
    	var upvect = cns.UpVectorReference; // object used as my upvector reference

    	Application.LogMessage( "Constraint type: " + cns.Type );
    	Application.LogMessage( "Constrained object: " + myself );
    	Application.LogMessage( "# of Constraining objects: " + stuckonyou.Count );
    	Application.LogMessage( "UpVectorRef: " + upvect );
    }

    // So if you had a sphere pose-constrained by a null (with no up-vector
    // reference), you would get something like this:
    //INFO : Constraint type: posecns
    //INFO : Constrained object: sphere
    //INFO : # of Constraining objects: 1
    //INFO : UpVectorRef: null

C++ API Example: Accessing the Constraint object

    // "obj" is a pointer to a constrained object
    Kinematics kine = obj.GetKinematics();
    CRefArray cnslist = kine.GetConstraints();

    // Enumerate over the CRefArray of Constraints
    for ( LONG i=0; i<cnslist.GetCount(); ++i ) {
    	CRef ref = cnslist.GetItem(i);
    	if ( ref.IsA(siConstraintID) ) {
    		Constraint cns(ref);
    		X3DObject myself = cns.GetConstrained(); // this should point to the original 'obj'
    		CRefArray stuckonyou = cns.GetConstraining(); // everyone constraining 'obj'
    		X3DObject upvect = cns.GetUpVectorReference(); // object used as my upvector reference

    		app.LogMessage( L"Constraint type: " + cns.GetType() );
    		app.LogMessage( L"Constrained object: " + myself.GetName() );
    		app.LogMessage( L" # of Constraining objects: " + stuckonyou.GetCount() );
    		app.LogMessage( L"UpVectorRef: " + upvect.GetName() );
    	}
    }

    // So if you had a sphere pose-constrained by a null (with no up-vector
    // reference), you would get something like this:
    //INFO : Constraint type: posecns
    //INFO : Constrained object: sphere
    //INFO : # of Constraining objects:
    //INFO : UpVectorRef:

You can also get animation information from clips and sources that are based on constraints. For more information, see Constraints and the Mixer.

Navigating the Constraint Stack

Order of Constraint Operations

The Constraint Stack is the collection of constraints nested under a constrained object's Kinematics property. The order that you see in the UI mirrors the typical operator stack, that is the last constraint applied is at the top. When you access the collection of constraints through the SDK, you get the constraints in the same order they were applied.

Blending Multiple Constraints

If an object has more than one constraint applied and you want to blend the constraint weighting, you can access the blendweight parameter from the Constraint object.

Important

There are some peculiarities about setting blendweights when dealing with 3 or more constraints. For more information on how to set blendweights in this case and why it's necessary, see Blending Constraints in the Animation (user) guide.

JScript Example: Blending Constraints

The blendweight parameter can be identified with shortcut notation or standard (full) notation (getting the parameter by name from the ParameterCollection:

    // "cns" is a pointer to a constraint
    cns.blendweight.Value = .25;// shortcut notation
    cns.Parameters("blendweight").Value = .25;// full notation

C++ API Example: Blending Constraints

You can change the blendweight parameter's value with the convenience ProjectItem::PutParameterValue function or by using the standard (full) notation (getting the parameter by name from the CParameterRefArray):

    // "cns" is a pointer to a constraint
    cns.PutParameterValue(L"blendweight", 0.75);// use convenience method

    CParameterRefArray paramlist = cns.GetParameters();// full notation
    Parameter param = paramlist.GetItem(L"blendweight");
    param.PutValue(0.75);

Constraints and the Mixer

Constraints can be used in the animation mixer as a type of animation within an ActionSource or ActionSource. However, when a constraint is used as the underlying DataSource for an action, the constraint is no longer accessible from the clip or the source, unlike some other types of data sources (such as FCurves or FCurves or ShapeKeys or ShapeKeys). This is because the full constraint is not stored inside the action (for performance reasons). Constraint clips maintain their own data independent from the source, so when you update the constraint (the source), the clip in the mixer does not reflect the new values.

Note

For more information about constraints and the mixer generally, including limitations, see the Nonlinear Animation (user) guide.

Storing Constraints as Actions

The only way to create an action source in the SDK is to use the StoreAction command. The StoreAction command stores any kind of action, so you need to make sure you set its parameters correctly. Here are few guidelines to follow:

- For the InputObjs parameter, specify the affected parameters, not the constraint itself. And remember that constraints are always defined in global space, so these will always be the global transformation parameters.

  Another important things to remember is that you need to specify the affected parameters with Relative Names. Otherwise, you may have connection problems later on instantiating this source as a clip in the mixer.

- For the Mode parameter, specify one of the following values:

  - 4 to create a source based on the Constraint only

  - 5 to create a source based on FCurves, Expressions and Constraints

  - 6 to create a source based on FCurves, Expressions and Constraints as well as the current values of non-animated parameters

- For the RemoveAnim parameter, if you specify true (the default), the constraint is removed from the animated parameters.

- For the AddClip parameter, if you specify false (the default) you can run the AddClip command later on with this source to instantiate it in the mixer.

Accessing Constraint-Based Sources and Clips

Action sources and clips based on constraints can be accessed like any other action source or mixer clip (see Sources and Clips for more information). But because a constraint-based source does not store the full constraint, the underlying constraint object is not accessible using either of the following:

- AnimationSourceItem.Source or AnimationSourceItem::GetSource

- MappedItem.Source2 or MappedItem::GetAnimationSource

A Word About Cycles in Sources

You always have to be careful about creating cycles with constraints (see the Animation (user) guide for details), but when you are working with constraint-based animation in the mixer it is easy to lose track of potential cycles, especially if you are copying sources from one object to another.

The CycleChecking command can help detect these cycles, but there's no way to work around them. The only fix is to modify that part of the scene so there is no circular dependency.

Constraints vs. Custom (Scripted) Operators?

At first glance, constraints may seem similar to custom (scripted) operators. However, there are several basic differences in the way they are implemented in Softimage which lead to these differences in behavior and appearance:

- In the UI, constraints appear as a collection of properties under the kinematics property of the constrained object while custom operators are nested under their output parameters.

- Constraints are generally applied to an entire object unlike custom operators, which are always applied directly to a specific parameter. Constraints affect the object's global transform, not its parameters.

- Both constraints and custom operators can be muted and even deleted; however, objects that were altered via constraints remain as they were at the time the constraint was removed, whereas objects affected by custom operators snap back to their pre-operator condition.

How Do Constraints Interact with Operators and Expressions?

Operators and expressions are always evaluated before constraints on the same object. If an operator is reading from a parameter that is driven by a constraint, the data pulled by the operator may be stale. You can work around this problem by applying the constraint to a null and reading the data from there instead.